Java BottomNavigationView-如何避免重新创建片段并重用它们
我想在我的项目底部导航栏。每个视图都有自己的片段。问题是,每次我点击按钮更改视图(例如从最近的视图更改为收藏夹视图)时,它都会创建具有全新状态的新片段(例如滚动位置、文本更改,无论我的片段包含什么)。我知道,在Android官方文档中,有人写道底部导航栏应该重置任务状态,但我认为这对用户来说太不舒服了。 我希望有类似的功能,比如instagram,你可以从feed切换到explore,再切换到feed到滚动位置。图像缓存所有保存的内容。我尝试了几乎所有的方法来解决这个问题唯一有效的方法是设置可见性消失,并根据情况设置可见性可见,但我知道这不是正确的方法,应该有更好的方法来解决这个问题,我不是说手动保存所需的实例。我几乎遵循了每一个关于底部导航片段的教程,但有趣的是,没有人有兴趣在每次不调用new的情况下使用它 我也尝试了onAttach和Deattach解决方案,但同样没有成功 可能是因为我使用的是金丝雀版本,或者是我的gradle依赖项有问题? 最新更新:Java BottomNavigationView-如何避免重新创建片段并重用它们,java,android,android-fragments,bottomnavigationview,Java,Android,Android Fragments,Bottomnavigationview,我想在我的项目底部导航栏。每个视图都有自己的片段。问题是,每次我点击按钮更改视图(例如从最近的视图更改为收藏夹视图)时,它都会创建具有全新状态的新片段(例如滚动位置、文本更改,无论我的片段包含什么)。我知道,在Android官方文档中,有人写道底部导航栏应该重置任务状态,但我认为这对用户来说太不舒服了。 我希望有类似的功能,比如instagram,你可以从feed切换到explore,再切换到feed到滚动位置。图像缓存所有保存的内容。我尝试了几乎所有的方法来解决这个问题唯一有效的方法是设置可见
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private FragmentManager fragmentManager;
private Fragment currentFragment;
String TAG = "babken";
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
Fragment fragment = null;
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.navigation_dashboard:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = FragmentSecond.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
}
return true;
}
};
private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.armen, fragment, tag)
.commit();
currentFragment = fragment;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
怎么样。
在类中声明片段
Fragment firstFragment,secondFragment,thirdFragment;
然后在开关情况下可以这样编码:
switch (item.getItemId()) {
case R.id.menu_dialer:
if(firstFragment != null) {
fragment = firstFragment;
}else{
fragment = FirstFragment.newInstance();
}
break;
case R.id.menu_email:
// the same ...
break;
case R.id.menu_map:
//the same ...
break;
}
创建三个片段作为类的成员并重用它们
public class MainActivity extends AppCompatActivity {
private final Fragment mFirstFragment = new FirstFragment();
private final Fragment mSecondFragment = new SecondFragment();
private final Fragment mThirdFragment = new ThirdFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
......
......
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, mFirstFragment);
fragmentTransaction.commit();
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
if (bottomNavigationView.getSelectedItemId() != item.getItemId()) {
switch (item.getItemId()) {
R.id.menu_dialer:
replaceFragment(mFirstFragment);
break;
case R.id.menu_email:
replaceFragment(mSecondFragment);
break;
case R.id.menu_map:
replaceFragment(mThirdFragment);
break;
}
}
return true;
}
});
}
private void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
}
我不会全局保留片段实例。 而是在创建片段时向片段添加标记
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, new PlaceholderFragment(), TAG_PLACEHOLDER)
.commit();
然后,您可以像这样检索它:
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
if (fragment == null) {
fragment = new PlaceholderFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, TAG_PLACEHOLDER)
.commit();
更新:我更新了我的答案并提供了完整的解决方案:
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private static final String TAG_FRAGMENT_THREE = "fragment_three";
private FragmentManager fragmentManager;
private Fragment currentFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// instantiate the fragment manager
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
// I'm aware that this code can be optimized by a method which accepts a class definition and returns the proper fragment
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.menu_email:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = SecondFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
case R.id.menu_map:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_THREE);
if (fragment == null) {
fragment = ThirdFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_THREE);
break;
}
return true;
}
});
}
private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.frameLayout, fragment, tag)
.commit();
currentFragment = fragment;
}
}
附加信息:如果你想确定片段状态没有改变,如果你还想能够擦除碎片,你应该考虑使用A和A,并且在每次点击事件
改变适配器中的当前片段,所以我已经研究了很长时间,并且发现没有办法重用。自动保存状态的片段您必须手动保存所需的状态,然后在创建新片段时检索它们,但滚动位置又如何?滚动位置太难,甚至在某些情况下,无法保存滚动视图位置状态(例如回收器视图)。因此,我在点击按钮时使用了称为可见性的概念,即片段变为可见,其他人自动隐藏。前面的所有答案都使用fragmentTransaction.replace(…)
。这将通过销毁当前片段(导致问题)来替换当前片段。因此,所有这些解决方案实际上都不起作用
这是我能找到的最接近这个问题的解决方案:
private void selectContentFragment(Fragment fragmentToSelect)
{
FragmentTransaction fragmentTransaction = this.getSupportFragmentManager().beginTransaction();
if (this.getSupportFragmentManager().getFragments().contains(fragmentToSelect)) {
// Iterate through all cached fragments.
for (Fragment cachedFragment : this.getSupportFragmentManager().getFragments()) {
if (cachedFragment != fragmentToSelect) {
// Hide the fragments that are not the one being selected.
fragmentTransaction.hide(cachedFragment);
}
}
// Show the fragment that we want to be selected.
fragmentTransaction.show(fragmentToSelect);
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction.add(R.id.fragment_container, fragmentToSelect);
}
fragmentTransaction.commit();
}
要使这项工作正常,您应该跟踪活动中数组(或单独变量)中的片段。作为参考,我预先实例化了SparseArray中的所有片段。我也有类似的问题,但这段代码解决了我的问题
public class MainActivity extends AppCompatActivity {
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
fm.beginTransaction().add(R.id.main_container, fragment3, "3").hide(fragment3).commit();
fm.beginTransaction().add(R.id.main_container, fragment2, "2").hide(fragment2).commit();
fm.beginTransaction().add(R.id.main_container,fragment1, "1").commit();
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fm.beginTransaction().hide(active).show(fragment1).commit();
active = fragment1;
return true;
case R.id.navigation_dashboard:
fm.beginTransaction().hide(active).show(fragment2).commit();
active = fragment2;
return true;
case R.id.navigation_notifications:
fm.beginTransaction().hide(active).show(fragment3).commit();
active = fragment3;
return true;
}
return false;
}
};
希望这有帮助
来源:您可以使用attach()和detach()方法:
我为FragmentManager类编写了一个Kotlin扩展函数
fun FragmentManager.switch(containerId: Int, newFrag: Fragment, tag: String) {
var current = findFragmentByTag(tag)
beginTransaction()
.apply {
//Hide the current fragment
primaryNavigationFragment?.let { hide(it) }
//Check if current fragment exists in fragmentManager
if (current == null) {
current = newFrag
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
}
这可以通过
supportFragmentManager.swtich(R.id.container,newFrag,newFrag.TAG)
从onNavigationItemSelected
调用,Thomas的回答几乎帮了我的忙,但有一个问题,每当我第一次打开新片段时,它们重叠了,但一旦我按菜单按钮再次打开它们,它们就不会重叠
因此,我修改了他的代码,并使用以下代码获得了解决方案:
private fun selectContentFragment(fragmentToSelect: Fragment) {
val fragmentTransaction = fragmentManager?.beginTransaction()
if (fragmentManager?.fragments?.contains(fragmentToSelect)!!) {
// Show the fragment that we want to be selected.
fragmentTransaction?.show(fragmentToSelect)
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction?.add(R.id.container, fragmentToSelect)
}
// Iterate through all cached fragments.
for (cachedFragment in fragmentManager?.fragments!!) {
if (cachedFragment !== fragmentToSelect) {
// Hide the fragments that are not the one being selected.
// Uncomment following line and change the name of the fragment if your host isn't an activity and a fragment otherwise whole view will get hidden.
// if (!cachedFragment.toString().contains("HomeContainerFragment"))
fragmentTransaction?.hide(cachedFragment)
}
}
fragmentTransaction?.commit()
}
确保每次都没有传递片段的新实例
这将起作用:
selectContentFragment(
when (item.itemId) {
R.id.home -> frag1
R.id.photoGallery -> frag2
else -> Home()
}
)
selectContentFragment(
when (item.itemId) {
R.id.home -> Home()
R.id.photoGallery -> PhotoGallery()
else -> Home()
}
)
其中frag1
和frag2
是全局变量,定义如下:
val frag1 = Home()
val frag2 = PhotoGallery()
这不起作用:
selectContentFragment(
when (item.itemId) {
R.id.home -> frag1
R.id.photoGallery -> frag2
else -> Home()
}
)
selectContentFragment(
when (item.itemId) {
R.id.home -> Home()
R.id.photoGallery -> PhotoGallery()
else -> Home()
}
)
它浪费了我几个小时。希望它能帮助别人 **这段代码对我很有帮助。就像youtube**
private Deque<Integer> fragmentIds = new ArrayDeque<>(3);
int itemId;
private HomeFragment homeFragment = new HomeFragment();
private FavouriteFragment favouriteFragment = new FavouriteFragment();
private NearmeFragment nearmeFragment = new NearmeFragment();
BottomNavigationView bottomNavigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentIds.push(R.id.action_home);
showTabWithoutAddingToBackStack(homeFragment);
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemClicked);
}
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked = new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
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.action_home:
return homeFragment;
case R.id.action_favorites:
return favouriteFragment;
case R.id.action_nearme:
return nearmeFragment;
}
return homeFragment;
}
private void showTabWithoutAddingToBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()).commit();
}
@Override
public void onBackPressed() {
if(fragmentIds.getLast() != R.id.action_home){
fragmentIds.addLast(R.id.action_home);
}
fragmentIds.pop();
bottomNavigationView.getMenu().getItem(fragmentIds.size()-1).setChecked(true);
if (!fragmentIds.isEmpty()) {
showTabWithoutAddingToBackStack(getFragment(fragmentIds.peek()));
} else {
finish();
}
}
private Deque fragmentIds=new ArrayDeque(3);
int itemId;
私有HomeFragment HomeFragment=新的HomeFragment();
private FavoriteFragment FavoriteFragment=新的FavoriteFragment();
private NearmeFragment NearmeFragment=新的NearmeFragment();
底部导航视图底部导航视图;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
碎片id.推送(R.id.action\U home);
不添加数据包堆栈的ShowTab(homeFragment);
bottomNavigationView=findViewById(R.id.bottom\u导航);
bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemClicked);
}
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked=新建BottomNavigationView.OnNavigationItemSelectedListener(){
@凌驾
公共布尔值onNavigationItemSelected(@NonNull MenuItem item){
itemId=item.getItemId();
if(fragmentIds.contains(itemId)){
fragmentId.remove(itemId);
}
fragmentIds.push(itemId);
ShowTab不添加堆栈(getFragment(item.getItemId());
返回true;
}
};
私有片段getFragment(int fragmentId){
交换机(碎片ID){
案例R.id.行动之家:
返回homeFragment;
案例R.id.action_收藏夹:
返回偏好片段;
案例R.id.action_nearme:
返回片段;
}
返回homeFragment;
}
私有void showtab不添加堆栈(片段){
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_容器,fragment,fragment.getClass().getSimpleName()).commit();
}
@凌驾
public void onBackPressed(){
if(fragmentIds.getLast()
fun FragmentManager.replace(containerId: Int, fragment: Fragment, tag: String) {
var current = findFragmentByTag(tag)
beginTransaction()
.apply {
//Hide the current fragment
primaryNavigationFragment?.let { hide(it) }
//Check if current fragment exists in fragmentManager
if (current == null) {
current = fragment
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
onNavigationItemSelected
supportFragmentManager.replace(R.id.fragment_id,YourFragmentClass,yourFragment.TAG)
<androidx.fragment.app.FragmentContainerView
android:id="@+id/homeFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/libraryFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/myStuffFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/moreFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_NavBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/app_bottom_nav_menu"
tools:ignore="BottomAppBar" />
supportFragmentManager.beginTransaction().replace(R.id.homeFragmentHost,HomeFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.libraryFragmentHost,LibraryFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.myStuffFragmentHost,MyStuffFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.moreFragmentHost,MoreFragment()).commitNow()
homeFragmentHost.visibility = View.VISIBLE
override fun onNavigationItemSelected(item: MenuItem): Boolean {
return when(item.itemId){
R.id.homeFragment -> loadFragmentHost(homeFragmentHost)
R.id.libraryFragment -> loadFragmentHost(libraryFragmentHost)
R.id.myStuffFragment -> loadFragmentHost(myStuffFragmentHost)
R.id.moreFragment -> loadFragmentHost(moreFragmentHost)
else -> false
}
private fun loadFragmentHost(view:FragmentContainerView): Boolean {
val list = arrayListOf(homeFragmentHost,libraryFragmentHost,myStuffFragmentHost,moreFragmentHost)
list.remove(view)
view.visibility = View.VISIBLE
list.forEach {
it.visibility = View.GONE
}
return true
}