I have an activity with 2 fragments. Fragment A has TextViews used as search fields. Fragment B shows the results based on the search.
I have button in my actionbar to clear the search fields. This button is for the activity and can be clicked from both fragments.
This is the code for the action button:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//Delete
if (id == R.id.action_delete) {
if (fragmentA != null) {
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentA.clearAll();
}
}
return super.onOptionsItemSelected(item);
}
With the above code, I return back to the first fragment (it's not added to the backstack). However the textviews are not cleared.
The clearAll method is called correctly because I'm also clearing properties of a custom object. However, the text in the textviews stay there. I have to click the button a second time (when fragment A is displayed) so that the text is cleared.
This is the method:
public void clearAll() {
searchClass.name = "";
searchClass.surname = "";
tvName.setText(null);
tvSurname.setText(null);
}
I'm setting null and not "" so that the hints should be displayed again.
What am I missing? Could it be that the fragment is is not shown so its view is still not available?
You can try this
tvName.setText("");
tvSurname.setText("");
Instead of this
tvName.setText(null);
tvSurname.setText(null);
You can try this-
public void clearAll() {
searchClass.name = "";
searchClass.surname = "";
tvName.setText("");
tvSurname.setText("");
}
To get it to work the following changes were done:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//Delete
if (id == R.id.action_delete) {
if (fragmentA != null) {
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
if (currentFragment == fragmentA ) {
fragmentA.clearAllImmediate();
}
else {
fragmentA.clearAll();
}
}
}
return super.onOptionsItemSelected(item);
}
And then in the fragment itself:
#Override
public void onResume() {
super.onResume();
if (needToClear) {
clearAllImmediate();
needToClear = false;
}
}
public void clearAll() {
needToClear = true;
}
public void clearAllImmediate() {
searchClass.name = "";
searchClass.surname = "";
tvName.setText(null);
tvSurname.setText(null);
}
Related
i have four fragments Home, Warning, Donate, and About, Home is the first one shown on launch, what i need is that when users are on any of these fragments (Donate, About, and Caution), and when they go back to the Home fragment using backstack only, I want to show an interstitial ad before showing the home fragment.
I don't want to show the ad when the home fragment launch for first time
Main Activity
public class MainActivity extends AppCompatActivity {
public BottomNavigationView bottomNavigationView;
Deque<Integer> integerDeque = new ArrayDeque<>(3);
boolean flag = true;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Window window = this.getWindow();
window.setStatusBarColor(this.getResources().getColor(R.color.black));
bottomNavigationView = findViewById(R.id.bottomNavigationView);
bottomNavigationView.setItemIconTintList(null);
integerDeque.push(R.id.home_icon);
loadFragments(new Home_Fragment());
bottomNavigationView.setSelectedItemId(R.id.home_icon);
bottomNavigationView.setOnNavigationItemSelectedListener(
item -> {
int id = item.getItemId();
if (integerDeque.contains(id)) {
if (id == R.id.home_icon) {
integerDeque.size();
if (flag) {
integerDeque.addFirst(R.id.home_icon);
flag = false;
}
}
integerDeque.remove(id);
}
integerDeque.push(id);
loadFragments(getFragment(item.getItemId()));
return true;
}
);
}
#SuppressLint("NonConstantResourceId")
private Fragment getFragment(int itemId) {
switch (itemId) {
case R.id.home_icon:
bottomNavigationView.getMenu().getItem(0).setChecked(true);
return new Home_Fragment();
case R.id.guide_icon:
bottomNavigationView.getMenu().getItem(1).setChecked(true);
return new Cuation_Fragment();
case R.id.donate_icon:
bottomNavigationView.getMenu().getItem(2).setChecked(true);
return new Donate_Fragment();
case R.id.about_icon:
bottomNavigationView.getMenu().getItem(3).setChecked(true);
return new About_Fragment();
}
bottomNavigationView.getMenu().getItem(0).setChecked(true);
return new Home_Fragment();
}
public void loadFragments(Fragment fragment) {
if (fragment != null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName())
.commit();
}
}
#Override
public void onBackPressed() {
integerDeque.pop();
if (!integerDeque.isEmpty()) {
bottomNavigationView.setSelectedItemId(integerDeque.peek());
} else {
finish();
}
}
}
I'm using 4 fragments and the MainActivity (which contains the bottom nav) but now because of a particular payment gateway issue with one of the fragments I have changed that fragment into an activity, but as the bottom nav has 3 fragments in it don't know how do add an activity in it, and I also don't want to change all the 3 remaining fragments into activity just to make it work with bottom nav
Files
MainActivity,Home_Fragment,Caution_Fragment,Donate_Fragment // this is the fragment i changed into activtiy// and About_Fragment
is there any way to add an activity in a bottom navigation view that already contains fragments
if you want to view the Donate_Fragment(Activity) code please tell me
I will add it
MainActivity.java
public class MainActivity extends AppCompatActivity {
public BottomNavigationView bottomNavigationView;
Deque<Integer> integerDeque = new ArrayDeque<>(3);
boolean flag = true;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Window window = this.getWindow();
window.setStatusBarColor(this.getResources().getColor(R.color.black));
bottomNavigationView = findViewById(R.id.bottomNavigationView);
bottomNavigationView.setItemIconTintList(null);
integerDeque.push(R.id.home_icon);
loadFragments(new Home_Fragment());
bottomNavigationView.setSelectedItemId(R.id.home_icon);
bottomNavigationView.setOnNavigationItemSelectedListener(
item -> {
int id = item.getItemId();
if (integerDeque.contains(id)) {
if (id == R.id.home_icon) {
integerDeque.size();
if (flag) {
integerDeque.addFirst(R.id.home_icon);
flag = false;
}
}
integerDeque.remove(id);
}
integerDeque.push(id);
loadFragments(getFragment(item.getItemId()));
return true;
}
);
}
#SuppressLint("NonConstantResourceId")
private Fragment getFragment(int itemId) {
switch (itemId) {
case R.id.home_icon:
bottomNavigationView.getMenu().getItem(0).setChecked(true);
return new Home_Fragment();
case R.id.guide_icon:
bottomNavigationView.getMenu().getItem(1).setChecked(true);
return new Cuation_Fragment();
case R.id.donate_icon:
bottomNavigationView.getMenu().getItem(2).setChecked(true);
return new Donate_Fragment();
case R.id.about_icon:
bottomNavigationView.getMenu().getItem(3).setChecked(true);
return new About_Fragment();
}
bottomNavigationView.getMenu().getItem(0).setChecked(true);
return new Home_Fragment();
}
public void loadFragments(Fragment fragment) {
if (fragment != null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName())
.commit();
}
}
#Override
public void onBackPressed() {
integerDeque.pop();
if (!integerDeque.isEmpty()) {
bottomNavigationView.setSelectedItemId(integerDeque.peek());
} else {
finish();
}
}
}
I spent my whole day now looking for an answer and didn't found any solution that suited this question.
I am looking for a way to create a BottomNavigation usage similar to the one of the Instagram or PlayKiosk app. Fragments should be added to the back stack only once. When pressing the back button I'd expect the app to jump back to the last Fragment that was visited and the button of the BottomNavigation to fit that Fragment.
Currently I use the following code:
//BottomNavigationListener
private BottomNavigationView.OnNavigationItemSelectedListener buttonNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
FragmentManager fragmentManager = getSupportFragmentManager();
switch (item.getItemId()) {
case R.id.navigation_game:
currentFragment = GameFragment.newInstance();
fragmentManager.beginTransaction()
.addToBackStack(TAG_FRAGMENT_GAME)
.replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_GAME)
.commit();
break;
case R.id.navigation_tournament:
currentFragment = TournamentFragment.newInstance();
fragmentManager.beginTransaction()
.addToBackStack(TAG_FRAGMENT_TOURNAMENT)
.replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_TOURNAMENT)
.commit();
break;
case R.id.navigation_history:
break;
}
return true;
}
};
But this leads to the problem that I could press the button of my BottomNavigation a couple of times and for each of this clicks a new Fragment would be instantiated. Also the BottomNavigation buttons are not set according to the Fragments.
I found this answer but it didn't work out Prevent The Same Fragment From Stacking More Than Once ( addToBackStack)
You can use following structure to have the same working logic of Instagram.
First create a custom limited unique queue class. It only holds last N items, where N is the limit number (or maximum item count) passed to its constructor. Moreover, this type of queue keeps only one instance of a class. If an instance of class A is already in the queue and another instance of class A is to be added to the queue, the former instance is removed first and then the new object is inserted.
public class LimitedUniqueQueue<E> extends LinkedList<E> {
private int limit;
public LimitedUniqueQueue(int limit) {
this.limit = limit;
}
#Override
public boolean add(E o) {
// For uniqueness
for (int i = 0; i < super.size(); i++) {
E item = super.get(i);
if (item.getClass() == o.getClass()) {
super.remove(i);
break;
}
}
boolean added = super.add(o);
// For size limit
while (added && size() > limit) {
super.remove();
}
return added;
}
}
Then update your Activity as follows:
public class MainActivity extends AppCompatActivity {
private BottomNavigationView navigation;
private LimitedUniqueQueue<Fragment> queue;
...
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
Fragment gameFragment;
Fragment tournamentFragment;
Fragment profileFragment;
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (item.getItemId()) {
case R.id.navigation_game:
if (gameFragment == null) {
gameFragment = new GameFragment();
}
transaction.replace(R.id.content, gameFragment).commit();
queue.add(gameFragment);
return true;
case R.id.navigation_tournament:
if (tournamentFragment == null) {
tournamentFragment = new TournamentFragment();
}
transaction.replace(R.id.content, tournamentFragment).commit();
queue.add(tournamentFragment);
return true;
case R.id.navigation_profile:
if (profileFragment == null) {
profileFragment = new ProfileFragment();
}
transaction.replace(R.id.content, profileFragment).commit();
queue.add(profileFragment);
return true;
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
queue = new LimitedUniqueQueue<>(navigation.getMenu().size());
navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener);
navigation.setSelectedItemId(R.id.navigation_game);
...
}
#Override
public void onBackPressed() {
if (queue.size() > 1) {
queue.removeLast();
Fragment previousFragment = queue.getLast();
if (previousFragment instanceof GameFragment) {
navigation.setSelectedItemId(R.id.navigation_game);
} else if (previousFragment instanceof TournamentFragment) {
navigation.setSelectedItemId(R.id.navigation_tournament);
} else if (previousFragment instanceof ProfileFragment) {
navigation.setSelectedItemId(R.id.navigation_profile);
}
} else {
super.onBackPressed();
}
}
...
}
In my app i have navigation drawer,So i have one MainActivity and rest are fragments. My app is working fine. Whenever i press back button it redirect to previous fragment.it works fine.but what i want is after successful payment i am displaying Successful payment page,on this page when user press back button i want to redirect to HomeFragment,but right now it goes to Placeorder fragment.
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else {
getFragmentManager().popBackStack();
}
}
Override your onBackPress method:
#Override
public void onBackPressed() {
if (fragment != null && fragment.getChildFragmentManager().getBackStackEntryCount() > 0){
fragment.getChildFragmentManager().popBackStack();
}else {
super.onBackPressed();
}
}
One way would be saving previous fragment (previousFragment) variable while opening new fragment and making it null when you are in first or placeholder fragment .... then check value of previousFragment inside onBackPressed method , if null perform exit operation else replace fragment with previousFragment by which you will get back to previous fragment on back press.
You should simulate function as below in abstract class fragment
/**
* Handle "Back" key pressed.
* #return TRUE if the "Back" key pressed was handled. FALSE indicate it's not handled and parent class can handle it.
*/
public abstract boolean onBackPressed()
And in MainActivity, you will handle this function
#Override
public void onBackPressed() {
if (mSelectedFragment != null && mSelectedFragment.onBackPressed()) {
return;
} else {
super.onBackPressed
}
}
inside your onBackPressed() do like this:
#Override
public void onBackPressed() {
if(getSupportFragmentManger.findFragmentByTag("paymentFragment") != null){
//add homeFragment;
}
else{
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else {
getFragmentManager().popBackStack();
}
}
}
you can set some global variable (true when payment is success else false) in Application class and in onbackpressed of your activity just check that variable is true or not.Then in backpressed of your activity call this function.
public void clearBackStack() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Use:
fragmentTransaction.replace(R.id.fragment_container, mFeedFragment);
fragmentTransaction.commit();
Don't do this:
fragmentTransaction.addToBackStack(null);
Follow this simple method
declare an integer as
public int count;
initialise this count as count=0 in your main page
now include this in your main activity
#Override
public void onBackPressed() {
if (count == 1) {
if (exit) {
super.onBackPressed();
return;
} else {
Toast.makeText(this, "Press Back again to Exit.", Toast.LENGTH_SHORT).show();
exit = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
exit = false;
}
}, 2000);
}
}else {
toolbar.setTitle("Home");
mDrawerLayout.closeDrawers();
count=1;
display(0);
}
}
I tried to handle back button in several fragments(in ActionBarActivity) , but it didn't work.
One of fragments's onKeyDown():
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if ((!currentDir.getName().equals("sdcard")) && (currentDir.getParentFile() != null)) {
currentDir = currentDir.getParentFile();
fill(currentDir);
Log.v(TAG,"true." );
} else {
getActivity().finish();
Log.v(TAG,"false." );
}
return false;
}
return super.getActivity().onKeyDown(keyCode, event);
}
Can someone tell me how to do it and how to handle back button in other fragments in ActionBarActivity?
onKeyDown(...) will never be called in a fragment so in order to handle the back button you have to move your code to the ActionBarActivity. In addition to this I would recommend you using the onBackPressed() method instead. That's because the onKeyDown-method will be called twice (*without further modification) if you keep the back-button pressed.
So you're final code in your ActionBarActivity would look like this:
#Override
public void onBackPressed(){
if ((!currentDir.getName().equals("sdcard")) && (currentDir.getParentFile() != null)) {
currentDir = currentDir.getParentFile();
fill(currentDir);
Log.v(TAG,"true." );
} else {
getActivity().finish();
Log.v(TAG,"false." );
}
super.onBackPressed();
}
Edit
In order to call a getter-Method of your Fragment in your ActionBarActivity you can do something like this in your Activity:
FragmentManager fragmentManager = getFragmentManager();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.content_pane); // The id of your container
if (currentFragment instanceof MyFragment) {
// Cast the Fragment to your Fragment so that you can access the public method
currentDirName = ((MyFragment) currentFragment).getCurrentDir();
} else if (currentFragment instanceof MyOtherFragment)
currentDirName = ((MyOtherFragment) currentFragment).getCurrentDir();
In your Fragment the getCurrentDir()-method just returns the currentDir name:
public String getCurrentDir(){
return currentDir.getName();
}