Android UI 之 Tab型別介面總結
Android 程式中實現Tab型別介面很常見,本人在做專案的時候也經常用到,所以想在這裡總結一下,實現tab型別介面的幾種方式,供大家參考。如有不對之處,歡迎大家指正!
一、TabActivity + TabWidget + TabHost.
實現TAB型別介面,首先想到的就是這種方式。但是在API level 13之後官方就不建議使用它了。不過還是在這裡簡單說一下它的使用吧。
使用它的關鍵就是佈局檔案了。需要在佈局中新增<TabHost>、<TabWidget>、<FrameLayout>這三個控制元件,id分別是系統提供的:@android:id/tabhost 、@android:id/tabs 、@android:id/tabcontent 。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent" > <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <!-- 可以指定tabwidget的位置 android:layout_alignParentBottom="true" --> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="false" > </TabWidget> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@android:id/tabs" > <LinearLayout android:id="@+id/tab1" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#DEB887" android:orientation="vertical" > </LinearLayout> <LinearLayout android:id="@+id/tab2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#BCEE68" android:orientation="vertical" > </LinearLayout> <LinearLayout android:id="@+id/tab3" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#7D9EC0" android:orientation="vertical" > </LinearLayout> </FrameLayout> </RelativeLayout> </TabHost> </LinearLayout>
一個linearlayout對應一個tab頁面的佈局。
tabHost = getTabHost(); tabHost.addTab(tabHost .newTabSpec("111") .setIndicator("", getResources().getDrawable(R.drawable.wuyong)) .setContent(R.id.tab1)); tabHost.addTab(tabHost .newTabSpec("222") .setIndicator("", getResources().getDrawable(R.drawable.gongsunsheng)) .setContent(R.id.tab2)); tabHost.addTab(tabHost.newTabSpec("333") .setIndicator("", getResources().getDrawable(R.drawable.likui)) .setContent(R.id.tab3)); tabHost.setBackgroundColor(Color.argb(150, 22, 70, 150)); tabHost.setCurrentTab(0); tabHost.setOnTabChangedListener(new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { Toast.makeText(FourthActivity.this, tabId, Toast.LENGTH_SHORT) .show(); } });
二、ViewPager + PageAdapter
目前最常見的tab介面就是使用viewpager來實現了。
先來說一下viewpager的一般使用步驟:
1. 在佈局檔案中新增viewpager控制元件
2. 在程式碼中設定viewpager介面卡,該類繼承與pagerAdapter或它的子類。必須實現以下四個方法:
(1)getCount()
(2)instantiateItem()
(3)destroyItem()
(4)isViewFromObject()
3. 初始化viewpager控制元件,設定監聽器
4. 設定監聽事件(setOnPageChangeListener)
下面看一下這種方式的效果圖:
主要的功能程式碼如下:
private void init() { viewPager = (ViewPager) findViewById(R.id.first_vp); LayoutInflater inflater = LayoutInflater.from(this); View view1 = inflater.inflate(R.layout.first_layout1, null); View view2 = inflater.inflate(R.layout.first_layout2, null); View view3 = inflater.inflate(R.layout.first_layout3, null); list.add(view1); list.add(view2); list.add(view3); viewPager.setAdapter(pagerAdapter); viewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int arg0) { setDots(arg0); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); }
private PagerAdapter pagerAdapter = new PagerAdapter() { //官方建議這麼寫 @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } //返回一共有多少個介面 @Override public int getCount() { return list.size(); } //例項化一個item @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(list.get(position)); return list.get(position); } //銷燬一個item @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(list.get(position)); } };
介面卡中必須要實現以上的四個方法。
如果只有這幾個頁面,互動性肯定是不好的,所以需要新增“指示器”,用來標識當前的頁面是哪一個!我在這裡用點來實現。就像效果圖顯示的那樣。
/** * 初始化底部的點 */ private void initDots() { pointLayout = (LinearLayout) findViewById(R.id.point_layout); dots = new ImageView[list.size()]; for (int i = 0; i < list.size(); i++) { dots[i] = (ImageView) pointLayout.getChildAt(i); } currentIndex = 0; dots[currentIndex].setBackgroundResource(R.drawable.dian_down); } /** * 當滾動的時候更換點的背景圖 */ private void setDots(int position) { if (position < 0 || position > list.size() - 1 || currentIndex == position) { return; } dots[position].setBackgroundResource(R.drawable.dian_down); dots[currentIndex].setBackgroundResource(R.drawable.dian); currentIndex = position; }
重點就是頁面切換之後,點也要切換。這時候就用到了OnPageChangeListener中的onPageSelected(int arg0)這個方法了。
@Override public void onPageSelected(int arg0) { setDots(arg0); }
三、Fragment + FragmentManager
fragment相信大家在專案中肯定都用過。這個方法主要就是利用fragmentManager對fragment的事務管理功能。
// 三個選項卡 private LinearLayout tab1Layout, tab2Layout, tab3Layout; // 預設選中第一個tab private int index = 1; // fragment管理類 private FragmentManager fragmentManager; // 三個fragment private Fragment tab1Fragment, tab2Fragment, tab3Fragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); fragmentManager = getSupportFragmentManager(); init(); } /** * 初始化控制元件 */ private void init() { tab1Layout = (LinearLayout) findViewById(R.id.tab1_layout); tab2Layout = (LinearLayout) findViewById(R.id.tab2_layout); tab3Layout = (LinearLayout) findViewById(R.id.tab3_layout); tab1Layout.setOnClickListener(this); tab2Layout.setOnClickListener(this); tab3Layout.setOnClickListener(this); // setDefaultFragment(); } /** * 設定預設顯示的fragment */ private void setDefaultFragment() { FragmentTransaction transaction = fragmentManager.beginTransaction(); tab1Fragment = new Tab1Fragment(); transaction.replace(R.id.content_layout, tab1Fragment); transaction.commit(); } /** *切換fragment * @param newFragment */ private void replaceFragment(Fragment newFragment) { FragmentTransaction transaction = fragmentManager.beginTransaction(); if (!newFragment.isAdded()) { transaction.replace(R.id.content_layout, newFragment); transaction.commit(); } else { transaction.show(newFragment); } } /** * 改變現象卡的選中狀態 */ private void clearStatus() { if (index == 1) { tab1Layout.setBackgroundColor(getResources().getColor(R.color.tab)); } else if (index == 2) { tab2Layout.setBackgroundColor(getResources().getColor(R.color.tab)); } else if (index == 3) { tab3Layout.setBackgroundColor(getResources().getColor(R.color.tab)); } } @Override public void onClick(View v) { clearStatus(); switch (v.getId()) { case R.id.tab1_layout: if (tab1Fragment == null) { tab1Fragment = new Tab1Fragment(); } replaceFragment(tab1Fragment); tab1Layout.setBackgroundColor(getResources().getColor( R.color.tab_down)); index = 1; break; case R.id.tab2_layout: if (tab2Fragment == null) { tab2Fragment = new Tab2Fragment(); } replaceFragment(tab2Fragment); tab2Layout.setBackgroundColor(getResources().getColor( R.color.tab_down)); index = 2; break; case R.id.tab3_layout: if (tab3Fragment == null) { tab3Fragment = new Tab3Fragment(); } replaceFragment(tab3Fragment); tab3Layout.setBackgroundColor(getResources().getColor( R.color.tab_down)); index = 3; break; } }
每一個fragment對應一個佈局,點選不同的按鈕來切換頁面。效果如下圖:
四、ViewPager + Fragment + FragmentPagerAdapter
如果想使用fragment的時候又想可以左右滑動,就可以使用這種方式。主要的部分就在viewpager的介面卡。它的介面卡繼承FragmentPagerAdapter.
package com.tab.view.demo3; import java.util.ArrayList; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; public class FragmentAdapter extends FragmentPagerAdapter { private ArrayList<Fragment> list; public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> list) { super(fm); this.list = list; } @Override public Fragment getItem(int arg0) { return list.get(arg0); } @Override public int getCount() { return list.size(); } }
需要傳入FragmentManager物件和一個存放fragment的list物件。
/** * 初始化viewpager */ private void initViewPager() { viewPager = (ViewPager) findViewById(R.id.third_vp); fragmentsList = new ArrayList<>(); Fragment fragment = new Tab1Fragment(); fragmentsList.add(fragment); fragment = new Tab2Fragment(); fragmentsList.add(fragment); fragment = new Tab3Fragment(); fragmentsList.add(fragment); viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentsList)); viewPager.setCurrentItem(0); viewPager.setOnPageChangeListener(this); }
對button新增點選事件。
@Override public void onClick(View v) { switch (v.getId()) { case R.id.tab1_tv: viewPager.setCurrentItem(0); break; case R.id.tab2_tv: viewPager.setCurrentItem(1); break; case R.id.tab3_tv: viewPager.setCurrentItem(2); break; } }
我在佈局檔案中新增了一個imageview作為指示器。如果想第一種tab型別介面的實現方式那樣在onPageSelected()方法中進行設定,效果是隻能當頁面完全切換過來之後才能把指示器移動過去。要想實現滑動頁面的時候同時移動指示器,就需要在onPageScrolled()方法中進行設定。
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { offset = (screen1_3 - cursorImg.getLayoutParams().width) / 2; Log.d("111", position + "--" + positionOffset + "--" + positionOffsetPixels); final float scale = getResources().getDisplayMetrics().density; if (position == 0) {// 0<->1 lp.leftMargin = (int) (positionOffsetPixels / 3) + offset; } else if (position == 1) {// 1<->2 lp.leftMargin = (int) (positionOffsetPixels / 3) + screen1_3 +offset; } cursorImg.setLayoutParams(lp); currentIndex = position; }
onPageScrolled中的三個引數比較重要。第一個引數是position。它的含義是表示當前顯示的介面中的第一個介面。意思就是的當滑動的時候,有可能出現兩個介面,position指的是左邊的介面。第二個引數是positionOffset指的是偏移量的比例,取值範圍是[0, 1)。第三個引數是positionOffsetPixels是指偏移的畫素值。後兩個引數都相對頁面(一個page)來說的。
我之前有看到過設定指示器的時候用的前兩個引數的,我也試了一下,OK的。不過感覺比較複雜,看了一下官方api,用第三個引數更簡單。關鍵就是理解第一個引數position。用這種方法我只在程式碼裡有兩個判斷就可以完成了。
效果圖如下:
五、Viewpager + PagerTitleStrip / PagerTabStrip
這種方式沒有上一種效果好看,而且標題變動。看一下效果圖:
佈局檔案:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <android.support.v4.view.ViewPager android:id="@+id/fifth_vp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" > <android.support.v4.view.PagerTabStrip android:id="@+id/fifth_strip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top" android:background="#7EC0EE" android:padding="10dip" /> </android.support.v4.view.ViewPager> </LinearLayout>
先來說一下PagerTitleStrip和PagerTabStrip的區別:PagerTitleStrip沒有指示器,只有標題,且標題沒有響應事件;而PagerTabStrip是帶有指示器的,當然也有標題,具有相應事件。二者的實現只在佈局檔案中有區別,只需要把android.support.v4.view.PagerTabStrip改成android.support.v4.viewPagerTitleStrip即可。
程式碼中需要注意的就是,在介面卡中重寫getPageTitle(int)方法來顯示標題。
PagerAdapter pagerAdapter = new PagerAdapter() { //此處省略其他的方法 // 重寫此方法即可顯示標題 @Override public CharSequence getPageTitle(int position) { return titleList.get(position); }
相關文章
- Android UI美化之 shape的使用及其屬性總結AndroidUI
- JavaScript基本型別總結JavaScript型別
- Android之SpannableString、SpannableStringBuilder總結AndroidUI
- OOM異常型別總結OOM型別
- [python] Python型別提示總結Python型別
- JS 資料型別總結JS資料型別
- Python的資料型別總結Python資料型別
- 後臺面試型別總結面試型別
- Go 的資料型別總結Go資料型別
- 常見線纜介面型別總結型別
- 我要做Android之要點總結Android
- 【spring原始碼系列】之【FactoryBean型別的介面】Spring原始碼Bean型別
- JavaScript變數型別檢測總結JavaScript變數型別
- Mysql支援的資料型別(總結)MySql資料型別
- Python str型別學習總結(一)Python型別
- Flume 總結(三)sources型別-1.9.0新版型別
- go 基礎總結 --- 資料型別Go資料型別
- JavaScript資料型別轉換總結JavaScript資料型別
- PHP弱型別安全問題總結PHP型別
- UI介面微信底部(ViewPager實現Tab,左右滑動+底部點選)UIViewpager
- Android通用UI元件之DialogAndroidUI元件
- JS基礎總結(1)——資料型別JS資料型別
- Redis中 HyperLogLog資料型別使用總結Redis資料型別
- 可變資料型別(mutable)與不可變資料型別(immutable)總結資料型別
- 演算法型別大總結(並附經典題型)演算法型別
- android webview總結AndroidWebView
- Kendo UI Grid 使用總結UI
- golang 中的四種型別轉換總結Golang型別
- Android UI 測試指南之 EspressoAndroidUIEspresso
- TypeScript 混合型別介面TypeScript型別
- Android總結篇系列:Android ServiceAndroid
- 泛型總結泛型
- Python3學習(基本資料型別-集合-字典-基本資料型別總結)Python資料型別
- Android UI 顯示原理分析小結AndroidUI
- 【乾貨】驗證碼的常見型別總結型別
- C++的資料型別總結,不能錯過C++資料型別
- c# 操作Redis的五種基本型別總結C#Redis型別
- Enum列舉型別實戰總結,保證有用!型別
- SpringMVC-方法四種型別返回值總結SpringMVC型別