在新项目的开发过程中,无意间点了几下首页的底部导航 发现fragment 竟然空白不展示了,what!!
这个新项目我首页导航用的是 ViewPager+Fragment+BottomNavigationView
ViewPager进行了自定义,保证它不能滑动和间隔页面(比如 目前在1 点击到4的时候)的时候不会有滑动的动画
代码贴出来
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.cmcm.teacherconsumers.R;
/**
* @author wavewave
* @CreateDate: 2019/3/20 7:04 PM
* @Description:
* @Version: 1.0
*/
public class MyViewPager extends ViewPager {
private boolean isCanScroll = false;
public MyViewPager(@NonNull Context context) {
super(context);
}
public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LazyViewPager);
setInitLazyItemOffset(a.getFloat(R.styleable.LazyViewPager_init_lazy_item_offset, DEFAULT_OFFSET));
a.recycle();
}
/**
* 设置其是否能滑动换页
*
* @param isCanScroll false 不能换页, true 可以滑动换页
*/
public void setScanScroll(boolean isCanScroll) {
this.isCanScroll = isCanScroll;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isCanScroll && super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return isCanScroll && super.onTouchEvent(ev);
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {
super.setCurrentItem(item, false);
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item, false);
}
}
然后在做到接口的时候发现一个问题就是fragment 切换后会一直重新创建,想起ViewPager有个
setOffscreenPageLimit(); 方法 可以设置缓存数量
这个设置后 它会提前加载的。。。根据情况使用
对于我来说,这种一打开就请求所有接口的,,,我怕老大打死我。
于是想起了好久之前的开源项目 懒加载 LazyFragmentPagerAdapter
导入进去使用。不错不错确实是懒加载 生命周期都不一样。
但是今天点出bug了,操作流程是
我的标签一共是四个,打开页面
点击第四个标签,然后再点击第二个标签,空白了。。。。
生命周期都没有走,debug找找 好像是懒加载机制里面的问题,
因为我比较懒,所以就换最古老的写法了。
Fragment add(), show(),hide()
因为之前也写过好多次,这次变抽出方法来
加入的方法
private void showFragment(Fragment fragment, String tag) {
if (TextUtils.isEmpty(tag)) return;
hideFragment();
if (fragment == null) {
if (tag.equals(fragmentTag[1])) {
chatFragment = ChatFragment.newInstance("", "");
getSupportFragmentManager().beginTransaction().add(R.id.main_ll, chatFragment, fragmentTag[1]).commit();
} else if (tag.equals(fragmentTag[2])) {
studentFragment = StudentFragment.newInstance("", "");
getSupportFragmentManager().beginTransaction().add(R.id.main_ll, studentFragment, fragmentTag[2]).commit();
} else if (tag.equals(fragmentTag[3])) {
myFragment = MyFragment.newInstance("", "");
getSupportFragmentManager().beginTransaction().add(R.id.main_ll, myFragment, fragmentTag[3]).commit();
} else {
homeFragment = HomeFragment.newInstance("", "");
getSupportFragmentManager().beginTransaction().add(R.id.main_ll, homeFragment, fragmentTag[0]).commit();
}
} else {
getSupportFragmentManager().beginTransaction().show(fragment).commit();
}
}
隐藏的方法
private void hideFragment() {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if (homeFragment != null) fragmentTransaction.hide(homeFragment);
if (chatFragment != null) fragmentTransaction.hide(chatFragment);
if (studentFragment != null) fragmentTransaction.hide(studentFragment);
if (myFragment != null) fragmentTransaction.hide(myFragment);
fragmentTransaction.commit();
}
写这个要注意一下,如果当前activity 被异常销毁重建的时候,会有重叠问题
打开手机的开发者选项,往下拉 找到 应用下面的 不保留活的 开关打开就可以测试拉(我的手机是华为,其他也会有这个选项)
于是动手解决一下,思路很简单
就是在它重建的时候,在FragmentManager通过Tag重新把 fragment对象找回来就好了
我们还要保证销毁前的样子 所以还要手动去保存一个 索引
重写 onSaveInstanceState(Bundle outState)
//记录当前选中的 fragment 索引用于异常销毁 恢复
private int selectPosition = -1;
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECT_FRAGMENT, selectPosition);
}
然后在 oncreate的时候恢复 我封装的Base 所以自己写的方法 叫做 afterCreate
@Override
protected void afterCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {//处理activity 异常销毁恢复 fragment
selectPosition = savedInstanceState.getInt(SELECT_FRAGMENT, -1);
saveFragment(selectPosition);
} else {
showFragment(homeFragment, fragmentTag[0]);
}
mainBnv.setOnNavigationItemSelectedListener(this);
}
/**
* 用于恢复 activity 异常销毁的fragment
*
* @param anInt
*/
private void saveFragment(int anInt) {
homeFragment = (HomeFragment) getSupportFragmentManager().findFragmentByTag(fragmentTag[0]);
chatFragment = (ChatFragment) getSupportFragmentManager().findFragmentByTag(fragmentTag[1]);
studentFragment = (StudentFragment) getSupportFragmentManager().findFragmentByTag(fragmentTag[2]);
myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(fragmentTag[3]);
switch (anInt) {
case 0:
default:
showFragment(homeFragment, fragmentTag[0]);
break;
case 1:
showFragment(chatFragment, fragmentTag[1]);
break;
case 2:
showFragment(studentFragment, fragmentTag[2]);
break;
case 3:
showFragment(myFragment, fragmentTag[3]);
break;
}
}
当然可能 有些不好上手 于是我写个demo放到github 上
https://github.com/renwavewave/FragmentDemo
业余时间也实现了另外一种方式,比这个简单方便
依然用ViewPager+Fragment+BottomNavigationView
ViewPager还是用的上面我自定义的MyViewPager
设置ViewPager的缓存数量为3 因为我只有四个所以设置3个
然后在fragment里面重写
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.d("aaaaa", "student isVisibleToUser:" + isVisibleToUser);
}
这个方法可以告诉我们 当前的fragment是否正在展示给用户看
isVisibkeToUser true表示当前正在展示
我们可以在 true的时候去获取网络数据
因为我们设置了ViewPager的缓存数量 所以一打开页面后,生命周期都会走一遍。
但是我们因为网络请求放到setUserVisibleHide 方法了。所以当前显示fragment的时候才会请求
这样也可以实现懒加载了。
demo里面 有这个实现 类名 叫 ViewPagerActivity