Fragment add show hide 异常销毁后重叠问题

在新项目的开发过程中,无意间点了几下首页的底部导航 发现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

发布了20 篇原创文章 · 获赞 5 · 访问量 9500

猜你喜欢

转载自blog.csdn.net/Small_Wave_Wave/article/details/89189649