根本上解决多重多层级ViewPgaer嵌套Fragment,Fragment是否可见问题

根本上解决多重多层级ViewPgaer嵌套Fragment,Fragment是否可见问题

如果是简单的一个ViewPager里面放置若干个Fragment,然后判断当前处于用户可见视野范围内,这个问题容易解决,方案也很多,在此不再具体解释。最麻烦的是ViewPager与Fragment发生多重嵌套,并且嵌套的层级很深时候,判断一个特定的Fragment是否可见变的是否棘手。比如ViewPagerA里面放几个Fragment(Fa,Fb,Fc),而Fa里面有放一个ViewPagerB,而ViewPagerB里面又盛放若干个Fragment(F甲,F乙,F丙),而 F甲 里面又再放一个ViewPagerC,ViewPagerC里面又放几个Fragment(F1,,F2,F3)…… 就像这样嵌套的越来越深时候,判断某一个特定的Fragment是否可见变的是否困难,但是这个需求和技术点又非常需要明确。典型的假如这些Fragment实时性都很强,都需要周期性的从服务器读取数据更新UI,如果让所有Fragment都同时实时更新,开销比较大,节俭的做法是只更新当前用户切换到的、处于用户视野可见范围内的Fragment数据才更新,而那些已经被用户切换走、处于不可见的Fragment暂停更新,但是当用户把那些原先不可见的Fragment切换到可见视野范围内又重新回来后,又有必要重新启动更新了。
我写一个代码例子:

package zhangphil.test;

import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class ViewPagerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.view_pager);

        ArrayList<Fragment> mFragments = new ArrayList<>();
        mFragments.add(TestFragmetA.newInstance("A0"));
        mFragments.add(TestFragmetA.newInstance("A1"));
        mFragments.add(TestFragmetA.newInstance("A2"));
        mFragments.add(TestFragmetA.newInstance("A3"));

        ViewPager mViewPager = findViewById(R.id.view_pager);
        MyPagerAdapter mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mFragments);
        mViewPager.setAdapter(mPagerAdapter);

        TabLayout mTabLayout = findViewById(R.id.tab_layout);

        for (int i = 0; i < mPagerAdapter.getCount(); i++) {
            TabLayout.Tab tab = mTabLayout.newTab();
            tab.setText("A" + i);
            mTabLayout.addTab(tab);
        }

        mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
        mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager) {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                mViewPager.setCurrentItem(tab.getPosition());

                mFragments.get(tab.getPosition()).setUserVisibleHint(true);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                mFragments.get(tab.getPosition()).setUserVisibleHint(false);
            }
        });

        /**
         * 全局的周期更新信号发射源,每隔3秒发一个信号,这个信号触发一次更新操作。
         */
        new Thread(new Runnable() {
            int count = 0;

            @Override
            public void run() {
                while (true) {
                    String string = count + "";
                    EventBus.getDefault().post(string);

                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    count++;
                }
            }
        }).start();
    }

    public static class MyPagerAdapter extends FragmentPagerAdapter {

        private ArrayList<Fragment> fragments;

        public MyPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
            super(fm);
            this.fragments = fragments;
        }

        @Override
        public Fragment getItem(int position) {
            return fragments.get(position);
        }

        @Override
        public int getCount() {
            return fragments.size();
        }
    }

    public static class TestFragmetA extends Fragment {
        private static String TAG = "A";

        public static TestFragmetA newInstance(String tag) {
            Bundle args = new Bundle();
            args.putString(TAG, tag);
            TestFragmetA fragment = new TestFragmetA();
            fragment.setArguments(args);
            return fragment;
        }


        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.view_pager, container, false);
            return v;
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            ArrayList<Fragment> mFragments = new ArrayList<>();
            mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-甲"));
            mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-乙"));
            mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-丙"));
            mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-丁"));

            ViewPager mViewPager = view.findViewById(R.id.view_pager);
            MyPagerAdapter mPagerAdapter = new MyPagerAdapter(getChildFragmentManager(), mFragments);
            mViewPager.setAdapter(mPagerAdapter);

            TabLayout mTabLayout = view.findViewById(R.id.tab_layout);

            for (int i = 0; i < mPagerAdapter.getCount(); i++) {
                TabLayout.Tab tab = mTabLayout.newTab();
                tab.setText("B" + i);
                mTabLayout.addTab(tab);
            }

            mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
            mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager) {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    mViewPager.setCurrentItem(tab.getPosition());

                    mFragments.get(tab.getPosition()).setUserVisibleHint(true);
                }

                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    mFragments.get(tab.getPosition()).setUserVisibleHint(false);
                }
            });
        }

        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            //这里的 isVisibleToUser 已经被上一层级切换TabLayout中设置过。
        }
    }

    public static class TestFragmetB extends Fragment {
        private static String TAG = "B";
        private TextView mRootView;

        //根据生命周期控制是否更新。
        private boolean CAN_REFRESH = false;

        public static TestFragmetB newInstance(String tag) {
            Bundle args = new Bundle();
            args.putString(TAG, tag);
            TestFragmetB fragment = new TestFragmetB();
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EventBus.getDefault().register(this);
        }

        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            if (mRootView == null) {
                mRootView = new TextView(container.getContext());
                mRootView.setGravity(Gravity.LEFT | Gravity.TOP);
                mRootView.setText(getArguments().getString(TAG));
                mRootView.setTextColor(Color.RED);
                mRootView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
            }

            return mRootView;
        }

        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onMsgEvent(String msg) {
            /**
             * 刷新判断条件是否成立。
             */
            if (getUserVisibleHint() && getParentFragment().getUserVisibleHint() && CAN_REFRESH) {
                update(msg);
            }
        }

        /**
         * 这里可以进行UI刷新。
         *
         * @param msg
         */
        private void update(String msg) {
            if (mRootView != null) {
                String s = mRootView.getText() + "";
                mRootView.setText(s + "," + msg);
            }
        }

        @Override
        public void onResume() {
            super.onResume();
            CAN_REFRESH = true;
        }

        @Override
        public void onPause() {
            super.onPause();
            CAN_REFRESH = false;
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            CAN_REFRESH = false;
            EventBus.getDefault().unregister(this);
        }
    }
}

布局文件:

<?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"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


在ViewPager与Fragment多重嵌套且层级深时候,我的这个方案采用主动通过setUserVisibleHint设置isVisibleToUser的值,在TabLayout(本例是TabLayout,可以是其他形式触发ViewPager切换)切换时候,TabLayout中调度ViewPager加载哪一个Fragment是明确的,那么就给当前TabLayout选中的那个Fragment(也即是ViewPager切换到的Fragment)的isVisibleToUser设置为true,其他未被选中的Fragment,isVisibleToUser设置为false。因为ViewPager中的某一个Fragment又嵌套了一个ViewPager,所以需要把上一级的TabLayout选中Fragment可见状态传递下去,所以在Fragment中,如果Fragment又包含一重ViewPager,则和上一步一样,把isVisibleToUser的值在本级TabLayout切换中传递下去(通过setUserVisibleHint)。
做了这么多isVisibleToUser的传递,目的最终是打算通过getUserVisibleHint()来判断当前Fragment是否可见。
本例通过EventBus没隔3秒发送一个更新触发信号,在所有Fragment中都可以接收这个更新信号,但是只有处于用户可见的Fragment才可以更新,而判断当前Fragment是否处于可见,就通过getUserVisibleHint()返回的布尔值,注意到本例还有一个getParentFragment().getUserVisibleHint()作为当前Fragment是否处于可见的联合判断条件之一,是因为Fragment嵌套的太深了,当前Fragment的上一层级Fragment也必须在多重嵌套中处于可见时候,才满足最终的可见条件判断。
同时增加了一个CAN_REFRESH布尔值作为更新判断条件之一,CAN_REFRESH目的是为了标记当前Fragment的生命周期情况,假设一种场景,当前Fragment的确处于可见,但是用户按了手机的home键,导致整个App处于不可见,那么getUserVisibleHint()方法捕捉不到这种变化,需要联合生命周期的onResume和onPause共同判断当前Fragment是否处于手机屏幕的可见状态。
代码运行后,每一个Fragment只有出于可见状态时候,接收到EventBus的更新消息并把数据显示到屏幕上,如果处于不可见,不会显示EventBus发射的更新信号。来回切换Fragment和ViewPager时候,只有出于当前可见的Fragment才会更新到最新的EventBus发射的消息编号。其他不可见的Fragment不会更新,但是切换到时候,就会追加更新到最新EventBus更新消息编号。

猜你喜欢

转载自blog.csdn.net/zhangphil/article/details/81738658
今日推荐