ViewPager切换引导页时播放对应Lottie动画的问题及解决方法
问题
我有一个包含3个引导页的Intro Activity,每个页面都包含一个Lottie动画,期望滑动到任意页面时,该页面的动画自动播放。当前实现滑动页面时出现错误,提示xx等变量未定义,需要正确的实现方式。
主布局 activity_welcome.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:showIn="@layout/activity_welcome"> <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:id="@+id/layoutDots" android:layout_width="match_parent" android:layout_height="@dimen/dots_height" android:layout_alignParentBottom="true" android:layout_marginBottom="@dimen/dots_margin_bottom" android:gravity="center" android:orientation="horizontal" android:visibility="gone"> </LinearLayout> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btnEnter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/black" android:text="ورود" android:fontFamily="@font/koodak" android:textSize="19dp" android:layout_marginBottom="150dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@color/appBlue" android:alpha="0"/> </RelativeLayout>
引导页布局 slide.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" xmlns:app="http://schemas.android.com/apk/res-auto"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/aban" android:text="چت مت! چتِ راحت!" android:textColor="@color/black" android:textSize="25dp" android:layout_centerHorizontal="true" android:layout_marginBottom="10dp" android:layout_above="@id/textView2"/> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/koodak" android:text="یک پیام رسان بدون امکانات اضافه دست و پاگیر..." android:textSize="20dp" android:layout_centerHorizontal="true" android:layout_above="@id/animationView1" /> <com.airbnb.lottie.LottieAnimationView android:id="@+id/animationView1" android:layout_width="229dp" android:layout_height="223dp" android:layout_centerInParent="true" app:lottie_autoPlay="true" app:lottie_fileName="chat.json" app:lottie_loop="false" /> </RelativeLayout> </RelativeLayout>
WelcomeActivity.java 原代码
public class WelcomeActivity extends AppCompatActivity { private ViewPager viewPager; private MyViewPagerAdapter myViewPagerAdapter; private int[] layouts; private PrefManager prefManager; LottieAnimationView xx,xx1,xx2; // LottieAnimationView lottieAnimationView2; @SuppressLint("MissingInflatedId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportActionBar().hide(); // Checking for first time launch - before calling setContentView() prefManager = new PrefManager(this); if (!prefManager.isFirstTimeLaunch()) { launchHomeScreen(); finish(); } // Making notification bar transparent if (Build.VERSION.SDK_INT >= 21) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } setContentView(R.layout.activity_welcome); appCompatButtonEnter = findViewById(R.id.btnEnter); viewPager = (ViewPager) findViewById(R.id.view_pager); // layouts of welcome sliders layouts = new int[]{ R.layout.activity_welcome_slide1, R.layout.activity_welcome_slide2, R.layout.activity_welcome_slide3 }; myViewPagerAdapter = new MyViewPagerAdapter(); viewPager.setAdapter(myViewPagerAdapter); viewPager.addOnPageChangeListener(viewPagerPageChangeListener); } private int getItem(int i) { return viewPager.getCurrentItem() + i; } private void launchHomeScreen() { prefManager.setFirstTimeLaunch(true); startActivity(new Intent(WelcomeActivity.this, MainActivity.class)); finish(); } // viewpager change listener ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { if(position == 0) { View view = viewPager.getChildAt(0); xx = view.findViewById(R.id.animationView1); } xx.playAnimation(); }else if(position == 1){ View view = viewPager.getChildAt(1); xx1 = view.findViewById(R.id.animationView2); flag1 = true; xx1.playAnimation(); }else { View view = viewPager.getChildAt(2); xx2 = view.findViewById(R.id.animationView3); flag2 = true; xx2.playAnimation(); } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; private void changeStatusBarColor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); } } public class MyViewPagerAdapter extends PagerAdapter { private LayoutInflater layoutInflater; public MyViewPagerAdapter() { } @Override public Object instantiateItem(ViewGroup container, int position) { layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(layouts[position], container, false); container.addView(view); return view; } @Override public int getCount() { return layouts.length; } @Override public boolean isViewFromObject(View view, Object obj) { return view == obj; } @Override public void destroyItem(ViewGroup container, int position, Object object) { View view = (View) object; container.removeView(view); } } }
解决方案
问题根源
- 语法错误:
onPageSelected方法中if(position ==0)的代码块闭合错误,导致xx.playAnimation()处于分支外部,非0位置时xx未初始化就被调用,触发变量未定义异常。 - View获取不可靠:
viewPager.getChildAt(position)无法稳定获取页面View,因为ViewPager仅缓存当前页和相邻页,非缓存页的View不存在。 - 未声明变量:
appCompatButtonEnter、flag1、flag2未声明,引发编译错误。 - 自动播放冲突:布局中
app:lottie_autoPlay="true"会导致页面初始化时自动播放,不符合滑动触发的需求。
修复实现
1. 修改引导页布局(移除自动播放)
删除Lottie标签中的app:lottie_autoPlay="true",改为手动控制:
<com.airbnb.lottie.LottieAnimationView android:id="@+id/animationView1" android:layout_width="229dp" android:layout_height="223dp" android:layout_centerInParent="true" app:lottie_fileName="chat.json" app:lottie_loop="false" />
(注:三个引导页的动画ID保持animationView1、animationView2、animationView3各自不变)
2. 修正WelcomeActivity.java
import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import com.airbnb.lottie.LottieAnimationView; import java.util.ArrayList; public class WelcomeActivity extends AppCompatActivity { private ViewPager viewPager; private MyViewPagerAdapter myViewPagerAdapter; private int[] layouts; private PrefManager prefManager; private Button appCompatButtonEnter; // 保存所有页面的View引用,确保可靠获取 private ArrayList<View> pageViews = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportActionBar().hide(); // 首次启动判断 prefManager = new PrefManager(this); if (!prefManager.isFirstTimeLaunch()) { launchHomeScreen(); finish(); return; } // 透明状态栏 if (Build.VERSION.SDK_INT >= 21) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } setContentView(R.layout.activity_welcome); appCompatButtonEnter = findViewById(R.id.btnEnter); viewPager = findViewById(R.id.view_pager); // 引导页布局数组 layouts = new int[]{ R.layout.activity_welcome_slide1, R.layout.activity_welcome_slide2, R.layout.activity_welcome_slide3 }; myViewPagerAdapter = new MyViewPagerAdapter(); viewPager.setAdapter(myViewPagerAdapter); viewPager.addOnPageChangeListener(viewPagerPageChangeListener); // 进入页面时自动播放第一页动画 playAnimationForPosition(0); } private int getItem(int i) { return viewPager.getCurrentItem() + i; } private void launchHomeScreen() { prefManager.setFirstTimeLaunch(true); startActivity(new Intent(WelcomeActivity.this, MainActivity.class)); finish(); } // ViewPager页面切换监听 private final ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { playAnimationForPosition(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; // 统一处理指定位置的动画播放 private void playAnimationForPosition(int position) { if (position >= pageViews.size()) return; View pageView = pageViews.get(position); LottieAnimationView lottie = null; // 根据页面位置匹配对应的Lottie控件 switch (position) { case 0: lottie = pageView.findViewById(R.id.animationView1); break; case 1: lottie = pageView.findViewById(R.id.animationView2); break; case 2: lottie = pageView.findViewById(R.id.animationView3); break; } if (lottie != null) { lottie.cancelAnimation(); // 先取消之前的动画,避免重复播放 lottie.playAnimation(); } } private void changeStatusBarColor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(android.graphics.Color.TRANSPARENT); } } // 自定义ViewPager适配器,保存页面View引用 public class MyViewPagerAdapter extends PagerAdapter { private LayoutInflater layoutInflater; public MyViewPagerAdapter() { layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); } @Override public Object instantiateItem(ViewGroup container, int position) { View view = layoutInflater.inflate(layouts[position], container, false); container.addView(view); pageViews.add(view); // 保存当前页面的View return view; } @Override public int getCount() { return layouts.length; } @Override public boolean isViewFromObject(View view, Object obj) { return view == obj; } @Override public void destroyItem(ViewGroup container, int position, Object object) { View view = (View) object; container.removeView(view); pageViews.remove(view); // 移除销毁页面的View引用 } } }
实现说明
- 通过
pageViews数组保存每个页面的View实例,确保能稳定获取当前页的Lottie控件。 playAnimationForPosition方法统一处理动画逻辑,先取消之前的动画再重新播放,避免重复播放问题。- 修复了原代码中的语法错误和未声明变量问题,确保编译运行正常。
- 移除布局中的自动播放属性,完全由代码控制动画触发时机,符合滑动到页面才播放的需求。
内容的提问来源于stack exchange,提问作者Mohsen Mokhtari
相关产品推荐
相关产品推荐

