效果圖
基本原理
要實現如上展示的兩種效果,我們需要兩個東西來幫助實現——clipChildren和PagerTransformer
1)clipChildren
這個屬性見得不多,可能很多小夥伴不熟悉,這個屬性是個布爾值,clip中文爲裁剪的意思,clipChildren即爲裁剪孩子(硬核翻譯)。一般修飾在ViewGroup上,它可以表示是否限制處於容器內部的子控件可以越界繪製,默認爲true。這麼說還是有點懵逼,直接看圖吧。
相信大家經常見到閒魚這種樣式的底部Tab欄,那麼這是如何實現的呢,肯定有人想過用佈局嵌套並設置底層佈局背景透明的這種方法,這種方法的確可行,可是稍嫌麻煩,如果是使用clipChildren,則要簡單許多。
比如,我現在實現一個類似效果的Tab欄,不使用clipChildren是這樣的。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
...>
<LinearLayout
android:layout_height="60dp"
...>
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@mipmap/ic_launcher" />
<ImageView
... />
<ImageView
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_gravity="bottom"
android:layout_weight="1"
android:src="@mipmap/ic_launcher" />
<ImageView
... />
<ImageView
.../>
</LinearLayout>
</RelativeLayout>
可以看見,中間的tab明顯被裁減了,那麼加上clipChildren屬性呢?
android:clipChildren="false"
當我們爲根節點設置clipChildren爲false時,神奇的一幕出現了,中間的tab竟然長出頭了(滑稽)。通過這個例子,你應該瞭解了clipChildren的作用了。
2)PagerTransformer
PagerTransformer是ViewPager暴露給我們的一個接口,它內部含有一個transformPage(view:View,position:Float)方法,通過這個方法我們可以拿它的兩個參數針對view實現各種切換效果。這裏着重講一下方法的第二個參數,它表示每個view的下標,但是注意它是一個Float類型的變量,它不像我們以往見的下標那樣,1就是1,2就是2,它是會變的,動態的。剛纔爲0的下標,滑動過後可能會變成1或-1,也就是在[1-,0],[0,1]之間變化,0並不是他的最小值並且當前展示的view下標永遠爲0。
話不多說,上圖
就記住一句話,對於下標,當前展示永爲0,左爲負遞減,右爲正遞增(總結的我都想給自己個贊。。)。
對於PagerTransformer,官方有倆個實現,有興趣的可以去看看。傳送門
動手實現
爲了實現文章開篇兩幅圖的效果,首先搞定vp單屏顯示多頁效果。
<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:clipChildren="false"
android:gravity="center_horizontal"
tools:context=".MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:overScrollMode="never"
android:layout_width="240dp"
android:clipChildren="false"
android:layout_height="wrap_content" />
</LinearLayout>
因爲vp默認只顯示一頁內容,所以需要兩個根節點都要加上clipChildren屬性。如果需要每頁有間隔的可以通過代碼設置
view_pager.pageMargin = 15
畫廊效果
private const val MIN_SCALE_Y = 0.75f
private const val MIN_SCALE_X = 0.95f
private const val MIN_ALPHA = 0.7f
class MyTransform : ViewPager.PageTransformer {
override fun transformPage(view: View, position: Float) {
view.apply {
when {
position < -1 -> { // [-∞,-1)
// 屏幕左邊的view
alpha = MIN_ALPHA
scaleX = MIN_SCALE_X
scaleY = MIN_SCALE_Y
}
position <= 1 -> { // [-1,1]
//X/Y方向上的縮放比例,跟隨下標變化,介於分別的最小值和1之間
val scaleFactorY = (MIN_SCALE_Y + (1 - MIN_SCALE_Y) * (1 - Math.abs(position)))
val scaleFactorX = (MIN_SCALE_X + (1 - MIN_SCALE_X) * (1 - Math.abs(position)))
scaleX = scaleFactorX
scaleY = scaleFactorY
// 隨着下標變化的透明度
alpha = (MIN_ALPHA + ((1 - Math.abs(position)) * (1 - MIN_ALPHA)))
}
else -> { // (1,+∞]
// 屏幕右邊的view
alpha = MIN_ALPHA
scaleX = MIN_SCALE_X
scaleY = MIN_SCALE_Y
}
}
}
}
}
屏幕兩側看不到的view的透明度和縮放大小永遠是固定的,避免它們從兩側出現時大小和透明度的突然變化帶來的落差感。屏幕顯示的view的透明度和大小隨着手指切換時在一個區間動態改變。
側旋式效果
private const val MIN_SCALE = 0.75f
class DashTransform : ViewPager.PageTransformer {
override fun transformPage(view: View, position: Float) {
view.apply {
val pageWidth = width
when {
position < -1 -> { // [-∞,-1)
alpha = 0f
}
position <= 0 -> { // [-1,0] 移動到左側的view
alpha = 1f - Math.abs(position)
scaleX = 1f
scaleY = 1f
rotation = 30 * position
}
position <= 1 -> { // (0,1] 從右側移動過來的view
alpha = 1 - position
// 抵消掉原來的位移,讓view固定住
translationX = pageWidth * -position
// 縮放漸變大小
val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))
scaleX = scaleFactor
scaleY = scaleFactor
}
else -> { // (1,+∞]
alpha = 0f
}
}
}
}
}
向左移動的view透明度隨着下標逐漸透明,並且加了一個30度以內的旋轉,原本從右側移動過來的view被固定在了向左移動的view的下面,因爲代碼設置的位移抵消掉了它原本的移動距離,使它看起來像是一隻固定在一個位置。
最後
其實很多炫酷的效果,乍一看以爲很難,其實無非就是通過旋轉、位移、縮放、漸變組合起來實現的,只要找到合適的地方和方法,合理利用組合就能實現非常棒的效果,看了這篇文章,對vp的切換效果感興趣的小夥伴可以動手實現一個自己的效果,學以致用,加深對動畫的理解。
擴展
基於本次博客的內容,我們還可以再次添加修改,定製實現一個帶切換效果的banner,集體內容就看下篇博客吧~