Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新
?現(xiàn)在很多APP,如微信、QQ、微博等等,它們的主頁面都無一例外的選擇使用底部Tab導(dǎo)航, 通過這種方式,可以很好的把頁面層級分化,很好的提高用戶體驗。相信,很多Android開發(fā)者,都使用到過這種經(jīng)典的設(shè)計,可是您你能保證您的設(shè)計真的沒問題么?
?為啥我會有這個疑問呢? 因為我日前就遇到了這么一個情況,發(fā)現(xiàn)我做的APP導(dǎo)航頁有問題。 具體可以參考這篇博客:【Android】保存Fragment切換狀態(tài)?, 首先說明的是,我的項目是從之前就沿用下來的框架,頁面底部tab的實現(xiàn),就是采用前面博客提到的方式, 可是在測試的時候,竟然發(fā)現(xiàn),使用這種方式來實現(xiàn),經(jīng)常會發(fā)生tab重疊情況: 比如,此刻選中的事“首頁”tab,可是內(nèi)容確實“活動”tab,尤其是在你的app在二級頁面發(fā)生崩潰返回到一級頁面時,這種情況經(jīng)常發(fā)生! 當然,這篇博客的評論里面,也提到了這個問題,所以,最后大家建議大家采用的是;"推薦直接使用ViewPager,通過自定義ViewPager禁用掉左右滑動和自動銷毀即可"
?
? 在我接觸的APP中,我覺得新浪微博的設(shè)計當然是最經(jīng)典的,為啥呢?就因為它多了一個功能,“底部tab的雙擊,來實現(xiàn)列表滾動到最上方并刷新博客列表”,要知道,這樣的設(shè)計,可以極大提高用戶體驗的(避免了用戶手動滾動到最上方,然后下拉刷新...),接下來,將帶著大家學(xué)習(xí)如何去實現(xiàn)吧。
先看效果圖(尤其是日志):
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.ContainerViewPagerandroid:id="@+id/viewpager"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginBottom="60dp" /><RadioGroupandroid:id="@+id/radiogroup"android:layout_width="fill_parent"android:layout_height="60dp"android:background="#ececec"android:layout_alignParentBottom="true"android:orientation="horizontal"><RadioButtonandroid:id="@+id/radio_main"style="@style/navigation_style"android:checked="true"android:drawableTop="@drawable/selector_main_bottom_tab_first"android:paddingLeft="0dp"android:text="首頁" /><RadioButtonandroid:id="@+id/radio_projects"style="@style/navigation_style"android:checked="false"android:drawableTop="@drawable/selector_main_bottom_tab_second"android:paddingLeft="0dp"android:text="活動" /><RadioButtonandroid:id="@+id/radio_studys"style="@style/navigation_style"android:checked="false"android:drawableTop="@drawable/selector_main_bottom_tab_third"android:paddingLeft="0dp"android:text="社區(qū)" /><RadioButtonandroid: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),要求可以實現(xiàn)Fragment切換,狀態(tài)的保存:
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之間的邏輯關(guān)系* 可保持Fragment切換狀態(tài)*/ public class FragmentViewPagerAdapter extends PagerAdapter implements MyViewPager.OnPageChangeListener {private List<Fragment> fragments; // 每個Fragment對應(yīng)一個Pageprivate 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);}@Overridepublic int getCount() {return fragments.size();}@Overridepublic boolean isViewFromObject(View view, Object o) {return view == o;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView(fragments.get(position).getView()); // 移出viewpager兩邊之外的page布局}@Overridepublic Object instantiateItem(ViewGroup container, int position) {Fragment fragment = fragments.get(position);if (!fragment.isAdded()) { // 如果fragment還沒有addedFragmentTransaction ft = fragmentManager.beginTransaction();ft.add(fragment, fragment.getClass().getSimpleName());ft.commit();/*** 在用FragmentTransaction.commit()方法提交FragmentTransaction對象后* 會在進程的主線程中,用異步的方式來執(zhí)行。* 如果想要立即執(zhí)行這個等待中的操作,就要調(diào)用這個方法(只能在主線程中調(diào)用)。* 要注意的是,所有的回調(diào)和相關(guān)的行為都會在這個調(diào)用中被執(zhí)行完成,因此要仔細確認這個方法的調(diào)用位置。*/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;}/*** 設(shè)置頁面切換額外功能監(jiān)聽器** @param onExtraPageChangeListener*/public void setOnExtraPageChangeListener(OnExtraPageChangeListener onExtraPageChangeListener) {this.onExtraPageChangeListener = onExtraPageChangeListener;}@Overridepublic void onPageScrolled(int i, float v, int i2) {if (null != onExtraPageChangeListener) { // 如果設(shè)置了額外功能接口onExtraPageChangeListener.onExtraPageScrolled(i, v, i2);}}@Overridepublic void onPageSelected(int i) {fragments.get(currentPageIndex).onPause(); // 調(diào)用切換前Fargment的onPause()if (fragments.get(i).isAdded()) {fragments.get(i).onResume(); // 調(diào)用切換后Fargment的onResume()}currentPageIndex = i;if (null != onExtraPageChangeListener) { // 如果設(shè)置了額外功能接口onExtraPageChangeListener.onExtraPageSelected(i);}}@Overridepublic void onPageScrollStateChanged(int i) {if (null != onExtraPageChangeListener) { // 如果設(shè)置了額外功能接口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左右滑動功能,大家可以再源碼里直接看到。
4. 實現(xiàn)MainActivity.java,為ViewPager設(shè)置PageAdapter, 并且,在MainActivity中實現(xiàn)雙擊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;@Overrideprotected 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() {@Overridepublic 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;}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic 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() {@Overridepublic void run() {try {Thread.sleep(500);firstClickTime = 0;} catch (InterruptedException e) {e.printStackTrace();}}}).start();} }
?注:在處首頁Tab的點擊事件時,除了要設(shè)置viewPager.setCurrentItem(TAB_HOME, false);外,還需要設(shè)置doubleClick(v);它主要是處理了雙擊事件。
? 通過以上四個步驟,已經(jīng)可以實現(xiàn)tab導(dǎo)航,雙擊tab調(diào)用Fragment中方法了。接下來,讓我們看下日志:仔細看下ViewPager+Fragment的生命周期:(我設(shè)置ViewPager取消了預(yù)加載功能)
1. 第一次進入到主頁面:加載FragmentMain,執(zhí)行生命周期方法
2. 分別點擊其他的Tab:
3. 之后再點擊FragmentMain,我們發(fā)現(xiàn),并未在執(zhí)行任何生命周期的方法;
4. 點擊FragmentMain頁面中的Button,進入新的Activity:
? 我們發(fā)現(xiàn),剛剛啟動的幾個Fragment(首頁、活動、社區(qū)),都執(zhí)行了onPause和onStop方法;
5. 返回到上一級頁面,也就是首頁:
??剛剛停止的幾個Fragment(首頁、活動、社區(qū)),執(zhí)行了onStart和onResume方法。
6. 接著,測試下首頁tab的雙擊事件:
? 會調(diào)用我們在FragmentMain中定義ScrollToTop方法,在該方法中,我們可以處理一些相應(yīng)的邏輯。
7. 退出APP,看下:
看到這里,不知道大家是否明白了如何定義使用Tab了,如果有疑問,可以再多看看源碼,也歡迎一起討論。
github源碼地址:https://github.com/zuiwuyuan/ViewpagerFragmentTab
? 如此這般,就OK啦!歡迎指正!
? 如有疑問,歡迎進QQ群:487786925( Android研發(fā)村 )
總結(jié)
以上是生活随笔為你收集整理的Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Makefile 使用总结
- 下一篇: Android-ConvenientBa