Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新
现在很多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研发村 )
Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新的更多相关文章
- uni-app h5端跳转到底部导航栏的时候使用方法uni.switchTab跳转刷新页面更新数据
h5端的uni-app项目 需求:uni-app h5端跳转到底部导航栏的时候使用方法uni.switchTab跳转刷新页面更新数据 百度的方法如下: uni.switchTab({ url: '/p ...
- [Android]--RadioGroup+RadioButton实现底部导航栏
RadioGroup+RadioButton组合方式打造简单实用的底部导航栏 代码块: <?xml version="1.0" encoding="utf-8&qu ...
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
原文链接 http://blog.csdn.net/way_ping_li/article/details/45727335 git diff diff --git a/frameworks/bas ...
- Android (争取做到)最全的底部导航栏实现方法
本文(争取做到)Android 最全的底部导航栏实现方法. 现在写了4个主要方法. 还有一些个人感觉不完全切题的方法也会简单介绍一下. 方法一. ViewPager + List<View> ...
- Android学习笔记- Fragment实例 底部导航栏的实现
1.要实现的效果图以及工程目录结构: 先看看效果图吧: 接着看看我们的工程的目录结构: 2.实现流程: Step 1:写下底部选项的一些资源文件 我们从图上可以看到,我们底部的每一项点击的时候都有不同 ...
- Android开源项目——带图标文字的底部导航栏IconTabPageIndicator
接下来的博客计划是,在<Android官方技术文档翻译>之间会发一些Android开源项目的介绍,直接剩下的几篇Android技术文档发完,然后就是Android开源项目和Gradle翻译 ...
- android底部导航栏实现
第一种用radiobutton实现 https://wizardforcel.gitbooks.io/w3school-android/content/75.html 布局文件,使用radiogrou ...
- Android应用底部导航栏(选项卡)实例
现在很多android的应用都采用底部导航栏的功能,这样可以使得用户在使用过程中随意切换不同的页面,现在我采用TabHost组件来自定义一个底部的导航栏的功能. 我们先看下该demo实例的框架图: 其 ...
- Android 修改底部导航栏navigationbar的颜色
Android 修改底部导航栏navigationbar的颜色 getWindow().setNavigationBarColor(Color.BLUE); //写法一 getWindow().set ...
随机推荐
- js创建svg元素的方法
需要JQuery <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- php数字转人民币金额大写
numToRmb.php <?php header("content-type:text/html;charset=utf-8"); function numToRmb($n ...
- 集训队日常训练20180518-DIV1
A.3583 n根木棍是否能分成相等两堆. 背包dp,首先求和sum,如果为偶数就说明不行,否则考虑做一个sum/2大小的背包. #include<bits/stdc++.h> using ...
- 有趣的HTML5 Web 存储
HTML5 web 存储,一个比cookie更好的本地存储方式. 什么是 HTML5 Web 存储? 使用HTML5可以在本地存储用户的浏览数据. 早些时候,本地存储使用的是 cookie.但是Web ...
- UE4物理模块(三)---碰撞查询(下)SAP/MBP/BVH算法简介
在上一文中介绍了碰撞查询的配置方法: Jerry:UE4物理模块(三)---碰撞查询(上)zhuanlan.zhihu.com 本篇介绍下UE4的各种零大小的射线检测,以及非零大小(带体积)的射线检 ...
- AndroidStudio离线打包MUI集成JPush极光推送并在java后端管理推送
1.AndroidStudio离线打包MUI 如何离线打包请参看上篇随笔<AndroidStudio离线打包MUI> 2.集成极光推送 官方文档:https://docs.jiguang. ...
- [运维]VMware vSphere介绍 标签: 运维 2017-04-21 19:48 532人阅读 评论(17)
大部分的程序员,应该是使用过vmware workstation的,我们用这款软件来创建虚拟机,满足我们学习或者工作的一些问题,今天介绍的是vmware家的另一款,不算是软件,比软件范围更大,VMwa ...
- Linux ar命令介绍 和常用示例
制作静态库要用到ar命令,命令格式: ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files... {dmpqrtx}中的 ...
- 三.BP神经网络
BP神经网络是包含多个隐含层的网络,具备处理线性不可分问题的能力.以往主要是没有适合多层神经网络的学习算法,,所以神经网络的研究一直处于低迷期. 20世纪80年代中期,Rumelhart,McClel ...
- 将centos 7 自带的 php 5.4升级为 5.6
1.进入终端后查看php版本 php -v 输出可能如下: PHP 5.4.35 (cli) (built: Nov 14 2014 07:04:10) Copyright (c) 1997-2014 ...