天天看點

Android UI 之 Tab類型界面總結

    轉載請注明出處:http://blog.csdn.net/crazy1235/article/details/42678877

    Android 程式中實作Tab類型界面很常見,本人在做項目的時候也經常用到,是以想在這裡總結一下,實作tab類型界面的幾種方式,供大家參考。如有不對之處,歡迎大家指正!

    一、TabActivity + TabWidget + TabHost.

    實作TAB類型界面,首先想到的就是這種方式。但是在API level 13之後官方就不建議使用它了。不過還是在這裡簡單說一下它的使用吧。

Android UI 之 Tab類型界面總結

    使用它的關鍵就是布局檔案了。需要在布局中添加<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)

    下面看一下這種方式的效果圖:

Android UI 之 Tab類型界面總結

主要的功能代碼如下:

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) {
			}
		});
	}
           
<span style="white-space:pre">	</span>//官方建議這麼寫
		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}
   <span style="white-space:pre">		</span>//傳回一共有多少個界面
		@Override
		public int getCount() {
			return list.size();
		}
<span style="white-space:pre">		</span>//執行個體化一個item
		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			container.addView(list.get(position));
			return list.get(position);
		}
 <span style="white-space:pre">		</span>//銷毀一個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對應一個布局,點選不同的按鈕來切換頁面。效果如下圖:

Android UI 之 Tab類型界面總結

四、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。用這種方法我隻在代碼裡有兩個判斷就可以完成了。

    效果圖如下:

Android UI 之 Tab類型界面總結

五、Viewpager + PagerTitleStrip / PagerTabStrip

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

Android UI 之 Tab類型界面總結

    布局檔案:

<?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);
		}
	};
           

總結:

    我目前所遇到的tab類型界面的實作方式就是這麼多了,若果大家有其他的實作方式,請留言。如果我寫的有誤,請留言指正,共同進步!謝謝!

    Demo下載下傳位址:http://download.csdn.net/detail/crazy1235/8358671

繼續閱讀