• Android之ActionBar、Tabs、Fragment、ViewPager实现标签页切换并缓存页面


    感觉 Android 到处都是坑,每个地方都要把人折腾半天。

    今天来简单说说 Android之ActionBar、Tabs、Fragment、ViewPager 实现标签页切换并缓存页面

    关于他们的介绍就不多说了,网上到处都是,只说关键的部分:

    我在开发的时候遇到几个疑难问题,花费大量时间处理,总结如下:

    1. 关于 Fragment 内部逻辑处理该写在哪个事件回调部分?

    2. ViewPager 页面切换动画卡顿,让我头疼了很久。

    3. ViewPager 中如何保存 Fragment 当前视图的状态,让 Tabs 页面切换后不会重新加载,这地方很坑爹

    4. ActionBar 中的 tab 很多时如何滚动显示


    解答:

    一、Fragment 的事件回调:

    package com.ai9475.meitian.ui.fragment;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ListView;
    
    import com.ai9475.meitian.R;
    import com.ai9475.meitian.view.DiaryList;
    import com.ai9475.util.ZLog;
    
    /**
     *
     * Created by ZHOUZ on 14-1-21.
     */
    public class DiaryListFragment extends BaseFragment
    {
        private static final String TAG = "DiaryListFragment";
    
        @Override
        public void onAttach(Activity activity)
        {
            ZLog.i(TAG, "onAttach");
            super.onAttach(activity);
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            ZLog.i(TAG, "onCreate");
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            ZLog.i(TAG, "onCreateView");
            return inflater.inflate(R.layout.fragment_diary_list, container, false);
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
            ZLog.i(TAG, "onActivityCreated");
            super.onActivityCreated(savedInstanceState);
            ZLog.i(TAG, "DiaryList0");
            DiaryList diaryList = new DiaryList(
                    getActivity().getApplicationContext(),
                    (ListView) getView().findViewById(R.id.diaryListCt)
    
            );
            ZLog.i(TAG, "DiaryList load0");
            diaryList.load("http://m.ai9475.com/?con=meitian_app");
            this.setRetainInstance(true);
        }
    
        @Override
        public void onStart()
        {
            ZLog.i(TAG, "onStart");
            super.onStart();
        }
    
        @Override
        public void onResume()
        {
            ZLog.i(TAG, "onResume");
            super.onResume();
        }
    
        @Override
        public void onPause()
        {
            ZLog.i(TAG, "onPause");
            super.onPause();
        }
    
        @Override
        public void onStop()
        {
            ZLog.i(TAG, "onStop");
            super.onStop();
        }
    
        @Override
        public void onDestroyView()
        {
            ZLog.i(TAG, "onDestroyView");
            super.onDestroyView();
        }
    
        @Override
        public void onDestroy()
        {
            ZLog.i(TAG, "onDestroy");
            super.onDestroy();
        }
    
        @Override
        public void onDetach()
        {
            ZLog.i(TAG, "onDetach");
            super.onDetach();
        }
    }
    

    上面的类中的 on 事件就是Fragment主要处理的时间回调,注意复写父类方法时要回调执行父类同名方法,否则会出错

    主要复写 onCreateView 方法,返回该 Fragment 所对应的视图对象,这里可以在返回视图对象前进行一些简单的配置,但千万不要写太耗时的处理阻塞UI主线程。

    另外 onActivityCreated 方法,是当 activity 的 onCreate 事件结束时的回调,此时当前的Fragment对应的view已经并入到整个布局中,此时可以使用 getView() 方法获取视图对象。

    其他几个事件没什么太多可说,有些我也还不是太清楚,还有些动画调用的事件。


    二、切换页面卡顿问题

    这个问题的产生主要可能是两方面,

    1. 没有使用 ViewPager 的缓存,每次切换都重新加载。

    2. 加载 Fragment 内部有耗时耗资源的逻辑处理。

    这里主要说下第二种情况,我一开始没处理掉 缓存问题时有一个解决办法,

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

    mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
    mViewPager.setOnPageChangeListener(
                    new ViewPager.SimpleOnPageChangeListener() {
                        private static final String TAG = "ViewPager.SimpleOnPageChangeListener";
                        private ArrayList hasLoadedPages = new ArrayList<Integer>();
                        @Override
                        public void onPageSelected(int position) {
                            ZLog.i(TAG, "onPageSelected position:"+ position);
                            getSupportActionBar().setSelectedNavigationItem(position);
                        }
    
                        @Override
                        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
                        {
                            ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);
                        }
    
                        @Override
                        public void onPageScrollStateChanged(int state) {
                            ZLog.i(TAG, "onPageScrollStateChanged");
                            int position = mViewPager.getCurrentItem();
    
                            switch (state) {
                                // 正在拖动
                                case ViewPager.SCROLL_STATE_DRAGGING :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);
                                    break;
                                // 拖动释放后正在沉降的过程
                                case ViewPager.SCROLL_STATE_SETTLING :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);
                                    break;
                                // 切换动画全部完成结束
                                case ViewPager.SCROLL_STATE_IDLE :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);
    				// 已加载过则不再加载
                                    if (hasLoadedPages.contains(position)) break;
                                    Fragment fragment = mPager.getFragments().get(position);
                                    runCallback(position, fragment);
                                    hasLoadedPages.add(position);
                                    break;
                            }
                        }
    
                        public void runCallback(int position, Fragment fragment) {
                            ZLog.i(TAG, "runCallback");
                            DiaryList diaryList;
                            switch (position) {
                                case 0 :
                                    ZLog.i(TAG, "DiaryList0");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load0");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                    break;
                                case 1:
                                    ZLog.i(TAG, "DiaryList1");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load1");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");
                                    break;
                                case 2:
                                    ZLog.i(TAG, "DiaryList2");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load2");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                    break;
                            }
                        }
                    }

    这里主要用到 public void onPageScrollStateChanged(int state) 页面滚动切换状态变化的事件监听

    当滚动动画完全结束 case ViewPager.SCROLL_STATE_IDLE  时再执行 Fragment 的逻辑处理,这样动画就会流畅了。

    但经过后来的测试发现有个很简单的解决方案,就是下面要说到的 ViewPager 的缓存功能,其实很简单。


    三、缓存 Tabs 页面切换不重新加载数据

    我在这地方折腾的最久,而且很多时候无从下手的感觉,网上搜索了很多文章都有说道保存 Fragment 数据和状态,但是没有整整提到如何来保存他的当前view状态,不知道如何保存,当然实际上我还是没搞懂如何仅仅缓存 Fragment 的状态,但对于 ViewPager 缓存 Tab 对应的 Fragment 还是找到了办法,之前花了很大功夫来自己实现,后来偶然发现他居然有个自带的方法

            // 设置缓存多少个 Tab对应的 fragment
            mViewPager.setOffscreenPageLimit(6);

    我测试时用了 6个 listView 加载图片列表数据,切换动画也没有任何卡顿现象,非常流畅,就这么简单一句就搞定了。

    配置该项后,ViewPager在切换时将不会清理不可见的 Fragment,不会触发 Fragment 的任何事件,因此也就不会导致其重新加载。


    四、ActionBar 中的 Tabs 

    这个其实不用操作,在tabs数量超过一屏后,例如我现在设置6个宽度超过了屏幕则会变成可以横向滚动的状态,而不需要自己实现,之前不知道在这方面查了很多资料都无果,只知道可以用 tabhost 可以实现,但在ActionBar 里面却又用不了,自己试了下方多个tab才发现原来会自动实现,无语。




    还是贴上 MainActivity.class 完整代码:

    package com.ai9475.meitian.ui;
    
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.app.FragmentStatePagerAdapter;
    import android.support.v4.app.FragmentTransaction;
    import android.support.v4.view.ViewPager;
    import android.support.v7.app.ActionBar;
    import android.view.Menu;
    import android.widget.ListView;
    
    import com.ai9475.meitian.AppManager;
    import com.ai9475.meitian.R;
    import com.ai9475.meitian.ui.fragment.DiaryListFragment;
    import com.ai9475.meitian.ui.fragment.Test2Fragment;
    import com.ai9475.meitian.ui.fragment.Test3Fragment;
    import com.ai9475.meitian.view.DiaryList;
    import com.ai9475.util.ZLog;
    
    import java.util.ArrayList;
    
    public class MainActivity extends BaseActivity
    {
        private static final String TAG = "MainActivity";
        private MyTabsPagerAdapter mPager;
        private ViewPager mViewPager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            ZLog.i(TAG, "start");
            // 执行父级初始化方法
            super.onCreate(savedInstanceState);
            //requestWindowFeature(Window.FEATURE_NO_TITLE);
            ZLog.i(TAG, "setContentView");
            setContentView(R.layout.activity_main);
    
            // 滑动页面视图配置
            ZLog.i(TAG, "MyTabsPagerAdapter start");
            mPager = new MyTabsPagerAdapter(getSupportFragmentManager());
            mPager.getFragments().add(new DiaryListFragment());
            mPager.getFragments().add(new Test2Fragment());
            mPager.getFragments().add(new Test3Fragment());
            mPager.getFragments().add(new Test3Fragment());
            mPager.getFragments().add(new Test3Fragment());
            mPager.getFragments().add(new Test3Fragment());
            // 滑动分页容器
            mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
            // 设置缓存多少个 fragment
            mViewPager.setOffscreenPageLimit(6);
            mViewPager.setAdapter(mPager);
            // 页面滑动事件
            mViewPager.setOnPageChangeListener(
                    new ViewPager.SimpleOnPageChangeListener() {
                        private static final String TAG = "ViewPager.SimpleOnPageChangeListener";
                        private ArrayList hasLoadedPages = new ArrayList<Integer>();
                        @Override
                        public void onPageSelected(int position) {
                            ZLog.i(TAG, "onPageSelected position:"+ position);
                            getSupportActionBar().setSelectedNavigationItem(position);
                        }
    
                        @Override
                        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
                        {
                            ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);
                        }
    
                        @Override
                        public void onPageScrollStateChanged(int state) {
                            ZLog.i(TAG, "onPageScrollStateChanged");
                            int position = mViewPager.getCurrentItem();
    
                            switch (state) {
                                // 正在拖动
                                case ViewPager.SCROLL_STATE_DRAGGING :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);
                                    break;
                                // 拖动释放后正在沉降的过程
                                case ViewPager.SCROLL_STATE_SETTLING :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);
                                    break;
                                // 切换动画全部完成结束
                                case ViewPager.SCROLL_STATE_IDLE :
                                    ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);
                                    /*if (hasLoadedPages.contains(position)) break;
                                    Fragment fragment = mPager.getFragments().get(position);
                                    runCallback(position, fragment);
                                    hasLoadedPages.add(position);*/
                                    break;
                            }
                        }
    
                        public void runCallback(int position, Fragment fragment) {
                            ZLog.i(TAG, "runCallback");
                            DiaryList diaryList;
                            switch (position) {
                                case 0 :
                                    ZLog.i(TAG, "DiaryList0");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load0");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                    break;
                                case 1:
                                    ZLog.i(TAG, "DiaryList1");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load1");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");
                                    break;
                                case 2:
                                    ZLog.i(TAG, "DiaryList2");
                                    diaryList = new DiaryList(
                                            getApplicationContext(),
                                            (ListView) fragment.getView().findViewById(R.id.diaryListCt)
    
                                    );
                                    ZLog.i(TAG, "DiaryList load2");
                                    diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                    break;
                            }
                        }
                    }
            );
    
            ActionBar actionBar = getSupportActionBar();
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
            // Tab 页面切换
            MyTabListener listener = new MyTabListener();
            // 默认的首页 tab
            ActionBar.Tab indexTab = actionBar.newTab()
                    .setText(getString(R.string.tab_index))
                    .setTabListener(listener);
            actionBar.addTab(indexTab);
            actionBar.addTab(actionBar.newTab()
                    .setText(getString(R.string.tab_hot))
                    .setTabListener(listener)
            );
            actionBar.addTab(actionBar.newTab()
                    .setText(getString(R.string.tab_tag))
                    .setTabListener(listener)
            );
            actionBar.addTab(actionBar.newTab()
                    .setText(getString(R.string.tab_tag))
                    .setTabListener(listener)
            );
            actionBar.addTab(actionBar.newTab()
                    .setText(getString(R.string.tab_tag))
                    .setTabListener(listener)
            );
            actionBar.addTab(actionBar.newTab()
                    .setText(getString(R.string.tab_tag))
                    .setTabListener(listener)
            );
    
            // 显示首页
            indexTab.select();
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        private class MyTabListener implements ActionBar.TabListener
        {
            @Override
            public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
            {
                int position = tab.getPosition();
                ZLog.i(TAG, "tab selected: "+ position);
                // 数据通信
                /*Bundle bundle = new Bundle();
                Fragment fragment = mPager.getItem(tab.getPosition());
                Toast.makeText(getApplicationContext(), "position:"+ tab.getPosition(), Toast.LENGTH_SHORT).show();
                fragment.setArguments(bundle);
                FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
                fragmentTransaction.add(R.id.fragmentContainer, fragment);
                fragmentTransaction.commit();*/
                mViewPager.setCurrentItem(position);
            }
    
            @Override
            public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
                ZLog.i(TAG, "tab reselected: "+ tab.getPosition());
            }
    
            @Override
            public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
            {
                ZLog.i(TAG, "tab unselected: "+ tab.getPosition());
            }
        };
    
        public class MyTabsPagerAdapter extends FragmentPagerAdapter
        {
            private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
    
            public MyTabsPagerAdapter(FragmentManager fm) {
                super(fm);
            }
    
            public ArrayList<Fragment> getFragments() {
                return this.mFragments;
            }
    
            @Override
            public Fragment getItem(int i) {
                return this.mFragments.get(i);
            }
    
            @Override
            public int getCount() {
                return this.mFragments.size();
            }
        }
    }
    




  • 相关阅读:
    Python 正则表达式简单了解
    scrapy 框架简单 爬取 4K高清 壁纸
    解决验证注解随机问题
    @ConfigurationProperties
    ConstraintValidator自定义注解
    通过日志解读Spring-Mybatis执行顺序
    Spring整合mybatis配置文件
    设计模式——代理模式
    Mybatis框架配置
    设计模式——单例模式
  • 原文地址:https://www.cnblogs.com/zhouzme/p/5758488.html
Copyright © 2020-2023  润新知