實(shí)現(xiàn)基礎(chǔ)的ViewPager###
1.主布局文件activity_main.xml,一個用于放ViewPager的RelativeLayout容器,里面就是我們用于展示的ViewPager了。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
tools:context="com.momo.pagetransformerdemo.activity.MainActivity">
<RelativeLayout
android:id="@+id/rl_container"
android:layout_width="match_parent"
android:layout_height="180dp"
android:clipChildren="false"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50dp">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="100dp"
android:layout_height="match_parent"
android:clipChildren="false"
android:layout_centerHorizontal="true">
</android.support.v4.view.ViewPager>
</RelativeLayout>
</RelativeLayout>
這里有個需要注意的地方:RelativeLayout和ViewPager的clipChildren屬性都需要設(shè)置為false,目的是讓ViewPager左邊右邊的界面也顯示出來,不然ViewPager只會顯示中間ViewPager在布局上所占的那部分空間。如果不清楚直接看下圖:
2.ViewPager的Adapter類,在構(gòu)造函數(shù)中傳入要顯示的數(shù)據(jù)源,這里就是每個page上要顯示的文本。在instantiateItem()方法中加載要顯示的頁面,找到其中的TextView,設(shè)置要顯示的文本。至于這四個函數(shù)的作用可以參考ViewPager 詳解(二)---詳解四大函數(shù)
MyPagerAdapter.java
public class MyPagerAdapter extends PagerAdapter {
private String[] mDatas;
private Context mContext;
public MyPagerAdapter(Context context,String[] mDatas) {
this.mContext =context;
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(mContext).inflate(R.layout.view_pager_item,null);
TextView tv = (TextView) view.findViewById(R.id.tv_text);
tv.setText(mDatas[position]);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
}
3.ViewPager的每個界面布局view_pager_item.xml,就是一個Textview,沒啥好說的。
<?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:background="@drawable/item_bg">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Page 1"
android:textSize="20dp"
android:layout_centerInParent="true"
android:textColor="#ffffff"
/>
</RelativeLayout>
4.MainActivity.java
public class MainActivity extends AppCompatActivity {
private RelativeLayout rl_container;
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findId();
initViewPager();
}
private void findId() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
rl_container = (RelativeLayout) findViewById(R.id.rl_container);
}
private void initViewPager() {
//創(chuàng)建數(shù)據(jù)源
String[] stringArr = new String[8];
for(int i=0;i<stringArr.length;i++){
stringArr[i] = "Page" + String.valueOf(i);
}
//設(shè)置Adapter
viewPager.setAdapter(new MyPagerAdapter(this,stringArr));
//設(shè)置緩存頁面數(shù)量
viewPager.setOffscreenPageLimit(stringArr.length/2);
//設(shè)置頁面間距
viewPager.setPageMargin(10);
}
}
寫到這里我們可以來運(yùn)行一下,結(jié)果發(fā)現(xiàn)只有中間滑動有效果,而如果在兩邊滑動時(shí),ViewPager是不會切換頁面的。這是因?yàn)辄c(diǎn)擊事件被容器RelativeLayout攔截了。加入下面的代碼就能解決問題了。
@Override
protected void onCreate(Bundle savedInstanceState) {
......
initViewPager();
rl_container.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return viewPager.dispatchTouchEvent(event);
}
});
}
給RelativeLayout設(shè)置OnTouchListener使得當(dāng)其接收到觸摸事件時(shí)會將該事件轉(zhuǎn)給ViewPager進(jìn)行分發(fā)。這樣就能讓ViewPager處理左右兩邊的觸摸事件了。
至此,一個簡單的ViewPager就完成了,來看看效果。
PageTransformer###
這個類是我們實(shí)現(xiàn)自定義切換動畫的核心。ViewPager有一個setPageTransformer()方法用于設(shè)置切換動畫。goolgle官方提供了兩個動畫效果,DepthPageTransformer和ZoomOutPageTransformer,具體效果可以參考一下鴻洋大神的這篇博客Android 實(shí)現(xiàn)個性的ViewPager切換動畫 實(shí)戰(zhàn)PageTransformer(兼容Android3.0以下)。關(guān)于下面要講到的position的意義,如果覺得博主講的不清楚也可以參考這篇博客Android 自定義 ViewPager 打造千變?nèi)f化的圖片切換效果。
我們看到PageTransformer中要實(shí)現(xiàn)這樣一個函數(shù):
@Override
public void transformPage(View page, float position) {
}
解釋一下,這里的page指的就是在滑動的頁面,而position是指該頁面的位置,用下面這張圖解釋:
position取值可以看成該頁面左上角的位置
- 當(dāng)position<-1的時(shí)候,表示該page處在當(dāng)前顯示頁面的左邊一個頁面位置以外。
- 當(dāng)position=-1的時(shí)候,表示該page處在當(dāng)前顯示頁面的左邊一個頁面位置,此時(shí)該頁面的右上角就是當(dāng)前顯示頁面的左上角。
- 當(dāng)position=0的時(shí)候,表示該page處在當(dāng)前顯示頁面位置
- 當(dāng)position=1的時(shí)候,表示該page處在當(dāng)前顯示頁面的右邊一個頁面位置,此時(shí)該頁面的左上角就是當(dāng)前顯示頁面的右上角。
- 當(dāng)position>1的時(shí)候,表示該page處在當(dāng)前顯示頁面的右邊一個頁面位置以外。
舉個栗子:
上圖中page0的position為-2,page1為-1,page2為當(dāng)前顯示頁面,position為0,page3為1,page4為2。在page2往左邊劃的過程中,page2的position值會從1變到0,同理,page3的position值會從1變到0。
有了這樣的一個position值,便可以開始制作動畫了。
制作動畫前先看一下要用到的幾個函數(shù)(這里可以參考這篇博客【Android開發(fā)】View的平移、縮放、旋轉(zhuǎn)以及位置、坐標(biāo)系)
-
page.setTranslationX(float x)
page.setTranslationY(float y)
這兩個函數(shù)用于設(shè)置page在x,y方向的位移 -
page.setScaleX(float scaleX);
page.setScaleY(float scaleY);
這兩個函數(shù)用于設(shè)置page在x,y方向的放縮比例 -
page.setAlpha(float alpha)
這個函數(shù)用于設(shè)置page的透明度 -
page.setRotation(float rotation)
page.setRotationX(float rotationX)
page,setRotationY(float rotationY)
這三個函數(shù)用于設(shè)置page在x,y,z軸方向的旋轉(zhuǎn)角度 -
page.setPivotX(float pivotX)
page.setPivotY(float pivotY)
這兩個函數(shù)是最重要的,也相對比較難理解,用于設(shè)置view縮放和旋轉(zhuǎn)的錨點(diǎn)坐標(biāo),正常情況下page的錨點(diǎn)在正中心位置
錨點(diǎn)的位置,將決定page縮放后所在的位置。因?yàn)槟J(rèn)是中心坐標(biāo),所以縮放后,其結(jié)果在水平中心位置或垂直中心位置。但是,如果錨點(diǎn)的位置變了,那么View縮放后的位置也將發(fā)生變化。
旋轉(zhuǎn)時(shí),將會以錨點(diǎn)所在的軸線為軸進(jìn)行旋轉(zhuǎn),比如:
- 不同錨點(diǎn)setRotation(90)的效果(紅點(diǎn)為錨點(diǎn)位置)
-
不同錨點(diǎn)setRotationY(45)的效果(紅點(diǎn)為錨點(diǎn)位置)
setRotationY(45)
了解以上幾個函數(shù)的效果就可以動手寫動畫了,所謂動畫無非就是像總結(jié)一個函數(shù)一樣,對于任意的position能找到一個特定的屬性與之對應(yīng),這個值可以是透明度、旋轉(zhuǎn)角度、位移等等。
舉個栗子,如果我們想實(shí)現(xiàn)下面這種動畫,只要找出這樣一個對應(yīng)的函數(shù)就可以了。
** 總結(jié)規(guī)律:**
- position<=-1時(shí), translationY = 100
- -1<position<0時(shí), translationY = -position*100(此時(shí)position為負(fù))
- position=0時(shí), translationY = 0
- 0<position<1時(shí), translationY = position*100
- position>=1時(shí), translationY =100;
我們還可以根據(jù)上面的規(guī)律畫成圖像:
有了這樣清晰的概念寫出動畫就不是什么難事了。下面看源碼:
@Override
public void transformPage(View page, float position) {
page.setPivotY(page.getHeight()/2);
float maxTransform = page.getHeight()/4;
if (position <= -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
page.setTranslationY(maxTransform);
} else if (position < 1)
{ // [-1,1]
// Modify the default slide transition to shrink the page as well
if (position < 0)//[0,-1]
{
page.setTranslationY(-position * maxTransform);
} else//[1,0]
{
page.setTranslationY(position * maxTransform);
}
} else
{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setTranslationY(maxTransform);
}
}
再看個復(fù)雜點(diǎn)的
@Override
public void transformPage(View page, float position) {
page.setPivotY(page.getHeight()/2);
float maxRotate = 35f;
float minScale = 0.8f;
float maxTranslationX = page.getWidth()/5;
if (position <= -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
page.setRotationY(maxRotate);
page.setPivotX(0);
page.setScaleX(minScale);
page.setScaleY(minScale);
page.setTranslationX(maxTranslationX);
} else if (position < 1)
{ // [-1,1]
page.setRotationY(-position * maxRotate);
if (position < 0)//[0,-1]
{
page.setPivotX(0);
page.setScaleX(1 + position * (1-minScale));
page.setScaleY(1 + position * (1-minScale));
page.setTranslationX(-position * maxTranslationX);
} else//[1,0]
{
page.setPivotX(page.getWidth());
page.setScaleX(1 - position * (1-minScale));
page.setScaleY(1 - position * (1-minScale));
page.setTranslationX(-position * maxTranslationX);
}
} else
{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setRotationY(-1 * maxRotate);
page.setPivotX(page.getWidth());
page.setScaleX(minScale);
page.setScaleY(minScale);
page.setTranslationX(-maxTranslationX);
}
}
最后啰嗦兩句###
總算寫完了,第一次寫博客,找各種各樣的工具花了好長時(shí)間,錄屏使用的是手機(jī)app錄屏專家,再傳到電腦上使用Free Video to GIF Converter轉(zhuǎn)成gif,斷斷續(xù)續(xù)花了一晚上。如果大家有好的錄屏軟件推薦麻煩給博主留個言,謝謝了!