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();
        }
    }
}
时间: 2024-09-16 19:40:13

Android之ActionBar、Tabs、Fragment、ViewPager实现标签页切换并缓存页面的相关文章

【Android开发】如何在tab选项卡标签页添加手势滑动ViewFlipper

问题描述 [Android开发]如何在tab选项卡标签页添加手势滑动ViewFlipper 如何在tab选项卡标签页添加手势滑动ViewFlipper,我在包中的一个main.java中写了添加了A的选项卡代码,在另一个A.java中加载了A的XML界面,然后我想在A中添加手势滑动翻页的功能,我尝试在A.java中添加ViewFlipper,但出现了这种情况部分代码如下:final int FLIP_DISTANCE = 50; ViewFlipper flipper; GestureDetec

最简单纯JavaScript实现Tab标签页切换的方式(推荐)_javascript技巧

先说一下最土的一种方法: Html: <div class="tab-head"> <h2 id="tab1" onmouseover="changeTab1()" class="selected">1</h2> <h2 id="tab2" onmouseover="changeTab2()">2</h2> <h2 id=

基于jQuery实现多标签页切换的效果(web前端开发)_jquery

这里,实现多标签页效果的方法有两个,一个是基于DOM的,另一个是基于jquery的,此次我写的是一个对于一个电话套餐的不同,显示不同的标签页 方法一: 首先,我们要把页面的大体框架和样式写出来,html和css代码如下: <ul id="tab"> <li id="tab1" onclick="show(1)">10元套餐</li> <li id="tab2" onclick=&quo

android fragment-问一个关于fragment+Viewpager加按钮跳转页面问题

问题描述 问一个关于fragment+Viewpager加按钮跳转页面问题 用fragment+viewpager写了个页面滑动, 然后我在首页想加一个按钮,然后实现点击跳转到另一个新页面,但是怎么跳都跳不过去我简单拉出来的页面,是不是Viewpager里不能实现加按钮跳转,能不能用什么东西代替它 解决方案 你要startActivity(it) 解决方案二: 你没有写跳转语句啊. 你在onClick(VIew v) 后面加一句: getActivity().startActivity(it);

标签页-如何打开新页面而不是打开新窗口?

问题描述 如何打开新页面而不是打开新窗口? 这种windon.open的方式是在新的窗口打开,我现在想在新标签页打开,该怎么写? 解决方案 用frame,设置它的src 解决方案二: 用window.localtion(). window.open()是打开新页面,也可以设置成打开页面

Vim技能修炼教程(9) - 缓冲区和标签页

缓冲区 上一节我们学习了窗口相关的命令,其实多窗口的最大功能在于存放多个不同的缓冲区. 文件载入内存之后,我们操作的其实是缓冲区.:write命令就是将缓冲区写回文件的目的. 查看内存中的缓冲区 通过:ls命令,可以查看当前存在的缓冲区 :ls 1 #a + "test.cpp" line 1 2 = "test.txt" line 7 8 %a "[No Name]" line 1 其中,第一列的编号是缓冲区的编号. "%"

请问怎么刷新标签页的内容?

问题描述 我想点击界面上的按钮,重新刷新下标签页的内容,请问怎么办呢?谢谢! 问题补充:7454103 写道 解决方案 我记得Android的Tab没有刷新吧.使用onRestart()函数重启当前的Activity能实现刷新!解决方案二:<meta http-equiv="refresh" content="60;url=<h:root/>/member/JWTTFComp.do">解决方案三:标签页 是指 iframe 页面? 还是 当期

全面解析标签页的切换方式_jquery

标签页的切换方式如下所示: 1.控制tab的显示与隐藏 2.tab不切换,数据加载 控制tab的显示与隐藏 前端脚本: 1.jquery实现: $(function(){ $(".sdkj-tabs li").click(function() { $(this).addClass("on").siblings().removeClass("on"); var index=$(".sdkj-tabs li").index(thi

如何使用Safari快速关闭所有标签页

  从名字我们就可以猜到这款插件的作用,它可以让用户一键清除目前打开的所有标签页.当你切换标签页的时候会发现,在屏幕底部出现了一个新的"x"按钮,按下这个按钮,所有的标签页便会关闭.在关闭标签页之前,插件还会弹出一条确认通知,以防用户误触. 需要注意的是,有时候可能会想保留某一个页面,这时候就可以用到插件的白名单功能.你只需打开标签页切换界面,然后在该标签页上双指长按,便可对其进行白名单处理.被白名单的标签页,将会在左上角显示一个锁定的按钮.即便你按了清除所有按钮,但该标签页依然会得以