什么是ViewPager?
關于ViewPager的介紹和使用,在之前我寫過一篇相關的文章《安卓開發復習筆記——ViewPager組件(仿微信引導界面)》,不清楚的朋友可以看看,這里就不再重復。
什么是Fragment?
Fragment是Android3.0后新增的概念,Fragment名為碎片,不過卻和Activity十分相似,具有自己的生命周期,它是用來描述一些行為或一部分用戶界面在一個Activity中,我們可以合并多個Fragment在一個單獨的activity中建立多個UI面板,或者重用Fragment在多個activity中。
關于Fragment的生命周期,由于Fragment需要依賴Activity,也就是說當一個Activity的生命周期結束之后,那么Fragment的生命周期也自然結束。如果把一個Activiy比作一座大宅子的話,那么Fragment就可以比作大宅子里的房間,大宅子里的房間其中一間倒塌了,并不會引起整個大宅子的倒塌,但如果大宅子倒塌了,那么大宅里的房間也就都倒塌了。
下面來看下Fragment的生命周期: Activity和Fragment生命周期對比(相似):

為了更好的理解Fragment,我找了下面的一張圖:
看左邊這張圖,它是我們傳統的手機界面,假設它現在呈現的是一個新聞列表頁,那么當我們點擊列表項中,我們將會跳轉到新聞詳細頁中,上面是標題,下面是正文,這里是2個Activity。
再看看右邊的圖,左邊是新聞列表頁,右邊是新聞詳細頁,我們可以動態的點擊左邊的列表項,使得右邊的新聞詳細頁動態變化,這里只有1個Activity里面嵌套了2個Fragment,左邊一個,右邊一個。

好了,做了簡單的介紹后,先來看看今天我們要實現的效果圖:(高仿微信主界面)

這里我畫了張界面分析圖,畫圖永遠的痛,湊合著看哈

這里的xml布局文件,我把每一部分都分開寫了:
top1.xml
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="50dp" 5 android:background="@drawable/bg" 6 android:paddingLeft="12dp" 7 android:paddingRight="12dp" > 8 9 <LinearLayout10 android:layout_width="wrap_content"11 android:layout_height="wrap_content"12 android:layout_centerVertical="true"13 android:gravity="center"14 android:orientation="horizontal" >15 16 <ImageView17 android:layout_width="30dp"18 android:layout_height="30dp"19 android:src="@drawable/weixin" />20 21 <TextView22 android:layout_width="wrap_content"23 android:layout_height="wrap_content"24 android:layout_marginLeft="12dp"25 android:text="微信"26 android:textColor="@android:color/white"27 android:textSize="18dp" />28 </LinearLayout>29 30 <LinearLayout31 android:layout_width="wrap_content"32 android:layout_height="wrap_content"33 android:layout_alignParentRight="true"34 android:layout_centerVertical="true"35 android:gravity="center"36 android:orientation="horizontal" >37 38 <ImageView39 android:layout_width="30dp"40 android:layout_height="30dp"41 android:src="@drawable/search" />42 43 <ImageView44 android:layout_width="30dp"45 android:layout_height="30dp"46 android:src="@drawable/add" />47 48 <ImageView49 android:layout_width="30dp"50 android:layout_height="30dp"51 android:src="@drawable/more" />52 </LinearLayout>53 54 </RelativeLayout>
top2.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="40dp" 4 android:orientation="vertical" > 5 6 <LinearLayout 7 android:layout_width="match_parent" 8 android:layout_height="37dp" 9 android:gravity="center_vertical"10 android:background="#cccccc"11 >12 13 <LinearLayout14 android:layout_width="wrap_content"15 android:layout_height="wrap_content"16 android:layout_weight="1"17 android:gravity="center" >18 19 <TextView20 android:id="@+id/tv1"21 android:layout_width="wrap_content"22 android:layout_height="wrap_content"23 android:text="聊天" 24 android:textColor="#339900"/>25 </LinearLayout>26 27 <LinearLayout28 android:layout_width="wrap_content"29 android:layout_height="wrap_content"30 android:layout_weight="1"31 android:gravity="center" >32 33 <TextView34 android:id="@+id/tv2"35 android:layout_width="wrap_content"36 android:layout_height="wrap_content"37 android:text="發現" 38 android:textColor="@android:color/black"/>39 </LinearLayout>40 41 <LinearLayout42 android:layout_width="wrap_content"43 android:layout_height="wrap_content"44 android:layout_weight="1"45 android:gravity="center" >46 47 <TextView48 android:id="@+id/tv3"49 android:layout_width="wrap_content"50 android:layout_height="wrap_content"51 android:text="通訊錄" 52 android:textColor="@android:color/black"/>53 </LinearLayout>54 </LinearLayout>55 56 <LinearLayout57 android:layout_width="match_parent"58 android:layout_height="3dp" >59 60 <ImageView61 android:id="@+id/tabline"62 android:layout_width="100dp"63 android:layout_height="match_parent"64 android:background="@drawable/tabline" />65 </LinearLayout>66 67 </LinearLayout>
mywx.xml(用include包含前2個布局文件,并設置垂直排列)
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context="com.example.weixin_test.MyWxTest" > 7 8 <include layout="@layout/top1" /> 9 10 <include layout="@layout/top2" />11 12 13 <android.support.v4.view.ViewPager14 android:id="@+id/viewpager"15 android:layout_width="match_parent"16 android:layout_height="wrap_content"17 android:layout_weight="1"18 >19 20 21 </android.support.v4.view.ViewPager>22 </LinearLayout>
Fragment1.xml(由于Flagment的布局文件只是簡單采用字符標示,布局都一樣,這里只給出第一個Fragment布局文件)
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 > 6 7 <TextView 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content"10 android:text="我是第一個界面"11 android:textSize="30dp"12 android:layout_centerInParent="true"13 14 />15 16 17 </RelativeLayout>
接下來是java代碼了,注釋很全(其實用法還是之前的ViewPager,只不過之前的ViewPager的數據源里存放的是view對象,而這里是Fragment)
1 package com.example.weixin_test; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.graphics.Color; 7 import android.os.Bundle; 8 import android.support.v4.app.Fragment; 9 import android.support.v4.app.FragmentActivity; 10 import android.support.v4.app.FragmentPagerAdapter; 11 import android.support.v4.view.ViewPager; 12 import android.support.v4.view.ViewPager.OnPageChangeListener; 13 import android.util.DisplayMetrics; 14 import android.util.Log; 15 import android.view.Display; 16 import android.view.ViewGroup.LayoutParams; 17 import android.view.Window; 18 import android.widget.ImageView; 19 import android.widget.LinearLayout; 20 import android.widget.TextView; 21 22 public class MyWxTest extends FragmentActivity { 23 24 PRivate ViewPager viewPager;// 聲明一個viewpager對象 25 private TextView tv1; 26 private TextView tv2; 27 private TextView tv3; 28 private ImageView tabline; 29 private List<Fragment> list;// 聲明一個list集合存放Fragment(數據源) 30 31 private int tabLineLength;// 1/3屏幕寬 32 private int currentPage = 0;// 初始化當前頁為0(第一頁) 33 34 @Override 35 protected void onCreate(Bundle savedInstanceState) { 36 super.onCreate(savedInstanceState); 37 requestWindowFeature(Window.FEATURE_NO_TITLE); 38 setContentView(R.layout.mywx); 39 // 初始化滑動條1/3 40 initTabLine(); 41 42 // 初始化界面 43 initView(); 44 } 45 46 private void initTabLine() { 47 // 獲取顯示屏信息 48 Display display = getWindow().getWindowManager().getDefaultDisplay(); 49 // 得到顯示屏寬度 50 DisplayMetrics metrics = new DisplayMetrics(); 51 display.getMetrics(metrics); 52 // 1/3屏幕寬度 53 tabLineLength = metrics.widthPixels / 3; 54 // 獲取控件實例 55 tabline = (ImageView) findViewById(R.id.tabline); 56 // 控件參數 57 LayoutParams lp = tabline.getLayoutParams(); 58 lp.width = tabLineLength; 59 tabline.setLayoutParams(lp); 60 } 61 62 private void initView() { 63 // 實例化對象 64 viewPager = (ViewPager) findViewById(R.id.viewpager); 65 tv1 = (TextView) findViewById(R.id.tv1); 66 tv2 = (TextView) findViewById(R.id.tv2); 67 tv3 = (TextView) findViewById(R.id.tv3); 68 list = new ArrayList<Fragment>(); 69 70 // 設置數據源 71 Fragment1 fragment1 = new Fragment1(); 72 Fragment2 fragment2 = new Fragment2(); 73 Fragment3 fragment3 = new Fragment3(); 74 75 list.add(fragment1); 76 list.add(fragment2); 77 list.add(fragment3); 78 79 // 設置適配器 80 FragmentPagerAdapter adapter = new FragmentPagerAdapter( 81 getSupportFragmentManager()) { 82 83 @Override 84 public int getCount() { 85 return list.size(); 86 } 87 88 @Override 89 public Fragment getItem(int arg0) { 90 return list.get(arg0); 91 } 92 }; 93 94 // 綁定適配器 95 viewPager.setAdapter(adapter); 96 97 // 設置滑動監聽 98 viewPager.setOnPageChangeListener(new OnPageChangeListener() { 99 100 @Override101 public void onPageSelected(int position) {102 // 當頁面被選擇時,先講3個textview的字體顏色初始化成黑103 tv1.setTextColor(Color.BLACK);104 tv2.setTextColor(Color.BLACK);105 tv3.setTextColor(Color.BLACK);106 107 // 再改變當前選擇頁(position)對應的textview顏色108 switch (position) {109 case 0:110 tv1.setTextColor(Color.rgb(51, 153, 0));111 break;112 case 1:113 tv2.setTextColor(Color.rgb(51, 153, 0));114 break;115 case 2:116 tv3.setTextColor(Color.rgb(51, 153, 0));117 break;118 }119 120 currentPage = position;121 122 }123 124 @Override125 public void onPageScrolled(int arg0, float arg1, int arg2) {126 Log.i("tuzi", arg0 + "," + arg1 + "," + arg2);127 128 // 取得該控件的實例129 LinearLayout.LayoutParams ll = (android.widget.LinearLayout.LayoutParams) tabline130 .getLayoutParams();131 132 if (currentPage == 0 && arg0 == 0) { // 0->1移動(第一頁到第二頁)133 ll.leftMargin = (int) (currentPage * tabLineLength + arg1134 * tabLineLength);135 } else if (currentPage == 1 && arg0 == 1) { // 1->2移動(第二頁到第三頁)136 ll.leftMargin = (int) (currentPage * tabLineLength + arg1137 * tabLineLength);138 } else if (currentPage == 1 && arg0 == 0) { // 1->0移動(第二頁到第一頁)139 ll.leftMargin = (int) (currentPage * tabLineLength - ((1 - arg1) * tabLineLength));140 } else if (currentPage == 2 && arg0 == 1) { // 2->1移動(第三頁到第二頁)141 ll.leftMargin = (int) (currentPage * tabLineLength - (1 - arg1)142 * tabLineLength);143 }144 145 tabline.setLayoutParams(ll);146 147 }148 149 @Override150 public void onPageScrollStateChanged(int arg0) {151 // TODO Auto-generated method stub152 153 }154 });155 156 }157 158 }對這個類做下說明:
1、這里的滑動屏幕下劃線動態跟隨的效果,其實實現方法有2種,原理是一樣的
(1)可以使用ViewPager的兩個子類ViewFlipper和ViewSwitche,這種方法比較簡單,直接用就行。
(2)用原生代碼實現,也就是動態的去控制下劃線的左外邊距。
這里我采用的是第2種方法,我覺得授人予魚還不如授人予漁,其實也并不復雜,細節去理下細節就懂了。
這里需要注意一個地方,我們在給ViewPager設置監聽器時,這邊會復寫一個onPageScrolled方法,里面有3個參數,我用Log打印出它們在頁面滑動時的數據變化

這是頁面一向頁面二滑動時候的數據記錄:
我們可以發現第一個參數值直接從0->1,第二個參數值從0.0依次增加到0.9xx無限靠近1,然后頁面到達第二頁它又恢復成了0,第三個參數從1開始累積到300+(這個我們不去關注)

這是頁面二向頁面三滑動時候的數據記錄:
我們可以發現第一個參數值直接從1->2,第二個參數值從0.0依次增加到0.9xx無限靠近1,然后頁面到達第二頁它又恢復成了0,第三個參數從1開始累積到300+(這個我們不去關注)
因此我們可以發現一個規律:
當ViewPager頁面值為0(第一頁)且當參數一為0時,頁面的狀態時從 第一頁到第二頁
當ViewPager頁面值為1(第二頁)且當參數一為1時,頁面的狀態時從 第一頁到第二頁
以此類推,大家可以自己打印出來看看,對這些數據比較有感覺,由于文章篇幅問題,這里就不再貼圖了。
我們可以利用第二個參數從0.0推薦遞增到1,這個數據來控制左外邊距(在第一頁時左外邊距為0,第二頁時左外邊距為1/3屏幕寬,第三頁時左外邊距為2/3屏幕寬)
由此推導出的公式為:
向左滑時:當前頁數*屏幕1/3寬+onPageScrolled方法第二個參數*屏幕1/3寬
向右滑時:當前頁數*屏幕1/3寬-(1-onPageScrolled方法第二個參數)*屏幕1/3寬
2、由于這里使用到了Fragment,這里就不再和以往一樣繼承Activity,這里需要繼承Activity的子類FragmentActivity。
由于3個Fragment的代碼幾乎一致,所以這里只給出Fragment1.java
1 package com.example.weixin_test; 2 3 import android.os.Bundle; 4 import android.support.annotation.Nullable; 5 import android.support.v4.app.Fragment; 6 import android.view.LayoutInflater; 7 import android.view.View; 8 import android.view.ViewGroup; 9 10 public class Fragment1 extends Fragment {11 @Override12 public View onCreateView(LayoutInflater inflater,13 @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {14 return inflater.inflate(R.layout.fragment1, container, false);15 }16 17 }來講一下關于這個類的說明:
1、Fragment一般是作為Activity界面的一部分,它把Layout對象嵌入到了Activity之中,若要對一個Fragment提供Layout對象必須去調用一個onCreateView()方法,它的返回值是一個View對象,這個方法為我們提供了一個LayoutInflater便于我們把XML布局文件轉換成View對象。
2、onCreateView()方法中:
container參數是用來存放Fragment的layout。
saveInstanceState參數是一個Bundle,跟Activity的onCreate()中Bundle差不多,用于狀態恢復。
3、inflate()方法中有三個參數:
1:layout的資源id。
2:存放fragment的layout的ViewGroup。
3:這個布爾值是代表是否在創建Fragment的layout期間,把layout附加到container上,由于系統已經把layout對象存放在了ViewGroup中,所以這里為false。
作者:Balla_兔子出處:http://m.survivalescaperooms.com/lichenwei/本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我!
新聞熱點
疑難解答