Steps to reproduce:
BottomNavigationView
":
Replace MainActivity with this:
class MainActivity : AppCompatActivity() {
private var fragmentIds = ArrayList<Int>()
val fragmentA: FragmentA = FragmentA()
private val fragmentB = FragmentB()
private val fragmentC = FragmentC()
private fun getFragment(fragmentId: Int): Fragment {
when (fragmentId) {
R.id.navigation_home -> {
return fragmentA
}
R.id.navigation_dashboard -> {
return fragmentB
}
R.id.navigation_notifications -> {
return fragmentC
}
}
return fragmentA
}
private fun updateView(fragmentId: Int) {
var exists = false
fragmentIds
.filter { it == fragmentId }
.forEach { exists = true }
if (exists) {
fragmentIds.remove(fragmentId)
showTabWithoutAddingToBackStack(getFragment(fragmentId))
} else {
fragmentIds.add(fragmentId)
showTab(getFragment(fragmentId))
}
}
private val onNavigationItemClicked = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
updateView(R.id.navigation_home)
return@OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
updateView(R.id.navigation_dashboard)
return@OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
updateView(R.id.navigation_notifications)
return@OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showTabWithoutAddingToBackStack(fragmentA)
navigation.setOnNavigationItemSelectedListener(onNavigationItemClicked)
}
private fun showTab(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(R.id.main_container, fragment, fragment::class.java.simpleName)
.addToBackStack(fragment::class.java.simpleName)
.commit()
}
fun showTabWithoutAddingToBackStack(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(R.id.main_container, fragment, fragment::class.java.simpleName)
.commit()
}
fun setBottomTab(id: Int) {
navigation.setOnNavigationItemSelectedListener(null)
navigation.selectedItemId = id
// currentTab = id
navigation.setOnNavigationItemSelectedListener(onNavigationItemClicked)
}
}
Create 3 new classes, FragmentA, FragmentB and FragmentC:
class FragmentA : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
setHasOptionsMenu(true)
return inflater.inflate(R.layout.fragment_a, container, false)
}
override fun onResume() {
super.onResume()
val act = activity as MainActivity
act.setBottomTab(R.id.navigation_home)
}
}
with this xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment A" />
</LinearLayout>
Here is a video that demonstrates above steps
Stacktrace:
12-06 12:58:35.899 25903-25903/com.example.jimclermonts.bottomnavigationview E/InputEventSender: Exception dispatching finished signal.
12-06 12:58:35.900 25903-25903/com.example.jimclermonts.bottomnavigationview E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
12-06 12:58:35.912 25903-25903/com.example.jimclermonts.bottomnavigationview E/MessageQueue-JNI: java.lang.**IllegalStateException: Fragment already added**: FragmentB{3aac1d9 #1 id=0x7f080059 FragmentB}
at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1882)
at android.support.v4.app.BackStackRecord.executePopOps(BackStackRecord.java:825)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2577)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2367)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:851)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:794)
at android.support.v4.app.FragmentActivity.onBackPressed(FragmentActivity.java:174)
All Android devices provide a Back button for this type of navigation, so you should not add a Back button to your app's UI. Depending on the user's Android device, this button might be a physical button or a software button.
In order to check when the 'BACK' button is pressed, use onBackPressed() method from the Android library. Next, perform a check to see if the 'BACK' button is pressed again within 2 seconds and will close the app if it is so.
I have implemented this concept using Bottombar library. I have uploaded to GitHub. Please check and comment here if any issues.
https://github.com/itvignes09/youtube-like-bttom-menu
Sample output
This lets you have the "do not repeat (but reorder) my fragments on the backstack" behavior using a custom backstack (deque):
class MainActivity extends AppCompatActivity {
private BottomNavigationView navigation;
// initialize with number of different fragments
private Deque<Integer> fragmentIds = new ArrayDeque<>(3);
private FragmentA fragmentA = new FragmentA();
private FragmentB fragmentB = new FragmentB();
private FragmentC fragmentC = new FragmentC();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentIds.push(R.id.navigation_home);
showTabWithoutAddingToBackStack(fragmentA);
navigation = findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(onNavigationItemClicked);
}
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked = new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
item.setChecked(true);
int itemId = item.getItemId();
if (fragmentIds.contains(itemId)) {
fragmentIds.remove(itemId);
}
fragmentIds.push(itemId);
showTabWithoutAddingToBackStack(getFragment(item.getItemId()));
return true;
}
};
private Fragment getFragment(int fragmentId) {
switch (fragmentId) {
case R.id.navigation_home:
return fragmentA;
case R.id.navigation_dashboard:
return fragmentB;
case R.id.navigation_notifications:
return fragmentC;
}
return fragmentA;
}
void showTabWithoutAddingToBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, fragment.getClass().getSimpleName()).commit();
}
void setBottomTab(int id) {
int itemIndex;
switch (id) {
case R.id.navigation_dashboard:
itemIndex = 1;
break;
case R.id.navigation_notifications:
itemIndex = 2;
break;
default:
case R.id.navigation_home:
itemIndex = 0;
}
navigation.getMenu().getItem(itemIndex).setChecked(true);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
onBackPressed();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onBackPressed() {
fragmentIds.pop();
if (!fragmentIds.isEmpty()) {
showTabWithoutAddingToBackStack(getFragment(fragmentIds.peek()));
} else {
finish();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With