preface
The guide page of JINGdong Home APP is commendable. The illustration + dynamic effect succinctly and vividly illustrates several highlights of the APP that attract users most (lots of goods, low price, fast delivery…). . This article mainly analyzes the disassembly of these animation effects, and completes a high imitation Demo, the complete Demo code can be obtained at the end of the article.
First take a look at the animation effect of jingdong Home APP guide page, as follows:
Functional analysis
The analysis results are based on decompilation of the APP and my own understanding (what else can I do? I’m too hard, hahaha)
- Shallow background image + four guide pages, each page after the animation is finished automatically slide to the next page. Implementation: With ViewPager+4 views, each page corresponds to a View, a View contains a LottieAnimationView, in the page to listen to the playing of Lottie animation, after the completion of playing automatically switch to the next page, ViewPager uses Alibaba open source UltraViewPager
The background image | Guide the figure 1 | Guide the figure 2 | Guide the figure 3 | Guide the figure 4 |
---|---|---|---|---|
- Page swiping has a rotating animation effect, which can be customized by the PageTransformer interface in ViewPager. Title has a transparency gradient effect, which is implemented with the property animation ObjectAnimator
- When sliding to the last page, a button with “Experience now” text appears, popping up from the bottom and changing transparency from 0 to 1, when sliding forward from the last page, the button disappears from the bottom and changing transparency from 1 to 0, i.e. displacement + transparency change animation, You can do this using a property animation ObjectAnimator
Layout analysis
Use UIAutomatorViewer to view the layout file as follows:
- The root layout is ConstraintLayout, the child node is ViewPager, and the XML file is as follows:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.tmall.ultraviewpager.UltraViewPager
android:id="@+id/viewpager"
android:layout_width="0.0 dip"
android:layout_height="0.0 dip"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivJump"
android:layout_width="46.0 dip"
android:layout_height="0.0 dip"
android:layout_marginRight="20.0 dip"
android:background="@drawable/pdj_guide_jump"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="63:28.5"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.07" />
<ImageView
android:id="@+id/iv_start"
android:layout_width="104.29999 dip"
android:layout_height="30.669983 dip"
android:background="@drawable/pdj_guide_button"
android:focusable="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
- There are four pages in the boot page, and each page has a Lottie file for LottieAnimationView. The XML file is as follows:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/pdj_guide_lottie_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
Resource analysis
- The resource file is stored in the SRC /main/assets directory. The configuration file is lottieconfig. json as follows:
{
"radius": 2020."totalPageNum": 4."lottieBgName": "guide_lottie_bg"."pageAnimTime": 350."buttonAnimTime": 350."lottieTitle": [{"lottieName": "title_lottie_0"
},
{
"lottieName": "title_lottie_1"
},
{
"lottieName": "title_lottie_2"
},
{
"lottieName": "title_lottie_3"}]."lottieMain": [{"lottieName": "main_lottie_0"
},
{
"lottieName": "main_lottie_1"
},
{
"lottieName": "main_lottie_2"
},
{
"lottieName": "main_lottie_3"."repeatInterval": {
"start": 0.288."end": 1}}}]Copy the code
- Radius – used when defining PageTransformer to determine the maximum rotation Angle
- TotalPageNum – Number of boot pages
- LottieBgName – The Lottie file name for the background image
- PageAnimTime – The sliding time to switch between ViewPager pages
- ButtonAnimTime – How long the “Experience now” button on the last page will be animated with displacement and transparency
- LottieTitle – The Lottie file name for each boot page title
- LottieMain – The Lottie file name for each boot page content
Code implementation
Based on the above analysis, it is relatively simple to implement in code, and part of the implementation code is posted here
- Load local Lottie ZIP files. Lottie supports loading local and remote JSON and ZIP files. Usually we put Lottie files in the SRC /main/ Assets directory
fun loadAssetsLottieZipFile(context: Context, lottieImageView:LottieAnimationView, fileName:String,repeatCount:Int= LottieDrawable.INFINITE,autoPlay:Boolean=true){
val lottieCompose= if(fileName.endsWith(".zip")){
LottieCompositionFactory.fromAssetSync(context, fileName).value
}
else{
LottieCompositionFactory.fromAssetSync(context, fileName.plus(".zip")).value
}
lottieImageView.progress=0.0 f
lottieImageView.repeatCount=repeatCount
lottieImageView.setComposition(lottieCompose!!)
if(autoPlay){
lottieImageView.playAnimation()
}
}
Copy the code
- Add dot indicator
viewpager.initIndicator() viewpager.indicator.setOrientation(UltraViewPager.Orientation.HORIZONTAL) .setNormalIcon(drawableToBitmap(ContextCompat.getDrawable(applicationContext, R.drawable.guide_white_dot)!!) ) .setFocusIcon(drawableToBitmap(ContextCompat.getDrawable(applicationContext, R.drawable.guide_dark_dot)!!) ) .setIndicatorPadding(ScreenHelper.dp2px(applicationContext,5.0 F))
.setMargin(0.0.0, ScreenHelper.dp2px(applicationContext, 20.0 F))
viewpager.indicator.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
viewpager.indicator.build()
Copy the code
DrawableToBitmap (drawableToBitmap)
/**
* Drawable转换成Bitmap
*/
private fun drawableToBitmap(drawable: Drawable): Bitmap {
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0.0, drawable.intrinsicWidth, drawable.intrinsicHeight)
drawable.draw(canvas)
return bitmap
}
Copy the code
-
Custom PageTransformer to achieve page switch rotation effect
- When position < -1, rotate to the maximum Angle with the rotation center at the lower right corner;
- -1 < position < 0, the closer position is to 0, the smaller the rotation Angle is, and the rotation center is closer to the edge center;
- When 0 <= position <= 1, the closer position is to 0, the smaller the rotation Angle is, and the rotation center is closer to the edge center;
- When position > 1, rotate to the maximum Angle with the rotation center at the lower left corner.
The code looks like this:
import android.content.Context
import android.view.View
import androidx.viewpager.widget.ViewPager
import com.kongpf.commonhelper.ScreenHelper
import kotlin.math.atan2
class GuideTransformer(context: Context, private val mRadius: Int) : ViewPager.PageTransformer {
private var mMaxRotate = 0f
init {
if (mRadius > 0) {
mMaxRotate = (2.0 * Math.toDegrees(atan2((ScreenHelper.getScreenWidth(context) / 2).toDouble(), mRadius.toDouble()))).toFloat()
}
}
override fun transformPage(page: View, position: Float) {
if (mRadius == 0) {
return
}
when {
position < -1.0 f -> {
page.rotation = -1.0 f * mMaxRotate
page.pivotX = page.width.toFloat()
page.pivotY = page.height.toFloat()
}
position <= 1.0 f- > {if (position < 0.0 f) {
page.pivotX = page.width * (0.5 f + 0.5 f * -position)
page.pivotY = page.height.toFloat()
page.rotation = position * mMaxRotate
} else {
page.pivotX = 0.5 f * page.width * (1.0 f - position)
page.pivotY = page.height.toFloat()
page.rotation = position * mMaxRotate
}
}
else -> {
page.rotation = mMaxRotate
page.pivotX = 0f
page.pivotY = page.height.toFloat()
}
}
}
}
Copy the code
Full code address
Github.com/kongpf8848/…