Android實現自定義引導頁,玩轉ViewPager
ViewPager簡介:
ViewPager(android.support.v4.view.ViewPager)是android擴充套件包v4包中的類,這個類可以讓使用者左右切換當前的view,實現滑動切換的效果。
注意:
ViewPager類直接繼承了ViewGroup類,也就是說它和我們經常打交道的LinearLayout一樣,都是一個容器,需要在裡面新增我們想要顯示的內容。
ViewPager類需要一個PagerAdapter介面卡類給它提供資料,這個和ListView類似。
ViewPager基礎使用
具體步驟:
1.在佈局檔案里加入
<android.support.v4.view.ViewPager
android:id="@+id/in_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
2.在Activity中載入要顯示的Views,通過動態載入佈局得到一個個View
mViewList = new ArrayList<View>();
LayoutInflater lf = getLayoutInflater().from(MainActivity.this );
View view1 = lf.inflate(R.layout.we_indicator1, null);
View view2 = lf.inflate(R.layout.we_indicator2, null);
View view3 = lf.inflate(R.layout.we_indicator3, null);
mViewList.add(view1);
mViewList.add(view2);
mViewList.add(view3);
3.自定義PagerAdapter,以便將步驟2中的Views資料載入到ViewPager容器中
public class ViewPagerAdatper extends PagerAdapter {
private List<View> mViewList ;
public ViewPagerAdatper(List<View> mViewList ) {
this.mViewList = mViewList;
}
@Override
public int getCount() {
return mViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return mViewList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
}
4.將ViewPager和自定義的PagerAdapter關聯起來
mIn_vp.setAdapter(new ViewPagerAdatper(mViewList));
通過簡單使用ViewPager,得到的展示效果,僅僅支援左右滑動,效果比較單一。
<img src="http://upload-images.jianshu.io/upload_images/3985563-bd29936a4e142c3c.gif?imageMogr2/auto-orient/strip" width="5" height="5" />
ViewPager進階使用——實現跟隨式小圓點效果
效果圖:
步驟:
1.新增小圓點
在佈局中的設定如下:
<RelativeLayout
android:id="@+id/rl_dots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp">
<LinearLayout
android:id="@+id/in_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
<ImageView
android:id="@+id/iv_light_dots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/light_dot"/>
</RelativeLayout>
隨後在LinearLayout中動態新增3個小黑點,小白點預設覆蓋在第一個小黑點的上面。
在Activity中的新增小黑點,程式碼如下:
private void addDots() {
mOne_dot = new ImageView(this);
mOne_dot.setImageResource(R.drawable.gray_dot);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(0, 0, 40, 0);
mIn_ll.addView(mOne_dot, layoutParams);
mTwo_dot = new ImageView(this);
mTwo_dot.setImageResource(R.drawable.gray_dot);
mIn_ll.addView(mTwo_dot, layoutParams);
mThree_dot = new ImageView(this);
mThree_dot.setImageResource(R.drawable.gray_dot);
mIn_ll.addView(mThree_dot, layoutParams);
setClickListener();
}
2.獲得兩個點之間的距離
實現小白點動態跟隨的思路是:根據頁面的移動情況,通過設定小白點的 leftMargin 實現小紅點的動態跟隨。因此首要的是獲得兩個點之間的距離,根據頁面移動到的位置,進行相應的運算。
需要注意的是:在onCreate()中直接獲得小白點的左邊距 getLeft()結果為0,這是因為View元件佈局要在onResume回撥後完成。
因此使用另一種方法:
mLight_dots.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//獲得兩個圓點之間的距離
mDistance = mIn_ll.getChildAt(1).getLeft() - mIn_ll.getChildAt(0).getLeft();
mLight_dots.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
在佈局發生改變或者某個檢視的可視狀態發生改變時呼叫該事件。但此方法會被多次呼叫,因此需要在獲取到檢視的寬度和高度後執行 remove 方法移除該監聽事件。
3.監聽頁面的移動情況
mIn_vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//頁面滾動時小白點移動的距離,並通過setLayoutParams(params)不斷更新其位置
float leftMargin = mDistance * (position + positionOffset);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mLight_dots.getLayoutParams();
params.leftMargin = (int) leftMargin;
mLight_dots.setLayoutParams(params);
}
@Override
public void onPageSelected(int position) {
//頁面跳轉時,設定小圓點的margin
float leftMargin = mDistance * position;
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mLight_dots.getLayoutParams();
params.leftMargin = (int) leftMargin;
mLight_dots.setLayoutParams(params);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
在頁面移動過程中,根據mDistance * (position + positionOffset) 可以實時更新小白點的位置
這部分內容加入了一個新的功能 點選小黑點 可以直接跳轉到對應的引導頁面,具體邏輯就是在小黑點的點選事件中加入如下程式碼:
mIn_vp.setCurrentItem(1);
在頁面選擇過程中,根據mDistance * position可以實時小紅點的位置
4.跳轉按鈕的實現
具體邏輯:到引導頁到達最後一頁時,跳轉到主頁的按鈕出現。其他情況為隱藏狀態
因此可以在onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
和onPageSelected(int position)
方法中加入如下程式碼:
if(position==2){
mBtn_next.setVisibility(View.VISIBLE);
}
當按照從第一頁到最後一頁,然後點選按鈕跳轉到首頁,則以上邏輯足夠。
但當用戶瀏覽到最後一頁後再回轉到前面感興趣的頁面,則會出現按鈕依舊出現的情況,不符合要求。因此要完善邏輯,加入新的判斷,邏輯如下:
if(position!=2&&mBtn_next.getVisibility()==View.VISIBLE){
mBtn_next.setVisibility(View.GONE);
}
以上邏輯保證了按鈕的正常顯示,剩下的就可以具體操作,實現跳轉到首頁的邏輯。
ViewPager進階使用——自定義炫酷動畫
ViewPager自帶了一個setPageTransformer用於設定切換動畫~
setPageTransformer (boolean reverseDrawingOrder, PageTransformer transformer)
需要傳入兩個引數
第一個引數:如果為true,則表明自定義的pageTransformer需要 page view從後到前的順序繪製,反之則為false。
第二個引數:傳入一個自定義的pageTransformer物件
因此實現炫酷動畫的關鍵點就在於:自定義pageTransformer
Google官方給我們展示了兩個動畫例子:DepthPageTransformer和ZoomOutPageTransformer,比較炫。我們就以Google官方的例子來學習自定義pageTransformer,以此為基礎,我們可以自定義各種各樣的動畫實現效果。
1.PageTransformer中position解析
自定義PageTransformer只需要實現一個方法,transformPage(View page, float position)
,而這個方法實現的關鍵就是對position的理解。
原始碼中的註釋解釋如下:
Apply the transformation to this page
position - Position of page relative to the current front-and-center position of the pager. 0 is front and center. 1 is one full page position to the right, and -1 is one page position to the left.
我們可以理解為:
0表示當前頁面,是當前頁面
-1表示左側的頁面,是左側頁面
1表示右側的頁面,是右側頁面
在使用者滑動介面的時候,position是動態變化的,下面以左滑為例:
選中頁面 position:0->-1
前一個item position:-1 -> -2
後一個item position:1 -> 0
但是當ViewPager設定pageMargin,設定兩個頁面之間的距離(通過呼叫viewPager.setPageMargin()方法設定)後,情況則不同。
如果你設定了pageMargin,前後頁面的position需要分別加上(或減去,前減後加)一個偏移量(偏移量的計算方式為pageMargin / pageWidth)。
同樣以左滑為例:
選中頁面position:0->-1 - pageMargin / pageWidth
前一個item position:-1 - pageMargin / pageWidth -> -2 - pageMargin / pageWidth
後一個item position:1 + pageMargin / pageWidth -> 0
因此我們可以將position的值應用於setAlpha(), setTranslationX(), 或者 setScaleY()等等方法,從而實現自定義的動畫效果。
2.實現transformPage(View page, float position)
方法
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
@Override
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
if (position < -1) { // [-Infinity,-1)
// 頁面遠離左側頁面
page.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// 頁面在由中間頁滑動到左側頁面 或者 由左側頁面滑動到中間頁
page.setAlpha(1);
page.setTranslationX(0);
page.setScaleX(1);
page.setScaleY(1);
} else if (position <= 1) { // (0,1]
//頁面在由中間頁滑動到右側頁面 或者 由右側頁面滑動到中間頁
// 淡入淡出效果
page.setAlpha(1 - position);
// 反方向移動
page.setTranslationX(pageWidth * -position);
// 0.75-1比例之間縮放
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// 頁面遠離右側頁面
page.setAlpha(0);
}
}
}
實現效果: