轉載請註明出處(萬分感謝!):
http://blog.csdn.net/javazejian/article/details/52144889
關聯文章:
ViewPager 從入門到帶你擼個啓動頁之ViewPager基礎入門(一)
ViewPager 從入門到帶你擼個啓動頁之Fragment+ViewPager(二)
ViewPager 從入門到帶你擼個啓動頁之實戰啓動頁(三)
ViewPager 從入門到帶你擼個啓動頁之實戰PageTransformer切換動畫特效(四)
一、啓動頁實現概述
通過前兩篇的分享,我們已經基本掌握了ViewPager的基本使用,本篇咱們就來實現一個通用的啓動頁,先來看看啓動頁效果:
看來效果還不錯,但實際上只用了ViewPager,並沒有用到Fragment,因爲移動就是3張圖片而已,所以PagerAdapter的實現類代碼也是相當簡單的。唯一比較棘手的是頁面移動過程中底部的3個小點也必須跟着移動,這點需要配合ViewPager的滑動監聽動態實時小圓點的移動位置。下面我們就來分析它的實現過程吧。
二、啓動頁實現過程
2.1 實現整體滑動效果
首先我們先來實現整體的滑動效果,也就是添加3張圖片作爲ViewPager的滑動頁面,因此我們的activity_guide.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</android.support.v4.view.ViewPager>
</RelativeLayout>
接下來我們創建GuideActivity.java:
package com.zejian.commonstartpage;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zejian
* Time 16/8/7.
* Description:
*/
public class GuideActivity extends Activity{
// ViewPager的數據
private List<ImageView> imageViewList;
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 去標題, 需要在setContentView方法之前調用
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_guide);
initView();// 初始化控件
}
/**
* 初始化控件
*/
private void initView() {
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
GuideAdapter adapter = new GuideAdapter();
mViewPager.setAdapter(adapter);
}
/**
* TODO:初始化ViewPager數據 void
*/
private void initData() {
int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
R.drawable.guide_3};
imageViewList = new ArrayList<>();
ImageView iv;// 圖片
for (int i = 0; i < imageResIDs.length; i++) {
iv = new ImageView(this);
iv.setBackgroundResource(imageResIDs[i]);
imageViewList.add(iv);
}
}
class GuideAdapter extends PagerAdapter {
@Override
public int getCount() {
return imageViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
/*
* 刪除元素
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView iv = imageViewList.get(position);
container.addView(iv);// 1. 向ViewPager中添加一個view對象
return iv; // 2. 返回當前添加的view對象
}
}
}
我們簡單說一下代碼,首先我們通過initData方法去創建我們所需需要的界面,因爲我們的界面都是圖片,所以這裏我們直接new出3個ImageView控件,並設置圖片即可,最後我們把其添加到imageViewList數據集合中,到此數據初始化完成:
/**
* 初始化ViewPager數據
*/
private void initData() {
int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
R.drawable.guide_3};
imageViewList = new ArrayList<>();
ImageView iv;// 圖片
for (int i = 0; i < imageResIDs.length; i++) {
iv = new ImageView(this);
iv.setBackgroundResource(imageResIDs[i]);
imageViewList.add(iv);
}
}
接着我們創建GuideAdapter數據適配器繼承自PagerAdapter,重寫其方法,返回我們所需要的界面,這個前兩篇已經說得很清楚了,這裏不過多分析了。最後我們把數據適配器GuideAdapter設置給ViewPager即可:
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
GuideAdapter adapter = new GuideAdapter();
mViewPager.setAdapter(adapter);
到此啓動頁初步實現完成,我們運行一下,看看效果。
不錯!預期效果已經開始出現。接下來我們先把最後一頁的按鈕添加上。
2.2 添加按鈕
需要添加按鈕的話我們就必須修改activity_guide.xml文件,添加一個按鈕如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</android.support.v4.view.ViewPager>
<Button
android:id="@+id/btn_guide_start_experience"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dip"
android:background="@drawable/button_bg"
android:textColor="@drawable/button_textcolor"
android:text="開始體驗"
android:textSize="20sp"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:visibility="gone"
/>
</RelativeLayout>
顯然我們添加了一個Button,並將其設置在父佈局的底部而且水平居中,注意此時按鈕先設置爲gone,待需要顯示時再設置其可見。然後我們在Activity_Guide.java中添加如下代碼:
btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience)
//設置監聽器
btnStartExperience.setOnClickListener(this);
設置完按鈕的點擊事件後,我們還需要解決一個問題,那就是當需要顯示Button時再顯示出來,很顯然,我們只需在第3頁時才需要顯示按鈕,這時我們就需要藉助ViewPager的滑動事件來處理了,因此我們實現ViewPager.OnPageChangeListener接口,並實現其方法如下:
/**
* 當頁面正在滾動時 position 當前選中的是哪個頁面 positionOffset 比例 positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
/**
* 當頁面被選中
*/
@Override
public void onPageSelected(int position) {
}
/**
* 當頁面滾動狀態改變
*/
@Override
public void onPageScrollStateChanged(int state) {
}
簡單介紹一下三個方法:
onPageScrolled(int position, float positionOffset,
int positionOffsetPixels)
當頁面正在滾動時被回調。
position 表示當前選中的是第幾個頁面;
positionOffset 是當前頁面滑動比例,其值得範圍是[0,1),如果頁面向右翻動,這個值不斷變大,最後在趨近1的情況後突變爲0。如果頁面向左翻動,這個值不斷變小,最後變爲0。
positionOffsetPixels 是當前頁面滑動像素的偏移距離onPageScrollStateChanged(int state)
當頁面滾動狀態改變時被回調。有三個值:0(END),1(PRESS) , 2(UP) 。
當用手指滑動翻頁時,手指按下去的時候會觸發這個方法,state值爲1,手指擡起時,如果發生了滑動(即使很小),其值會變爲2,最後變爲0 。總共執行這個方法三次。噹噹前頁面翻頁時,會執行這個方法兩次,state值分別爲2 , 0 。- onPageSelected(int position)
當頁面被選中時回調,position表示被選中的頁面,position下標從0開始計算。
很顯然,我們應該利用onPageSelected(int position)決定實現我們的Button是否展示,當position爲最後一個頁面時展示,其他時候都隱藏即可。代碼如下:
/**
* 當頁面被選中
*/
@Override
public void onPageSelected(int position) {
// 顯示體驗按鈕
if (position == imageViewList.size() - 1) {
// 顯示
btnStartExperience.setVisibility(View.VISIBLE);
} else {
btnStartExperience.setVisibility(View.GONE);// 隱藏
}
}
到此按鈕也集成完,我們看看整體GuideActivity代碼:
package com.zejian.commonstartpage;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zejian
* Time 16/8/7.
* Description:
*/
public class GuideActivity extends Activity implements ViewPager.OnPageChangeListener, View.OnClickListener {
// ViewPager的數據
private List<ImageView> imageViewList;
// 開始體驗按鈕
private Button btnStartExperience;
// ViewPager
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 去標題, 需要在setContentView方法之前調用
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_guide);
initView();// 初始化控件
}
/**
* 初始化控件
*/
private void initView() {
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience);
initData();// 初始化ViewPager數據
GuideAdapter adapter = new GuideAdapter();
mViewPager.setAdapter(adapter);
mViewPager.setOnPageChangeListener(this);// 設置監聽器
btnStartExperience.setOnClickListener(this);// 按鈕添加監聽
}
/**
* TODO:初始化ViewPager數據 void
*/
private void initData() {
int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
R.drawable.guide_3};
imageViewList = new ArrayList<>();
ImageView iv;// 圖片
for (int i = 0; i < imageResIDs.length; i++) {
iv = new ImageView(this);
iv.setBackgroundResource(imageResIDs[i]);
imageViewList.add(iv);
}
}
/**
* 當頁面正在滾動時 position 當前選中的是哪個頁面 positionOffset 比例 positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
/**
* 當頁面被選中
*/
@Override
public void onPageSelected(int position) {
// 顯示體驗按鈕
if (position == imageViewList.size() - 1) {
btnStartExperience.setVisibility(View.VISIBLE);// 顯示
} else {
btnStartExperience.setVisibility(View.GONE);// 隱藏
}
}
/**
* 當頁面滾動狀態改變
*/
@Override
public void onPageScrollStateChanged(int state) {
}
/**
* 打開新的界面
*/
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "跳轉新界面", Toast.LENGTH_SHORT).show();
}
class GuideAdapter extends PagerAdapter {
@Override
public int getCount() {
return imageViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
/*
* 刪除元素
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView iv = imageViewList.get(position);
container.addView(iv);// 1. 向ViewPager中添加一個view對象
return iv; // 2. 返回當前添加的view對象
}
}
}
我們運行看看其效果:
2.3 添加指示器
終於到最後一步了,也是比較麻煩的一步,爲每一個頁面添加指示器,而且指示器必須跟隨頁面的移動而移動。那就先從佈局開始吧,我們在activity_guide.xml文件最下方添加如下代碼:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dip">
<LinearLayout
android:id="@+id/ll_guide_point_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"></LinearLayout>
<View
android:id="@+id/select_point"
android:layout_width="10px"
android:layout_height="10px"
android:background="@drawable/point_select" />
</RelativeLayout>
我們在佈局中添加了一個相對佈局,其中包含了一個id爲ll_guide_point_group的LinearLayout,這個線性佈局主要用於存放指示器列表也就是3個灰色圓點,而另外一個id爲select_point的View則是表示一個被選中的紅色圓點。接下來我們就先來初始化這些點列表指示器的數據,因爲灰色圓點的數量跟啓動頁面的數量是一致的,所以有多少個頁面就有多少個灰色圓點,因此我們只需在生成圖片時同時生成灰色圓點即可,每個點的寬高都爲10,相隔距離也爲10,代碼如下:
private void initData() {
int[] imageResIDs = { R.drawable.guide_1, R.drawable.guide_2,
R.drawable.guide_3 };
imageViewList = new ArrayList<ImageView>();
ImageView iv;// 圖片
View view;// 點
LayoutParams params; // 參數類
for (int i = 0; i < imageResIDs.length; i++) {
iv = new ImageView(this);
iv.setBackgroundResource(imageResIDs[i]);
imageViewList.add(iv);
// 根據圖片的個數, 每循環一次向LinearLayout中添加一個點
view = new View(this);
view.setBackgroundResource(R.drawable.point_normal);
// 設置參數
params = new LayoutParams(10, 10);
if (i != 0) {
params.leftMargin = 10;
}
view.setLayoutParams(params);// 添加參數
llPointGroup.addView(view);
}
}
從代碼我們可以看出,根據圖片的個數, 每循環一次向LinearLayout(llPointGroup)中添加一個點,最後便可以生成與圖片數量相同的灰色圓點。現在我們來運行一下,看看點的效果:
嗯,小圓點已經出現,接下來就是如何移動的問題了。現在我們需要實現的效果是這樣的,當我們滑動頁面時,紅色小圓點也跟着移動,例如當頁面滑動到屏幕的一半時,紅色小圓點也應處於灰色圓點的中間位置。如下圖:
因此,我們去動態計算頁面滑動的比例,然後按相同比例去計算紅色小圓點需要滑動的距離。比如當第1頁往第0頁滑動到中間時,此時小紅點的滑動距離應該如下計算:
我們假設屏幕寬度爲320px,那麼當第1頁滑動到屏幕中間時,其滑動距離就是160,此時滑動比較就是160/320=0.5,也就是說紅色小圓點的滑動比例也是0.5,通過這個滑動比例我們就可以計算出兩個點間的滑動距離,我們可以通過以下方式計算出小紅點要滑動的距離:
pWidth=llPointGroup.getChildAt(1).getLeft()-llPointGroup.getChildAt(0).getLeft()
最後乘以滑動比例就是小紅點的滑動距離了。當然這只是其中一次滑動的完整計算過程,頁面滑動過程中的小紅點變化的計算方式也是相同(因爲滑動過程中會多次調用onPageScrolled方法後面會說明),現在剩下的問題是我們如何知道頁面滑動過程呢?還記得前面我們ViewPager設置的OnPageChangeListener監聽器吧?其中有個回調方法onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的參數positionOffset不就是頁面的滑動比例嗎?確實如此,因此我們可以利用這個滑動比例來動態計算小紅點的滑動距離,這樣也就可以達到頁面滑動的過程中小紅點也跟隨移動的效果。下面我們給出實現代碼:
/**
* 當頁面正在滾動時 position 當前選中的是哪個頁面 positionOffset 比例 positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
//獲取兩個點間的距離,獲取一次即可
if(pWidth==0) {
pWidth = llPointGroup.getChildAt(1).getLeft()
- llPointGroup.getChildAt(0).getLeft();
}
// 獲取點要移動的距離
int leftMargin = (int) (pWidth * (position + positionOffset));
// 給紅點設置參數
RelativeLayout.LayoutParams params = (android.widget.RelativeLayout.LayoutParams) mSelectPointView
.getLayoutParams();
params.leftMargin = leftMargin;
mSelectPointView.setLayoutParams(params);
}
這裏我們再來簡單分析一下計算移動距離的代碼:
// 獲取點要移動的距離
int leftMargin = (int) (pWidth * (position + positionOffset));
由於position表示當前頁面的下標,取值範圍在[0,2],而positionOffset則表示當前界面的滑動比例,取值範圍在[0,1),當頁面向右滑動,positionOffset值不斷變大,最後在趨近1的情況後突變爲0。如果頁面向左滑動,這個值不斷變小,最後變爲0,當positionOffset突變爲0時則說明頁面已處於展示位置。因此當我們把當前頁面往左滑動時,當前position=0,而positionOffset則在不斷的增大,我們過Log來觀察一下
當第0頁滑動到左側,而第1頁正好在中間展示時,position=1,而positionOffset=0;
因此我們可以通過positionOffset的值來計算小紅點的滑動距離,噹噹前頁面往左滑動時,positionOffset不斷增大,此時由於position=0;所以pWidth * (position + positionOffset)也在不斷增大,計算出小紅點的移動距離leftMargin後,我們只需動態設置其params.leftMargin參數即可,這也就達到了小紅點跟隨移動的目的(onPageScrolled函數在移動中多次被觸發),最終position=1,positionOffset=0,小紅點也剛好落在了第2個灰色小圓點上。往後第3個點原理也是一樣的,向右移動也依次類推即可。。
最後我們給出GuideActivity的代碼:
package com.zejian.commonstartpage;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zejian
* Time 16/8/7.
* Description:
*/
public class GuideActivity extends Activity implements ViewPager.OnPageChangeListener, View.OnClickListener {
// ViewPager的數據
private List<ImageView> imageViewList;
// 點的組
private LinearLayout llPointGroup;
// 選中的點view對象
private View mSelectPointView;
// 開始體驗按鈕
private Button btnStartExperience;
// ViewPager
ViewPager mViewPager;
// 點之間的寬度
private int pWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 去標題, 需要在setContentView方法之前調用
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_guide);
initView();// 初始化控件
}
/**
* 初始化控件
*/
private void initView() {
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience);
llPointGroup = (LinearLayout) findViewById(R.id.ll_guide_point_group);
mSelectPointView = findViewById(R.id.select_point);
initData();// 初始化ViewPager數據
GuideAdapter adapter = new GuideAdapter();
mViewPager.setAdapter(adapter);
mViewPager.setOnPageChangeListener(this);// 設置監聽器
btnStartExperience.setOnClickListener(this);// 按鈕添加監聽
}
/**
* TODO:初始化ViewPager數據 void
*/
private void initData() {
int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
R.drawable.guide_3};
imageViewList = new ArrayList<>();
ImageView iv;// 圖片
View view;// 點
LayoutParams params; // 參數類
for (int i = 0; i < imageResIDs.length; i++) {
iv = new ImageView(this);
iv.setBackgroundResource(imageResIDs[i]);
imageViewList.add(iv);
// 根據圖片的個數, 每循環一次向LinearLayout中添加一個點
view = new View(this);
view.setBackgroundResource(R.drawable.point_normal);
// 設置參數
params = new LayoutParams(10, 10);
if (i != 0) {
params.leftMargin = 10;
}
view.setLayoutParams(params);// 添加參數
llPointGroup.addView(view);
}
}
/**
* 當頁面正在滾動時 position 當前選中的是哪個頁面 positionOffset 比例 positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
Log.e("zejian","positionOffset:-->"+positionOffset);
Log.e("zejian","position:-->"+position);
//獲取兩個點間的距離,獲取一次即可
if(pWidth==0) {
pWidth = llPointGroup.getChildAt(1).getLeft()
- llPointGroup.getChildAt(0).getLeft();
}
// 獲取點要移動的距離
int leftMargin = (int) (pWidth * (position + positionOffset));
// 給紅點設置參數
RelativeLayout.LayoutParams params = (android.widget.RelativeLayout.LayoutParams) mSelectPointView
.getLayoutParams();
params.leftMargin = leftMargin;
mSelectPointView.setLayoutParams(params);
}
/**
* 當頁面被選中
*/
@Override
public void onPageSelected(int position) {
// 顯示體驗按鈕
if (position == imageViewList.size() - 1) {
btnStartExperience.setVisibility(View.VISIBLE);// 顯示
} else {
btnStartExperience.setVisibility(View.GONE);// 隱藏
}
}
/**
* 當頁面滾動狀態改變
*/
@Override
public void onPageScrollStateChanged(int state) {
}
/**
* 打開新的界面
*/
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "跳轉新界面", Toast.LENGTH_SHORT).show();
}
class GuideAdapter extends PagerAdapter {
@Override
public int getCount() {
return imageViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
/*
* 刪除元素
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView iv = imageViewList.get(position);
container.addView(iv);// 1. 向ViewPager中添加一個view對象
return iv; // 2. 返回當前添加的view對象
}
}
}
最近效果:
到此,啓動頁已全部完成,告本篇一段落。源碼下載:
啓動頁(commonstartpage項目)源碼GitHub下載地址
ViewPager 從入門到帶你擼個啓動頁之ViewPager基礎入門(一)
ViewPager 從入門到帶你擼個啓動頁之Fragment+ViewPager(二)