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.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="社區" /><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),要求可以實現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對應一個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對象后* 會在進程的主線程中,用異步的方式來執行。* 如果想要立即執行這個等待中的操作,就要調用這個方法(只能在主線程中調用)。* 要注意的是,所有的回調和相關的行為都會在這個調用中被執行完成,因此要仔細確認這個方法的調用位置。*/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;}@Overridepublic void onPageScrolled(int i, float v, int i2) {if (null != onExtraPageChangeListener) { // 如果設置了額外功能接口onExtraPageChangeListener.onExtraPageScrolled(i, v, i2);}}@Overridepublic 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);}}@Overridepublic 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左右滑動功能,大家可以再源碼里直接看到。
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;@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的點擊事件時,除了要設置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滚动、刷新的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Makefile 使用总结
- 下一篇: Jersey Restful Appli