现在很多APP,如微信、QQ、微博等等,它们的主页面都无一例外的选择使用底部Tab导航, 通过这种方式,可以很好的把页面层级分化,很好的提高用户体验。相信,很多Android开发者,都使用到过这种经典的设计,可是您你能保证您的设计真的没问题么?

为啥我会有这个疑问呢? 因为我日前就遇到了这么一个情况,发现我做的APP导航页有问题。 具体可以参考这篇博客:【Android】保存Fragment切换状态, 首先说明的是,我的项目是从之前就沿用下来的框架,页面底部tab的实现,就是采用前面博客提到的方式, 可是在测试的时候,竟然发现,使用这种方式来实现,经常会发生tab重叠情况: 比如,此刻选中的事“首页”tab,可是内容确实“活动”tab,尤其是在你的app在二级页面发生崩溃返回到一级页面时,这种情况经常发生! 当然,这篇博客的评论里面,也提到了这个问题,所以,最后大家建议大家采用的是;"推荐直接使用ViewPager,通过自定义ViewPager禁用掉左右滑动和自动销毁即可"

在我接触的APP中,我觉得新浪微博的设计当然是最经典的,为啥呢?就因为它多了一个功能,“底部tab的双击,来实现列表滚动到最上方并刷新博客列表”,要知道,这样的设计,可以极大提高用户体验的(避免了用户手动滚动到最上方,然后下拉刷新...),接下来,将带着大家学习如何去实现吧。


先看效果图(尤其是日志):


1. 直接定义tab页面,一个ViewPager,四个RadioButton:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.lnyp.vf.ContainerViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_marginBottom="60dp" />    <RadioGroup        android:id="@+id/radiogroup"        android:layout_width="fill_parent"        android:layout_height="60dp"        android:background="#ececec"        android:layout_alignParentBottom="true"        android:orientation="horizontal">        <RadioButton            android:id="@+id/radio_main"            style="@style/navigation_style"            android:checked="true"            android:drawableTop="@drawable/selector_main_bottom_tab_first"            android:paddingLeft="0dp"            android:text="首页" />        <RadioButton            android:id="@+id/radio_projects"            style="@style/navigation_style"            android:checked="false"            android:drawableTop="@drawable/selector_main_bottom_tab_second"            android:paddingLeft="0dp"            android:text="活动" />        <RadioButton            android:id="@+id/radio_studys"            style="@style/navigation_style"            android:checked="false"            android:drawableTop="@drawable/selector_main_bottom_tab_third"            android:paddingLeft="0dp"            android:text="社区" />        <RadioButton            android:id="@+id/radio_user_center"            style="@style/navigation_style"            android:checked="false"            android:drawableTop="@drawable/selector_main_bottom_tab_forth"            android:paddingLeft="0dp"            android:text="我的" />    </RadioGroup></RelativeLayout>

2. 自定义PagerAdapter,为ViewPager添加布局(Fragment),要求可以实现Fragment切换,状态的保存:

