Android 程序中實(shí)現(xiàn)Tab類(lèi)型界面很常見(jiàn),所以想在這里總結(jié)一下,實(shí)現(xiàn)tab類(lèi)型界面的幾種方式,供大家參考。
一、TabActivity + TabWidget + TabHost.
實(shí)現(xiàn)TAB類(lèi)型界面,首先想到的就是這種方式。但是在API level 13之后官方就不建議使用它了。
二、ViewPager + PageAdapter
目前最常見(jiàn)的tab界面就是使用viewpager來(lái)實(shí)現(xiàn)了。
先來(lái)說(shuō)一下viewpager的一般使用步驟:
在布局文件中添加viewpager控件
在代碼中設(shè)置viewpager適配器,該類(lèi)繼承與pagerAdapter或它的子類(lèi)。必須實(shí)現(xiàn)以下四個(gè)方法:
(1)getCount()
(2)instantiateItem()
(3)destroyItem()
(4)isViewFromObject()初始化viewpager控件,設(shè)置監(jiān)聽(tīng)器
-
設(shè)置監(jiān)聽(tīng)事件(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() { //官方建議這么寫(xiě) @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } //返回一共有多少個(gè)界面 @Override public int getCount() { return list.size(); } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(list.get(position)); return list.get(position); } //銷(xiāo)毀一個(gè)item @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(list.get(position)); } };
適配器中必須要實(shí)現(xiàn)以上的四個(gè)方法。
如果只有這幾個(gè)頁(yè)面,交互性肯定是不好的,所以需要添加“指示器”,用來(lái)標(biāo)識(shí)當(dāng)前的頁(yè)面是哪一個(gè)!我在這里用點(diǎn)來(lái)實(shí)現(xiàn)。就像效果圖顯示的那樣。
/**
* 初始化底部的點(diǎn)
*/
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);
}
/**
* 當(dāng)滾動(dòng)的時(shí)候更換點(diǎn)的背景圖
*/
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;
}
重點(diǎn)就是頁(yè)面切換之后,點(diǎn)也要切換。這時(shí)候就用到了OnPageChangeListener中的onPageSelected(int arg0)這個(gè)方法了。
三、Fragment + FragmentManager
fragment相信大家在項(xiàng)目中肯定都用過(guò)。這個(gè)方法主要就是利用fragmentManager對(duì)fragment的事務(wù)管理功能。
// 三個(gè)選項(xiàng)卡
private LinearLayout tab1Layout, tab2Layout, tab3Layout;
// 默認(rèn)選中第一個(gè)tab
private int index = 1;
// fragment管理類(lèi)
private FragmentManager fragmentManager;
// 三個(gè)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();
}
/**
* 設(shè)置默認(rèn)顯示的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);
}
}
/**
* 改變現(xiàn)象卡的選中狀態(tài)
*/
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;
}
}
每一個(gè)fragment對(duì)應(yīng)一個(gè)布局,點(diǎn)擊不同的按鈕來(lái)切換頁(yè)面。效果如下圖:
四、ViewPager + Fragment + FragmentPagerAdapter
如果想使用fragment的時(shí)候又想可以左右滑動(dòng),就可以使用這種方式。主要的部分就在viewpager的適配器。它的適配器繼承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對(duì)象和一個(gè)存放fragment的list對(duì)象。
/**
* 初始化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);
}
對(duì)button添加點(diǎn)擊事件。
@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;
}
}
我在布局文件中添加了一個(gè)imageview作為指示器。如果想第一種tab類(lèi)型界面的實(shí)現(xiàn)方式那樣在onPageSelected()方法中進(jìn)行設(shè)置,效果是只能當(dāng)頁(yè)面完全切換過(guò)來(lái)之后才能把指示器移動(dòng)過(guò)去。要想實(shí)現(xiàn)滑動(dòng)頁(yè)面的時(shí)候同時(shí)移動(dòng)指示器,就需要在onPageScrolled()方法中進(jìn)行設(shè)置。
@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中的三個(gè)參數(shù)比較重要。第一個(gè)參數(shù)是position。它的含義是表示當(dāng)前顯示的界面中的第一個(gè)界面。意思就是的當(dāng)滑動(dòng)的時(shí)候,有可能出現(xiàn)兩個(gè)界面,position指的是左邊的界面。第二個(gè)參數(shù)是positionOffset指的是偏移量的比例,取值范圍是[0, 1)。第三個(gè)參數(shù)是positionOffsetPixels是指偏移的像素值。后兩個(gè)參數(shù)都相對(duì)頁(yè)面(一個(gè)page)來(lái)說(shuō)的。
我之前有看到過(guò)設(shè)置指示器的時(shí)候用的前兩個(gè)參數(shù)的,我也試了一下,OK的。不過(guò)感覺(jué)比較復(fù)雜,看了一下官方api,用第三個(gè)參數(shù)更簡(jiǎn)單。關(guān)鍵就是理解第一個(gè)參數(shù)position。用這種方法我只在代碼里有兩個(gè)判斷就可以完成了。
效果圖如下:
五、Viewpager + PagerTitleStrip / PagerTabStrip
這種方式?jīng)]有上一種效果好看,而且標(biāo)題變動(dòng)。不在詳細(xì)介紹。
六、Viewpager + TabLayout
TabLayout 是官方的,最好的是它可以兼容到2.2以上版本,包括2.2。很簡(jiǎn)單。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_FindFragment_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/titleBlue"
app:tabIndicatorColor="@color/white"
app:tabSelectedTextColor="@color/gray"
app:tabTextColor="@color/white"
/>
<android.support.v4.view.ViewPager
android:id="@+id/vp_FindFragment_pager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
這里面沒(méi)有什么特別的,就是添加了一個(gè)TabLayout和Viewpager作為上下的布局。其中
app:tabIndicatorColor="@color/white" // 下方滾動(dòng)的下劃線顏色
app:tabSelectedTextColor="@color/gray" // tab被選中后,文字的顏色
app:tabTextColor="@color/white" // tab默認(rèn)的文字顏色
因?yàn)檫@里面我每個(gè)欄目下,都會(huì)有一些列表,所以采用list<View>的方式,在里面切換layout不太適合,所以我采用了List<Fragment>來(lái)直接加載多個(gè)fragment
public class TabAdapter extends FragmentPagerAdapter {
private List<Fragment> list_fragment; //fragment列表
private List<String> list_Title; //tab名的列表
public TabAdapter(FragmentManager fm,List<Fragment> list_fragment,List<String> list_Title) {
super(fm);
this.list_fragment = list_fragment;
this.list_Title = list_Title;
}
@Override
public Fragment getItem(int position) {
return list_fragment.get(position);
}
@Override
public int getCount() {
return list_Title.size();
}
//此方法用來(lái)顯示tab上的名字
@Override
public CharSequence getPageTitle(int position) {
return list_Title.get(position % list_Title.size());
}
}
創(chuàng)建Fragment
public class PageFragment extends Fragment {
public static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
public static PageFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
PageFragment pageFragment = new PageFragment();
pageFragment.setArguments(args);
return pageFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments().getInt(ARG_PAGE);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_page, container, false);
TextView textView = (TextView) view;
textView.setText("Fragment #" + mPage);
return view;
}
}
使用
public class TabFragment extends Fragment {
private TabLayout tab_FindFragment_title; //定義TabLayout
private ViewPager vp_FindFragment_pager; //定義viewPager
private FragmentPagerAdapter fAdapter; //定義adapter
private List<Fragment> list_fragment; //定義要裝fragment的列表
private List<String> list_title; //tab名稱(chēng)列表
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_find, container, false);
initControls(view);
return view;
}
/**
* 初始化各控件
* @param view
*/
private void initControls(View view) {
tab_FindFragment_title = (TabLayout)view.findViewById(R.id.tab_FindFragment_title);
vp_FindFragment_pager = (ViewPager)view.findViewById(R.id.vp_FindFragment_pager);
//將fragment裝進(jìn)列表中
list_fragment = new ArrayList<>();
list_fragment.add(new PageFragment.newInstance(1));
list_fragment.add(new PageFragment.newInstance(2));
list_fragment.add(new PageFragment.newInstance(3));
//將名稱(chēng)加載tab名字列表,正常情況下,我們應(yīng)該在values/arrays.xml中進(jìn)行定義然后調(diào)用
list_title = new ArrayList<>();
list_title.add("tab1");
list_title.add("tab2");
list_title.add("tab3");
//設(shè)置TabLayout的模式
tab_FindFragment_title.setTabMode(TabLayout.MODE_FIXED);
//為T(mén)abLayout添加tab名稱(chēng)
tab_FindFragment_title.addTab(tab_FindFragment_title.newTab().setText(list_title.get(0)));
tab_FindFragment_title.addTab(tab_FindFragment_title.newTab().setText(list_title.get(1)));
tab_FindFragment_title.addTab(tab_FindFragment_title.newTab().setText(list_title.get(2)));
fAdapter = new TabAdapter(getActivity().getSupportFragmentManager(),list_fragment,list_title);
//viewpager加載adapter
vp_FindFragment_pager.setAdapter(fAdapter);
//tab_FindFragment_title.setViewPager(vp_FindFragment_pager);
//TabLayout加載viewpager
tab_FindFragment_title.setupWithViewPager(vp_FindFragment_pager);
//tab_FindFragment_title.set
}
}
七、TabPageIndicator/PagerSlidingTabStrip+ViewPager+FragmentPagerAdapter
TabPageIndicator 和 PagerSlidingTabStrip很像,不過(guò)有點(diǎn)過(guò)時(shí)了,不在詳細(xì)介紹。
八、FlycoTabLayout
這個(gè)是我見(jiàn)過(guò)的最好的,大家可以點(diǎn)擊看一下, 不在詳細(xì)介紹。