原文地址:http://www.cnblogs.com/kross/p/3372987.html
我們實現一個上面是一個可以左右滑動的頁面,下面是三個可點擊切換的tab按鈕,tab按鈕上還有一個激活條。效果如下圖所示:
----------------我是分割線--------------------

--------------------------我是分割線,下面的圖片表示往右滑動,白條中的小機器人往右滑動-------------------------

--------------------------我是分割線,切換到第二個頁面,tab中的第二個被激活,字呈白色-----------------------

------------------我是分割線-------------------
OK,效果就是如上所示的效果。現在開始一步一步的介紹如何實現。
1.創建好基礎的布局文件。
整個頁面是一個LinearLayout,如果你用其他的布局也是可以的,在下只是圖一個方便了。
根據最終效果,我們將整個布局分為三個部分:
第一部分是上面那一大塊有顏色的區域,這是一個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又需要三個獨立的布局。
因此我們需要做的第一件事是准備三個我們期望的布局。
為了簡單起見,我們准備layout1、layout2、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
原文地址:http://www.cnblogs.com/kross/p/3372987.html