import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.support.v4.view.PagerAdapter;import android.view.View;import android.view.ViewGroup;import java.util.List;/** * 为ViewPager添加布局(Fragment),绑定和处理fragments和viewpager之间的逻辑关系 * 可保持Fragment切换状态 */public class FragmentViewPagerAdapter extends PagerAdapter implements MyViewPager.OnPageChangeListener {    private List<Fragment> fragments; // 每个Fragment对应一个Page    private FragmentManager fragmentManager;    private ContainerViewPager viewPager; // viewPager对象    private int currentPageIndex = 0; // 当前page索引(切换之前)    private OnExtraPageChangeListener onExtraPageChangeListener; // ViewPager切换页面时的额外功能添加接口    public FragmentViewPagerAdapter(FragmentManager fragmentManager, ContainerViewPager viewPager, List<Fragment> fragments) {        this.fragments = fragments;        this.fragmentManager = fragmentManager;        this.viewPager = viewPager;        this.viewPager.setAdapter(this);        this.viewPager.setOnPageChangeListener(this);    }    @Override    public int getCount() {        return fragments.size();    }    @Override    public boolean isViewFromObject(View view, Object o) {        return view == o;    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {        container.removeView(fragments.get(position).getView()); // 移出viewpager两边之外的page布局    }    @Override    public Object instantiateItem(ViewGroup container, int position) {        Fragment fragment = fragments.get(position);        if (!fragment.isAdded()) { // 如果fragment还没有added            FragmentTransaction ft = fragmentManager.beginTransaction();            ft.add(fragment, fragment.getClass().getSimpleName());            ft.commit();            /**             * 在用FragmentTransaction.commit()方法提交FragmentTransaction对象后             * 会在进程的主线程中,用异步的方式来执行。             * 如果想要立即执行这个等待中的操作,就要调用这个方法(只能在主线程中调用)。             * 要注意的是,所有的回调和相关的行为都会在这个调用中被执行完成,因此要仔细确认这个方法的调用位置。             */            fragmentManager.executePendingTransactions();        }        if (fragment.getView().getParent() == null) {            container.addView(fragment.getView()); // 为viewpager增加布局        }        return fragment.getView();    }    /**     * 当前page索引(切换之前)     *     * @return     */    public int getCurrentPageIndex() {        return currentPageIndex;    }    public OnExtraPageChangeListener getOnExtraPageChangeListener() {        return onExtraPageChangeListener;    }    /**     * 设置页面切换额外功能监听器     *     * @param onExtraPageChangeListener     */    public void setOnExtraPageChangeListener(OnExtraPageChangeListener onExtraPageChangeListener) {        this.onExtraPageChangeListener = onExtraPageChangeListener;    }    @Override    public void onPageScrolled(int i, float v, int i2) {        if (null != onExtraPageChangeListener) { // 如果设置了额外功能接口            onExtraPageChangeListener.onExtraPageScrolled(i, v, i2);        }    }    @Override    public void onPageSelected(int i) {        fragments.get(currentPageIndex).onPause(); // 调用切换前Fargment的onPause()        if (fragments.get(i).isAdded()) {            fragments.get(i).onResume(); // 调用切换后Fargment的onResume()        }        currentPageIndex = i;        if (null != onExtraPageChangeListener) { // 如果设置了额外功能接口            onExtraPageChangeListener.onExtraPageSelected(i);        }    }    @Override    public void onPageScrollStateChanged(int i) {        if (null != onExtraPageChangeListener) { // 如果设置了额外功能接口            onExtraPageChangeListener.onExtraPageScrollStateChanged(i);        }    }    /**     * page切换额外功能接口     */    public static class OnExtraPageChangeListener {        public void onExtraPageScrolled(int i, float v, int i2) {        }        public void onExtraPageSelected(int i) {        }        public void onExtraPageScrollStateChanged(int i) {        }    }}


注: 上述代码中,有个ContainerViewPager,该ContainerViewPager是继承ViewPager,主要是为了去除ViewPager左右滑动功能,大家可以再源码里直接看到。


3. 自定义四个(随便几个)Fragment, 每个Fragment就是ViewPager里要承载的View,负责显示每个Tab页面


4. 实现MainActivity.java,为ViewPager设置PageAdapter, 并且,在MainActivity中实现双击tab功能:

import android.os.Bundle;import android.os.SystemClock;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.view.View;import android.widget.RadioButton;import java.util.ArrayList;import java.util.List;import butterknife.Bind;import butterknife.ButterKnife;import butterknife.OnClick;public class MainActivity extends FragmentActivity {    public static final int TAB_HOME = 0;    public static final int TAB_PROJECTS = 1;    public static final int TAB_STUDYS = 2;    public static final int TAB_USER_CENTER = 3;    @Bind(R.id.viewpager)    public ContainerViewPager viewPager;    @Bind(R.id.radio_main)    public RadioButton radioMain;    @Bind(R.id.radio_projects)    public RadioButton radioProjects;    @Bind(R.id.radio_studys)    public RadioButton radioStudys;    @Bind(R.id.radio_user_center)    public RadioButton radioUserCenter;    FragmentMain fragmentMain;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        initView();        addPageChangeListener();    }    private void initView() {        List<Fragment> fragments = new ArrayList<Fragment>();        fragmentMain = new FragmentMain();        FragmentHuodong fragmentHuodong = new FragmentHuodong();        FragmentShequ fragmentShequ = new FragmentShequ();        FragmentMy fragmentMy = new FragmentMy();        fragments.add(fragmentMain);        fragments.add(fragmentHuodong);        fragments.add(fragmentShequ);        fragments.add(fragmentMy);        this.viewPager.setOffscreenPageLimit(0);        FragmentViewPagerAdapter adapter = new FragmentViewPagerAdapter(this.getSupportFragmentManager(), viewPager, fragments);    }    private void addPageChangeListener() {        viewPager.setOnPageChangeListener(new MyViewPager.OnPageChangeListener() {            @Override            public void onPageSelected(int id) {                switch (id) {                    case TAB_HOME:                        radioMain.setChecked(true);                        break;                    case TAB_PROJECTS:                        radioProjects.setChecked(true);                        break;                    case TAB_STUDYS:                        radioStudys.setChecked(true);                        break;                    case TAB_USER_CENTER:                        radioUserCenter.setChecked(true);                        break;                }            }            @Override            public void onPageScrolled(int arg0, float arg1, int arg2) {            }            @Override            public void onPageScrollStateChanged(int arg0) {            }        });    }    @OnClick({R.id.radio_main, R.id.radio_projects, R.id.radio_studys, R.id.radio_user_center})    public void onClick(View v) {        switch (v.getId()) {            case R.id.radio_main:                viewPager.setCurrentItem(TAB_HOME, false);                doubleClick(v);                break;            case R.id.radio_projects:                viewPager.setCurrentItem(TAB_PROJECTS, false);                break;            case R.id.radio_studys:                viewPager.setCurrentItem(TAB_STUDYS, false);                break;            case R.id.radio_user_center:                viewPager.setCurrentItem(TAB_USER_CENTER, false);                break;        }    }    long firstClickTime = 0;    long secondClickTime = 0;    public void doubleClick(View view) {        if (firstClickTime > 0) {            secondClickTime = SystemClock.uptimeMillis();            if (secondClickTime - firstClickTime < 500) {                fragmentMain.ScrollToTop();            }            firstClickTime = 0;            return;        }        firstClickTime = SystemClock.uptimeMillis();        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(500);                    firstClickTime = 0;                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}

注:在处首页Tab的点击事件时,除了要设置viewPager.setCurrentItem(TAB_HOME, false);外,还需要设置doubleClick(v);它主要是处理了双击事件。


通过以上四个步骤,已经可以实现tab导航,双击tab调用Fragment中方法了。接下来,让我们看下日志:仔细看下ViewPager+Fragment的生命周期:(我设置ViewPager取消了预加载功能)

1. 第一次进入到主页面:加载FragmentMain,执行生命周期方法



2. 分别点击其他的Tab:



3. 之后再点击FragmentMain,我们发现,并未在执行任何生命周期的方法;

4. 点击FragmentMain页面中的Button,进入新的Activity:


我们发现,刚刚启动的几个Fragment(首页、活动、社区),都执行了onPause和onStop方法;


5. 返回到上一级页面,也就是首页:


刚刚停止的几个Fragment(首页、活动、社区),执行了onStart和onResume方法。


6. 接着,测试下首页tab的双击事件:


会调用我们在FragmentMain中定义ScrollToTop方法,在该方法中,我们可以处理一些相应的逻辑。


7. 退出APP,看下:




看到这里,不知道大家是否明白了如何定义使用Tab了,如果有疑问,可以再多看看源码,也欢迎一起讨论。


github源码地址:https://github.com/zuiwuyuan/ViewpagerFragmentTab


如此这般,就OK啦!欢迎指正!
如有疑问,欢迎进QQ群:487786925( Android研发村 )

更多相关文章

  1. eBook 功能模块二之设置模块Android
  2. 人工智能交互集成在线语音合成能力的Tips
  3. Android(安卓)开发之集成百度地图的定位与地图展示
  4. (详细代码,文末Demo下载)android简单修改密码、登录、注册功能 基于
  5. android OnPageChangeListener
  6. android调用输入软键盘回车键跟删除键
  7. 科普篇
  8. android 利用重力感应监测手机摇晃,做出相应反应
  9. Android(安卓)4.4 KitKat 更新内容

随机推荐

  1. android布局tips,基础知识搜集
  2. android shape的使用
  3. android HAL介绍
  4. Android中的坐标系以及获取坐标的方法
  5. Android(安卓)各种实现Tab效果的实现方式
  6. Android(安卓)内核简单分析
  7. Android(安卓)开发笔记 —— AndroidStud
  8. Android层次化安全架构及核心组件概览
  9. 应用界面主题Theme使用方法
  10. Android系统移植与调试之------->如何修