原文地址:http://www.cnblogs.com/kross/p/3372987.htmlhtml
咱們實現一個上面是一個能夠左右滑動的頁面,下面是三個可點擊切換的tab按鈕,tab按鈕上還有一個激活條。效果以下圖所示:java
----------------我是分割線--------------------android
--------------------------我是分割線,下面的圖片表示往右滑動,白條中的小機器人往右滑動-------------------------數組
--------------------------我是分割線,切換到第二個頁面,tab中的第二個被激活,字呈白色-----------------------框架
------------------我是分割線-------------------dom
OK,效果就是如上所示的效果。如今開始一步一步的介紹如何實現。ide
1.建立好基礎的佈局文件。函數
整個頁面是一個LinearLayout,若是你用其餘的佈局也是能夠的,在下只是圖一個方便了。佈局
根據最終效果,咱們將整個佈局分爲三個部分:post
第一部分是上面那一大塊有顏色的區域,這是一個ViewPager,裏面的內容是經過Fragment加載進來的。
第二部分是那個在白條中間的小機器人這一塊,這個做爲激活條(本身編的名詞,就是表名當前的tab是被激活的),就是一個ImageView。
第三部分是最底下的三個tab,是一個水平的LinearLayout,裏面每個tab都是ImageView+TextView構成的。
注意這裏tab這個佈局是被重複使用的,由於有三個嘛,爲了代碼重用,咱們可使用<include>標籤。
因此,咱們先寫好每一個小tab的佈局(/res/layout/tab.xml),代碼以下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/tab_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_launcher"/> <TextView android:id="@+id/tab_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/tab_text" android:gravity="center" android:textColor="#cccccc"/> </LinearLayout>
爲了方便,就直接使用啓動圖標的圖片了,啊哈哈……啊哈哈哈……
接下來咱們去寫主體頁面的佈局(/res/layout/activity_main.xml),代碼以下:
注意<include>標籤的使用哦~
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <!-- ViewPager的使用必須是完整的名字哦 --> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1"/> <!-- 這是所謂的激活條,爲了方便,也直接使用啓動圖標的圖片了 --> <ImageView android:id="@+id/active_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="matrix" android:src="@drawable/ic_launcher" android:background="#ffffff"/> <!-- 下面的線性佈局是三個tab放置的區域 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- 使用include標籤,能夠直接重用xml,這是我在《Android UI 基礎教程》中發現的~\(^o^)/~ --> <include android:id="@+id/tab1" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" layout="@layout/tab"/> <include android:id="@+id/tab2" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content" layout="@layout/tab"/> <include android:id="@+id/tab3" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content" layout="@layout/tab"/> </LinearLayout> </LinearLayout>
OK,這樣咱們整個頁面的框架就有了。
2.給ViewPager添加頁面
OKOK,在這一步呢,咱們要作的是給ViewPager這個控件添加頁面,咱們從一開始就打算添加三個頁面的,因此上面纔有3個tab,若是你須要更多,都是同樣的道理。
ViewPager這個控件使用起來是很是方便的,在給它添加好頁面後,它自身能夠實現左右滑動切換的效果,很是滴好用啊。
ViewPager最重要的是給它設置適配器(setAdapter),咱們指望的結果是,每一個頁面裏,都有本身的控件,操做這些控件的方法均可以分別寫在各自的Fragment中,而不是所有都寫在MainActivity中。因此,咱們要讓ViewPager裏面裝載的是Fragment,所以,咱們須要去拓展(extends)一個FragmentPagerAdapter。
實現FragmentPagerAdapter又須要事先準備好三個Fragment,三個Fragment又須要三個獨立的佈局。
所以咱們須要作的第一件事是準備三個咱們指望的佈局。
爲了簡單起見,咱們準備layout一、layout二、layout3(/res/layout/layout*.xml)這樣三個佈局,這三個佈局的背景顏色都是不同的,裏面都放了一個按鈕,爲了可以在後面實現每一個頁面獨立的功能。
佈局文件的代碼以下:
<?xml version="1.0" encoding="utf-8"?> <!-- 這是layout1.xml的佈局代碼,另外兩個佈局文件就是背景顏色和按鈕的id,text改變了而已 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#ff0000"> <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="button1"/> </LinearLayout>
接下來,咱們要拓展(extends)三個Fragment,分別是Fragment1.java、Fragment2.java、Fragment3.java(/src/fragment/Fragment*.java)。代碼以下:
推薦不一樣功能的類,創建不一樣的包來進行分類管理。
public class Fragment1 extends Fragment { private View layout = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.layout = inflater.inflate(R.layout.layout1, null); //從這裏到return之間,你能夠爲這個Fragment添加其功能。 Button b = (Button)this.layout.findViewById(R.id.button1); b.setOnClickListener(new ButtonListener()); return this.layout; } public class ButtonListener implements OnClickListener { @Override public void onClick(View v) { Toast.makeText(getActivity(), "button1", Toast.LENGTH_SHORT).show(); } } }
OKOK,三個Fragment也準備好了,接下來實現一個FragmentPagerAdapter(/src/adapter/FragmentAdapter.java),代碼以下:
public class FragmentAdapter extends FragmentPagerAdapter{ //這個是存放三個Fragment的數組,待會從MainActivity中傳過來就好了 private ArrayList<Fragment> fragmentArray; //本身添加一個構造函數從MainActivity中接收這個Fragment數組 public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragmentArray) { this(fm); this.fragmentArray = fragmentArray; } public FragmentAdapter(FragmentManager fm) { super(fm); } //這個函數的做用是當切換到第arg0個頁面的時候調用。 @Override public Fragment getItem(int arg0) { return this.fragmentArray.get(arg0); } @Override public int getCount() { return this.fragmentArray.size(); } }
實現好Adapter後,咱們就去MainActivity(src/com.kross.test/MainActivity.java)中配置一下。
在MainActivity中,咱們須要作以下幾件事情:
1.準備好Fragment,把它們添加進數組。
2.獲取到ViewPager的實例,爲其設置適配器。
3.設置ViewPager當前顯示第一頁。
由於在FragmentAdapter的構造函數中須要一個FragmentManager,因此,MainActivity應該繼承FragmentActivity。
代碼以下:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);this.initViewPager(); }
private void initViewPager() { //獲取到ViewPager的實例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //構造好存放Fragment的數組 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //爲ViewPager設置適配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //設置當前顯示的頁面 this.viewPager.setCurrentItem(0); } }
3.爲下面的三個tab添加點擊事件,並設置好相應的激活效果。
完成上面兩步,應該能夠實現三個頁面滑動的效果。但下面的三個Tab是沒有被用上的。咱們這步要作到的事情是:點擊下面的tab,切換到相應的頁面,並激活tab,滑動上面的頁面,並激活tab。
這裏須要使用到一個接口OnPageChangeListener。這個接口中有三個方法,都是在頁面滑動的時候會被調用。咱們須要使用這個接口,讓頁面滑動的時候激活相應的tab。
如今來實現該接口PagerListener(/src/listener/PagerListener.java),代碼以下:
public class PagerListener implements OnPageChangeListener { private ArrayList<View> tabArray = null;private final static String TAG = "PagerListener"; public PagerListener(ArrayList<View> tabArray) { this.tabArray = tabArray; } @Override //這個…… public void onPageScrollStateChanged(int arg0) { Log.i(TAG, "onPageScrollStateChanged:" + Integer.toString(arg0)); } @Override //這個方法是頁面滾動的時候被調用,咱們將在這個方法裏完成對激活條的操做 public void onPageScrolled(int arg0, float arg1, int arg2) { Log.i(TAG, "onPageScrolled"); } @Override //這個方法是當頁面切換完畢後被調用, public void onPageSelected(int arg0) { Log.i(TAG, "onPageSelected"); //先將全部的tab設置成灰色的 for (View item : this.tabArray) { ((TextView)item.findViewById(R.id.tab_text)).setTextColor(Color.rgb(20, 20, 20)); } //再將當前的tab設置成白色的,表示激活狀態 ((TextView)this.tabArray.get(arg0).findViewById(R.id.tab_text)).setTextColor(Color.WHITE); } }
接下來,咱們須要實現一個OnClickListener。
TabListener(src/listener/TabListener.java),代碼以下:
public class TabListener implements OnClickListener { private ViewPager viewPager = null; public TabListener(ViewPager v) { this.viewPager = v; } @Override public void onClick(View arg0) { switch (arg0.getId()) { case R.id.tab1: this.viewPager.setCurrentItem(0); break; case R.id.tab2: this.viewPager.setCurrentItem(1); break; case R.id.tab3: this.viewPager.setCurrentItem(2); break; } } }
OKOK,接下來,咱們只須要在MainActivity中獲取tab的實例,給tab設置監聽器,給ViewPager設置監聽器就OK了。
MainActivity的代碼變成以下這樣:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; //三個tab private View tab1 = null; private View tab2 = null; private View tab3 = null;//存放tab的數組 private ArrayList<View> tabArray = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);this.initViewPager(); this.initTab(); this.setListener(); }private void initTab() { this.tab1 = findViewById(R.id.tab1); this.tab2 = findViewById(R.id.tab2); this.tab3 = findViewById(R.id.tab3); this.tabArray = new ArrayList<View>(); this.tabArray.add(tab1); this.tabArray.add(tab2); this.tabArray.add(tab3); } private void initViewPager() { //獲取到ViewPager的實例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //構造好存放Fragment的數組 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //爲ViewPager設置適配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //設置當前顯示的頁面 this.viewPager.setCurrentItem(0); } private void setListener() { TabListener tl = new TabListener(this.viewPager); this.tab1.setOnClickListener(tl); this.tab2.setOnClickListener(tl); this.tab3.setOnClickListener(tl); this.viewPager.setOnPageChangeListener(new PagerListener(this.tabArray); } }
完成上面的三步,就基本上實現了大部分的功能了。頁面能夠切換,tab也能夠點,有激活效果。
4.使用Matrix讓激活條隨頁面的滑動而滑動。
激活條的滑動,須要使用到OnPageChangeListener接口中的onPageScrolled方法,
該方法有會在頁面被滑動的時候調用,將會獲取到三個參數,第一個參數爲當前頁面的下標,第二個參數爲滑動距離佔屏幕寬度的百分比,第三個參數爲實際滑動的像素。
假設咱們當前處在0下標的頁面,咱們從右往左滑動,滑向1下標的頁面,在滑動的時候,該方法會一直被調用。
在沒有完全切換到1下標的頁面時,第一個參數一直是0,直到完全切換到1下標的頁面。
第二個參數是個[0,1)的浮點數,一滑動,該參數就開始變大 0.12313---->0.23321---->0.31233,當頁面快完成完全切換的時候,也就是該參數將無限逼近1,但不會是1,而後忽然變成了0,完成的頁面的切換。
第三個參數就很好理解了,個人屏幕寬度是720px,我滑到通常,它的值就是360。
知道了這些,咱們須要獲取激活條須要移動的長度,以及屏幕的寬度。
tab有三個,因此咱們用屏幕寬度/3,這是每一個tab的寬度,tab的寬度 - 圖片的寬度,這是剩餘的空間,剩餘的空間 / 2,就是小圖片的偏移量(offset)。
激活條圖片默認會出如今所在控件位置的最左邊,初始的時候,咱們就須要將它的位置設置在一個offset以後。
所以MainActivity的代碼變成以下這樣:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; //三個tab private View tab1 = null; private View tab2 = null; private View tab3 = null; //偏移量 private int offset; //圖片寬度 private int imageWidth; //存放tab的數組 private ArrayList<View> tabArray = null; private ImageView activeBar = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //獲取激活條控件 this.activeBar = (ImageView)findViewById(R.id.active_bar); //獲取該激活條的寬度 this.imageWidth = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher).getWidth(); //獲取屏幕的寬度 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int screenWidth = dm.widthPixels; //計算出偏移量 this.offset = (screenWidth / 3 - imageWidth) / 2; this.initViewPager(); this.initTab(); this.setListener(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private void initTab() { this.tab1 = findViewById(R.id.tab1); this.tab2 = findViewById(R.id.tab2); this.tab3 = findViewById(R.id.tab3); this.tabArray = new ArrayList<View>(); this.tabArray.add(tab1); this.tabArray.add(tab2); this.tabArray.add(tab3); } private void initViewPager() { //獲取到ViewPager的實例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //構造好存放Fragment的數組 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //爲ViewPager設置適配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //設置當前顯示的頁面 this.viewPager.setCurrentItem(0); } private void setListener() { TabListener tl = new TabListener(this.viewPager); this.tab1.setOnClickListener(tl); this.tab2.setOnClickListener(tl); this.tab3.setOnClickListener(tl); //注意這裏的構造函數 this.viewPager.setOnPageChangeListener(new PagerListener(this.tabArray, this.activeBar, this.offset, this.imageWidth)); } }
須要注意給ViewPager設置監聽器的時候,傳入了激活條的句柄,偏移量以及圖片的寬度。
接下來咱們須要修改下PagerListener的代碼:
public class PagerListener implements OnPageChangeListener { private ArrayList<View> tabArray = null; private ImageView activeBar = null; private int offset; private int imageWidth; private final static String TAG = "PagerListener"; public PagerListener(ArrayList<View> tabArray, ImageView activeBar, int offset, int iw) { this.tabArray = tabArray; this.activeBar = activeBar; this.offset = offset; this.imageWidth = iw; } @Override //這個…… public void onPageScrollStateChanged(int arg0) { Log.i(TAG, "onPageScrollStateChanged:" + Integer.toString(arg0)); } @Override //這個方法是頁面滾動的時候被調用,咱們將在這個方法裏完成對激活條的操做 public void onPageScrolled(int arg0, float arg1, int arg2) { Log.i(TAG, "onPageScrolled"); //new一個矩陣 Matrix matrix = new Matrix(); //設置激活條的最終位置 switch (arg0) { case 0: //使用set直接設置到那個位置 matrix.setTranslate(this.offset, 0); break; case 1: matrix.setTranslate(this.offset * 3 + this.imageWidth, 0); break; case 2: matrix.setTranslate(this.offset * 5 + this.imageWidth * 2, 0); break; } //在滑動的過程當中,計算出激活條應該要滑動的距離 float t = (this.offset * 2 + this.imageWidth) * arg1; //使用post追加數值 matrix.postTranslate(t, 0); this.activeBar.setImageMatrix(matrix); } @Override //這個方法是當頁面切換完畢後被調用, public void onPageSelected(int arg0) { Log.i(TAG, "onPageSelected"); //先將全部的tab設置成灰色的 for (View item : this.tabArray) { ((TextView)item.findViewById(R.id.tab_text)).setTextColor(Color.rgb(20, 20, 20)); } //再將當前的tab設置成白色的,表示激活狀態 ((TextView)this.tabArray.get(arg0).findViewById(R.id.tab_text)).setTextColor(Color.WHITE); } }
OKOK,完成上面全部的代碼,就OK了。這樣就實現了全部的效果。真是辛苦啊……
若是還有疑惑的話,能夠來微博上問我:http://weibo.com/KrossFord