編輯:Android資訊
Android 程序中實現Tab類型界面很常見,本人在做項目的時候也經常用到,所以想在這裡總結一下,實現tab類型界面的幾種方式,供大家參考。如有不對之處,歡迎大家指正!
實現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();
}
});
目前最常見的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的事務管理功能。
// 三個選項卡
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對應一個布局,點擊不同的按鈕來切換頁面。效果如下圖:

如果想使用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。用這種方法我只在代碼裡有兩個判斷就可以完成了。
效果圖如下:

這種方式沒有上一種效果好看,而且標題變動。看一下效果圖:

布局文件:
<?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控件系列:Button(按鈕)
Button,就是按鈕,是android中應用最多的組件之一,Button有兩種用法,一種是XML中配置,另一種是在程序中直接使用 在XML布局文件裡,會遇到如下
Android 架構演化之路
大家好! 過了一好陣子了(在此期間我收到了大量的讀者反饋) 我決定是時候回到手機程序架構這個話題上了(這裡用android代碼舉例), 給大家另一個我認為好的解決
AndroidEventBus (事件總線) 的設計與實現
1. 功能介紹 AndroidEventBus是一個Android平台的事件總線庫, 它簡化了Activity、Fragment、Service等組件或者對象之間
Android TextView 預渲染詳解
Android中的TextView是整個framework中最復雜的控件之一,負責Android中顯示文本的大部分工作,framwork中 的許多控件也直接或者間