I have 3 Fragments inside a ViewPager. I have different back button functionalities which I want to implement for each Fragment. At the moment, I have created methods in each fragment which correspond to what I want the back button to do. I implemented an interface in my activity with the following methiod:
#Override
public void onCameraBack(int i) {
currFrag = i;
}
The idea here is that there are three fragments, each fragment calls this method in its onStart() method. Fragment 0 passes 0, fragment 1 passes 1 and fragment 2 passes 2. So in this way the holding activity knows which fragment is visible. Now I am trying to get a reference to that fragment to be able to call the backbutton method I have implemented in it. I tried using this :
#Override
public void onBackPressed() {
if (currFrag == 0) {
}
else if (currFrag == 1) {
FragmentBTesting fragmentBTesting= new FragmentBTesting();
fragmentBTesting.FragmentBBack();
}
}`
but this doesn't work. I can't use the findFragmentbyId method since my fragments do not have fragment tags in their XML, they fill the whole screen and their views are defined using linearLayouts. My question is how can I get an instance of the fragment so I can call its FragmentBack() method.
Here is my full activity
public class FragmentActivityTesting extends FragmentActivity implements FragmentATesting.logoutListener,FragmentBTesting.onCameraBack {
ViewPager viewPager = null;
SessionManager session = new SessionManager(MyApp.getContext());
int currFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_activity_testing);
viewPager = (ViewPager) findViewById(R.id.pic_pager);
setStatusBarColor();
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new MyAdapter(fragmentManager));
viewPager.setCurrentItem(1);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("CLOSE_ALL");
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
registerReceiver(broadcastReceiver, intentFilter);
}
#TargetApi(21)
public void setStatusBarColor() {
Window window = this.getWindow();
// clear FLAG_TRANSLUCENT_STATUS flag:
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
if (Integer.valueOf(android.os.Build.VERSION.SDK) >= 21) {
window.setStatusBarColor(this.getResources().getColor(R.color.green));
}
}
#Override
public void logout() {
session.logOut();
Intent a = new Intent(this,MainActivity.class);
startActivity(a);
Intent intent = new Intent("CLOSE_ALL");
this.sendBroadcast(intent);
finish();
}
#Override
public void onBackPressed() {
if (currFrag == 0) {
}
else if (currFrag == 1) {
FragmentBTesting fragmentBTesting= new FragmentBTesting();
fragmentBTesting.FragmentBBack();
}
}
#Override
public void onCameraBack(int i) {
currFrag = i;
}
}
class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
if (position ==0) {
fragment = new FragmentATesting();
}
else if (position == 1) {
fragment = new FragmentBTesting();
}
else if (position == 2) {
fragment = new FragmentCTesting();
}
return fragment;
}
#Override
public int getCount() {
return 3;
}
}
Instead of telling your activity what fragment you are currently on, why not tell it what to execute when back is pressed?
In your activity you can have a field to hold the callback field (as a Runnable, I know so much hate. You can make your own interface if you want), the setter, and then the onBackPressed implementation. Here is a snippet from my code that works. Im using Guava's Optional class, but you can null it instead if you're into that kinda thing.
This is the Activity which should implement ActivityWithHomeButton
private Optional<? extends Runnable> backButtonListener = Optional.absent();
#Override
public void onBackPressed() {
// Check if there is a custom back button
if (backButtonListener.isPresent()) {
backButtonListener.get().run();
backButtonListener = Optional.absent();
} else {
super.onBackPressed();
}
}
#Override
public void setBackButtonListener(Optional<? extends Runnable> backButtonListener) {
this.backButtonListener = backButtonListener;
}
Here is the interface I implement in the activity
public interface ActivityWithHomeButton {
void setBackButtonListener(Optional<? extends Runnable> runnable);
}
and of course the usage from a fragment
parent.setBackButtonListener(Optional.of(new Runnable() {
#Override
public void run() {
// do back button stuff
}
}));
You can put this wherever you want in the fragment. You are also going to want to clear the back button listener whenever you no longer need it (onPause). You can do this as such
parent.setBackButtonListener(Optional.<Runnable>absent());
Where parent can be attained using the standard activity-fragment communication pattern detailed here (http://developer.android.com/training/basics/fragments/communicating.html). This code goes in your fragment
private ActivityWithHomeButton parent;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
parent = (ActivityWithHomeButton) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ActivityWithHomeButton");
}
}
Hope it helps!
Related
I have a save menu option in my fragment where I save the user's data, I also want to save the data onbackpressed in fragments?How can I achieve this ?
This is my saveMethod:
public void saveNote() {
title = edit_title.getText().toString().trim();
text = txtnote2.getText().toString().trim();
if (title.isEmpty()) {
Toast.makeText(getActivity(), "Please enter a title", Toast.LENGTH_SHORT).show();
}
else if (!text.isEmpty() || !title.isEmpty()) {
long date = new Date().getTime(); // get current time;
if (temp == null) {
temp = new Note(title, text, date,activityName);
dao.insertNote(temp); //inserts note record to db;
} else {
temp.setNoteTitle(title);
temp.setNoteText(text);
temp.setNoteDate(date);
temp.setActivityName(activityName);
dao.updateNote(temp);
}
Toast.makeText(getActivity(), "Saved!!", Toast.LENGTH_SHORT).show();
// finish(); //return to main activity
getActivity().startActivity(new Intent(getActivity(),MainActivity.class));
getActivity().finish();
}
}
I want to call this method onbackpressed in fragments
How can this be done ?
To tell fragments when the back button has been pressed, first of all you need a base fragment which all of your other fragments inherit from. This base fragment implements the following:
public interface OnBackPressed {
void onBackPressed();
}
public class BaseFragment extends Fragment implements OnBackPressed {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onBackPressed() {
// do what you want to save
saveNote()}
}
Now in your hosting Activity, call any implementations of the interface before any fragments are popped:
#Override
public void onBackPressed() {
saveData()
super.onBackPressed();
}
private void saveData(){
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f : fragments){
if(f != null && f instanceof BaseFragment)
((BaseFragment)f).onBackPressed();
}
}
Have you tried this:
public class BaseFragment extends Fragment {
/**
* Could handle back press.
* #return true if back press was handled
*/
public boolean onBackPressed() {
return false;
}
}
I got a problem with updating ViewPager fragments. We need to show fragments with data to registered user, when he doesn't registered we need to show fragments with message to register. I use this method to check it in MainActivity:
#Override
public void setAdapter(boolean isUserExist) {
Log.d("RegDebug", "In setAdapter");
mainPagerAdapter.clearData();
mainPagerAdapter.addFragment(searchFragment, getString(R.string.search_title));
if (isUserExist) {
Log.d("RegDebug", "In setAdapter reg");
mainPagerAdapter.addFragment(new ChatsFragment(), getString(R.string.chats_title));
mainPagerAdapter.addFragment(new ActionsFragment(), getString(R.string.actions_title));
Toast.makeText(getApplicationContext(), "Registered!", Toast.LENGTH_SHORT).show();
} else {
Log.d("RegDebug", "In setAdapter unreg");
mainPagerAdapter.addFragment(RegisterFragment.newInstance(Consts.CHATS_TAB_NAME), getString(R.string.chats_title));
mainPagerAdapter.addFragment(RegisterFragment.newInstance(Consts.ACTIONS_TAB_NAME), getString(R.string.actions_title));
Toast.makeText(getApplicationContext(), "Unregistered!!!", Toast.LENGTH_SHORT).show();
}
mainPagerAdapter.notifyDataSetChanged();
viewPager.setAdapter(mainPagerAdapter);
}
I call this method in presenter with setting value from firebase auth, checking if user exists:
public void checkForUserExist() {
if (mainInteractor.isUserExist()) {
getViewState().setRegAdapter();
} else getViewState().setUnregAdapter();
}
And then call presenter method in onCreate of MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dialogFragment = new FilterDialogFragment();
searchFragment = new SearchFragment();
//UI
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
viewPager = findViewById(R.id.main_view_pager);
tabLayout = findViewById(R.id.main_tabs);
//mainPagerAdapter = new MainPagerAdapter(getSupportFragmentManager());
mainPresenter.checkForUserExist();
tabLayout.setupWithViewPager(viewPager);
}
I try to log the boolean result and it returns exactly value that must be, but pager adapter can't update its content.Code of MainPagerAdapter:
public class MainPagerAdapter extends FragmentPagerAdapter{
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public MainPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public int getCount() {
return fragmentTitleList.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title){
fragmentList.add(fragment);
fragmentTitleList.add(title);
}
public void clearData(){
fragmentList.clear();
fragmentTitleList.clear();
notifyDataSetChanged();
Log.d("RegDebug", " fragmentList size is " + fragmentList.size()
+ " fragmentTitleList size is " + fragmentTitleList.size());
}
}
Use OnPageChangeListener of ViewPager class & notify to your current fragment from there using interface.
why can't I contact the second page
This is inside my Adapter
holder.btn_filter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (position == 1)
{
ShowMessages showMessages = new ShowMessages();
showMessages.message();
}
}}
Home Activity
public class ShowMessages extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_messages);
}
public void message()
{
Toast.makeText(this, "hola", Toast.LENGTH_SHORT).show();
//or set text or change adapter etc...
}
}
When called with message() inside adapter comes out of the application why ?
I see that ShowMessages is an activity here and when you try to instantiate an activity through new, the context is null. Always use intents to create new activities. If you want to trigger some method in the activity from the adapter, pass the activity to the adapter as the constructor argument, cache it in the adapter and then use it to call message().
Something like this would do:
ShowMessages.java - adapter initialization in activity:
MyAdapter adapter = new MyAdapter(this);
MyAdapter.java:
private Activity activity;
MyAdapter(ShowMessages activity) {
this.activity = activity;
}
And then in your adapter:
holder.btn_filter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (position == 1)
{
activity.message();
}
}}
Also, you are being memory efficient here, as you are not instantiating a new activity for every click at position 1.
I am trying to use multiple Fragments on an Activity. What I am doing is:
1. Add the first Fragment to the Activity. The first Fragment contains a button at the bottom of the screen.
2. Replace it with the second Fragment by adding it to the backstack.
When I click on the button on the bottom of the first Fragment, it automatically slides up (I don't know why) and moves to second Fragment:
When I go back to the first Fragment by pressing the back button, the button in the first Fragment goes out of view:
MainActivity
public class MainActivity extends AppCompatActivity implements UserDetailsFragment.UserDetailsFragmentListener,
PhotoFragment.PhotoFragmentListener, TestFragment.TestFragmentListener{
#Bind(R.id.container)
FrameLayout frameLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_profile);
ButterKnife.bind(this);
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
initView();
}
private void initView() {
TestFragment testFragment = new TestFragment();
FragmentUtil.replaceFragment(this,R.id.container, testFragment);
}
#Override
public void onProfileDetailCompleted(UserModel userModel) {
PhotoFragment photoFragment = PhotoFragment.newInstance(userModel);
FragmentUtil.replaceFragment(this, R.id.container, photoFragment);
}
#Override
public void onPhotoUploaded(UserModel userModel) {
}
#Override
public void onSkipPhotoClicked() {
PhotoFragment photoFragment = PhotoFragment.newInstance(null);
FragmentUtil.replaceFragment(this, R.id.container, photoFragment);
}
#Override
public void onTest() {
PhotoFragment photoFragment = PhotoFragment.newInstance(null);
FragmentUtil.replaceFragment(this, R.id.container, photoFragment);
}
}
TestFragment.java
public class TestFragment extends BaseFragment {
public TestFragment() {
// Required empty public constructor
}
public interface TestFragmentListener {
void onTest();
}
private TestFragmentListener mListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_test, container, false);
ButterKnife.bind(this, view);
return view;
}
#OnClick(R.id.next_btn)
public void onNextButtonClicked() {
//TODO validate view
if(mListener!=null)
mListener.onTest();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof TestFragmentListener) {
mListener = (TestFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement TestFragmentListener");
}
}
}
FragmentUtils.java
public class FragmentUtil {
public static boolean hadFragment(AppCompatActivity activity) {
return activity.getSupportFragmentManager().getBackStackEntryCount() != 0;
}
public static void replaceFragment(AppCompatActivity activity, int contentId, BaseFragment fragment) {
FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_left_in, R.anim.slide_left_out);
if (hadFragment(activity)) {
transaction.replace(contentId, fragment, fragment.getClass().getSimpleName());
} else {
transaction.add(contentId, fragment, fragment.getClass().getSimpleName());
}
transaction.addToBackStack(null);
transaction.commit();
}
public static void removeFragment(AppCompatActivity activity, BaseFragment fragment) {
activity.getSupportFragmentManager().beginTransaction()
.remove(fragment)
.commit();
}
public static void showFragment(AppCompatActivity activity, BaseFragment fragment) {
activity.getSupportFragmentManager().beginTransaction()
.show(fragment)
.commit();
}
public static void hideFragment(AppCompatActivity activity, BaseFragment fragment) {
activity.getSupportFragmentManager().beginTransaction()
.hide(fragment)
.commit();
}
public static void attachFragment(AppCompatActivity activity, BaseFragment fragment) {
activity.getSupportFragmentManager().beginTransaction()
.attach(fragment)
.commit();
}
public static void detachFragment(AppCompatActivity activity, BaseFragment fragment) {
activity.getSupportFragmentManager().beginTransaction()
.detach(fragment)
.commit();
}
}
If I move from the second Fragment to the third Fragment (which is the same as the first), the button on the bottom of the second screen will look fine. But the button on first Fragment still goes out of the view. The problem only exists in the view of the first Fragment. Please help.
i had somewhat similar issue. I was using the appcompact theme and my bottom layout was going out of the screen when i replace fragment. Then I tried different theme after that it worked perfectly fine. So if your are also using app compact theme then try any other theme. Hope so it will solve your problem.
I have two ACTIVITY (A and B). I have a FRAGMENT F in ACTIVITY A. My Fragment F cointains 3 EditText (1,2,3).
My app gets a string from Activity B and places it in EditText 3. I have no problem with that. My problem is, when i type something in EditText 1 and 2 then I'll get the string from ACTIVITY B and put it in EditText 3, all of the information I typed in EditText 1 and 2 are gone.
My question is, how will the information I typed in EditText 1 and 2 stay even though I get the string from ACTIVITY B to EditText 3. Here's my code:
ACTIVITY A
public class ActivityA extends FragmentActivity {
ViewPager viewPager = null;
PageIndicator pIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
viewPager = (ViewPager) findViewById(R.id.pager);
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new MyAdapter(fragmentManager));
pIndicator = (PageIndicator)findViewById(R.id.indicator);
pIndicator.setViewPager(viewPager);
}
public class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter (FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = null;
if (i == 0)
{
fragment = new FragmentF();
}
if (i == 1)
{
fragment = new FragmentG();
}
if (i == 2)
{
fragment = new FragmentH();
}
if (i == 3)
{
fragment = new FragmentI();
}
return fragment;
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0)
{
return "TAB.F";
}
if (position == 1)
{
return "TAB.G";
}
if (position == 2)
{
return "TAB.H";
}
if (position == 3)
{
return "TAB.I";
}
return null;
}
}
public void btn_to_actb(View view) {
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
}
ACTIVITY B
public final static String EXTRA_MESSAGE = "com.sample.MESSAGE";
// onClick get button from activity B layout
public void get(View view) {
Intent intent = new Intent(this, ActivityA.class);
TextView textView = (TextView)findViewById(R.id.coordinates);
String message = textView.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
FRAGMENT F
EditText editText;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String num = getActivity().getIntent().getStringExtra("com.sample.MESSAGE");
View v = inflater.inflate(R.layout.fragmentf, container, false);
// EditText3 from fragment F layout
editText = (EditText) v.findViewById(R.id.edittext3);
editText.setText(num);
return v;
}
Notice that when you launch your intent to Activity A from B, the Activity A could be recreated o resumed depending on if A was stopped or if it was still on memory.
Your fragment F could be recreated or resumed too depending on how you manage the fragment in your Activity A, so you would lose all the information stored in editTexts.
It would be usefull if you post the code of your Activity A.
The best way to keep the data of fragments between transitions, is store it in your Activity and get it from the Fragment.
In your Fragment
#Override
public void onResume(){
EditText1.setText(getActivity().getTextOfEditText1());
EditText2.setText(getActivity().getTextOfEditText2());
super.onResume();
}
** And in your Activity**
public class A extends Activity{
//...
private String textOfEditText1;
private String textOfEditText2;
//... Other stuff
public String getTextOfEditText1(){
return textOfEditText1;
}
public String getTextOfEditText2(){
return textOfEditText2;
}
}
You should have methods to set the Strings from the Fragment when you modify them.
Hope it helps :)