一、前言

<font face=黑体>现在很多 App 都有引导页,今天我们就利用 ViewPager 来实现引导页,并且在引导页中加入帧动画属性动画音乐播放等功能。

<font face=黑体>功能目录如下:
目录

二、最终效果展示

<font face=黑体>最终效果如下所示:

效果

三、实现步骤

3.1、引入ViewPager

<font face=黑体>布局采用 ConstraintLayout 实现,整体还是很简单的,就是一个全屏的 ViewPager加上右上角的音乐播放按钮以及底部的小圆点。
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    android:layout_height="match_parent">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/mViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_music_switch"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginTop="30dp"
        android:layout_marginEnd="20dp"
        android:src="@drawable/img_guide_music"
        app:layout_constraintEnd_toStartOf="@+id/tv_guide_skip"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_guide_skip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:text="跳过"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_guide_point_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
        android:src="@drawable/img_guide_point_p"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.45"
        app:layout_constraintStart_toStartOf="parent" />

    <ImageView
        android:id="@+id/iv_guide_point_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
        android:src="@drawable/img_guide_point"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <ImageView
        android:id="@+id/iv_guide_point_3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
        android:src="@drawable/img_guide_point"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.55"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.2、实现 ViewPager 效果

3.2.1、引入ViewPager的三个子View

<font face=黑体>三个子View的布局都一样,这里就以一个为例子,子View的布局也很简单,就是一个图片加上一个文字。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_guide_star"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@drawable/guide_star_anim" />

    <TextView
        android:id="@+id/tv_guide_star"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="身在井隅,心向璀璨"
        android:textSize="18sp" />

</LinearLayout>

3.2.2、创建ViewPager适配器

public class BasePageAdapter extends PagerAdapter {

    private List<View> mList;

    public BasePageAdapter(List<View> mList) {
        this.mList = mList;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(mList.get(position));
        return mList.get(position);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mList.get(position));
    }
}

3.2.3、代码实现

public class MainActivity extends AppCompatActivity {

    private ImageView iv_music_switch;
    private TextView tv_guide_skip;
    private ImageView iv_guide_point_1;
    private ImageView iv_guide_point_2;
    private ImageView iv_guide_point_3;
    private ViewPager mViewPager;

    private View view1;
    private View view2;
    private View view3;

    private List<View> mPageList = new ArrayList<>();
    private BasePageAdapter mPageAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        iv_music_switch = findViewById(R.id.iv_music_switch);
        tv_guide_skip = findViewById(R.id.tv_guide_skip);
        iv_guide_point_1 = findViewById(R.id.iv_guide_point_1);
        iv_guide_point_2 = findViewById(R.id.iv_guide_point_2);
        iv_guide_point_3 = findViewById(R.id.iv_guide_point_3);
        mViewPager = findViewById(R.id.mViewPager);

        view1 = View.inflate(this, R.layout.layout_pager_guide_1, null);
        view2 = View.inflate(this, R.layout.layout_pager_guide_2, null);
        view3 = View.inflate(this, R.layout.layout_pager_guide_3, null);

        mPageList.add(view1);
        mPageList.add(view2);
        mPageList.add(view3);

        //预加载
        mViewPager.setOffscreenPageLimit(mPageList.size());

        mPageAdapter = new BasePageAdapter(mPageList);
        mViewPager.setAdapter(mPageAdapter);
    }
}

3.2.4、效果展示

效果

<font face=黑体>现在我们已经实现类滑动,但是下面的小圆点并没有随着我们的滑动而变化。

3.3、实现小圆点逻辑

<font face=黑体>小圆点的逻辑很简单,只需要去监听 ViewPager 的页面变化,然后动态的改变小圆点的图片就可以了。

<font face=黑体>监听ViewPager的页面变化:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        swlectPoint(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});

<font face=黑体>动态选择小圆点:

private void swlectPoint(int position) {
    switch (position) {
        case 0:
            iv_guide_point_1.setImageResource(R.drawable.img_guide_point_p);
            iv_guide_point_2.setImageResource(R.drawable.img_guide_point);
            iv_guide_point_3.setImageResource(R.drawable.img_guide_point);
            break;
        case 1:
            iv_guide_point_1.setImageResource(R.drawable.img_guide_point);
            iv_guide_point_2.setImageResource(R.drawable.img_guide_point_p);
            iv_guide_point_3.setImageResource(R.drawable.img_guide_point);
            break;
        case 2:
            iv_guide_point_1.setImageResource(R.drawable.img_guide_point);
            iv_guide_point_2.setImageResource(R.drawable.img_guide_point);
            iv_guide_point_3.setImageResource(R.drawable.img_guide_point_p);
            break;
    }
}

<font face=黑体>效果展示:

效果

<font face=黑体>可以看到,下面的小圆点已经随着我们的滑动而动态的变化了。

3.4、实现帧动画

<font face=黑体>帧动画的本质就是将一张张的图片,通过代码对这些图片进行连续的活动,从而形成动画。帧动画的实现可以通过 xml方式 实现和 代码方式 实现,我们这里利用的是 xml方式 来实现的。

3.4.1、在/res/drawable新建animation-list

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item android:drawable="@mipmap/img_star_1" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_2" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_3" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_4" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_5" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_6" android:duration="2000"/>
    <item android:drawable="@mipmap/img_star_7" android:duration="2000"/>

</animation-list>

3.4.2、在布局中引入

<ImageView
    android:id="@+id/iv_guide_smile"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@drawable/guide_smile_anim" />

3.4.3、开启帧动画

<font face=黑体>开启帧动画需要获取背景,并将其强转成 AnimationDrawable。

 //帧动画
iv_guide_star = view1.findViewById(R.id.iv_guide_star);
iv_guide_night = view2.findViewById(R.id.iv_guide_night);
iv_guide_smile = view3.findViewById(R.id.iv_guide_smile);

//播放帧动画
AnimationDrawable animStar = (AnimationDrawable) iv_guide_star.getBackground();
if (!animStar.isRunning()) {
    animStar.start();
}

AnimationDrawable animNight = (AnimationDrawable) iv_guide_night.getBackground();
if (!animNight.isRunning()) {
    animNight.start();
}

AnimationDrawable animSmile = (AnimationDrawable) iv_guide_smile.getBackground();
if (!animSmile.isRunning()) {
    animSmile.start();
}

3.4.4、效果展示

效果

3.5、实现音乐播放

<font face=黑体>音乐播放是用 MediaPlayer实现的,我这里封装了一个 MediaPlayerManager 音乐播放工具类,其实就是对 MediaPlayer 一些 API 的调用,源码中会提供这个工具类,这里就不细讲了,播放的音乐文件是存储在本地 raw 文件夹下的。

/**
* 播放音乐
 */
private void startMusic() {

    mGuideMusic = new MediaPlayerManager();
    mGuideMusic.setLooping(true);
    final AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.guide);
    mGuideMusic.startPlay(file);

    mGuideMusic.setOnComplteionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            mGuideMusic.startPlay(file);
        }
    });
}

3.6、属性动画

<font face=黑体>当我们在播放音乐的时候,需要让音乐图片一直旋转,我们这里就用属性动画的 ObjectAnimator 对象动画来实现。

public class AnimUtils {

    /**
     * 旋转动画
     * @param view
     * @return
     */
    public static ObjectAnimator rotation(View view) {
        ObjectAnimator mAnim = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
        mAnim.setDuration(2 * 1000);
        mAnim.setRepeatMode(ValueAnimator.RESTART);
        mAnim.setRepeatCount(ValueAnimator.INFINITE);
        mAnim.setInterpolator(new LinearInterpolator());
        return mAnim;
    }

}

<font face=黑体>在播放音乐的时候开启:

 private void startMusic() {

   mGuideMusic = new MediaPlayerManager();
    mGuideMusic.setLooping(true);
    final AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.guide);
    mGuideMusic.startPlay(file);

    mGuideMusic.setOnComplteionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            mGuideMusic.startPlay(file);
        }
    });

    //旋转动画
    mAnim = AnimUtils.rotation(iv_music_switch);
    mAnim.start();
}

<font face=黑体>效果展示:

效果

可以看到音乐图片已经在旋转了。

四、MainActivity 完整代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView iv_music_switch;
    private TextView tv_guide_skip;
    private ImageView iv_guide_point_1;
    private ImageView iv_guide_point_2;
    private ImageView iv_guide_point_3;
    private ViewPager mViewPager;

    private View view1;
    private View view2;
    private View view3;

    private List<View> mPageList = new ArrayList<>();
    private BasePageAdapter mPageAdapter;

    private ImageView iv_guide_star;
    private ImageView iv_guide_night;
    private ImageView iv_guide_smile;

    private MediaPlayerManager mGuideMusic;

    private ObjectAnimator mAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        iv_music_switch = findViewById(R.id.iv_music_switch);
        tv_guide_skip = findViewById(R.id.tv_guide_skip);
        iv_guide_point_1 = findViewById(R.id.iv_guide_point_1);
        iv_guide_point_2 = findViewById(R.id.iv_guide_point_2);
        iv_guide_point_3 = findViewById(R.id.iv_guide_point_3);
        mViewPager = findViewById(R.id.mViewPager);

        iv_music_switch.setOnClickListener(this);

        view1 = View.inflate(this, R.layout.layout_pager_guide_1, null);
        view2 = View.inflate(this, R.layout.layout_pager_guide_2, null);
        view3 = View.inflate(this, R.layout.layout_pager_guide_3, null);

        mPageList.add(view1);
        mPageList.add(view2);
        mPageList.add(view3);

        //预加载
        mViewPager.setOffscreenPageLimit(mPageList.size());

        mPageAdapter = new BasePageAdapter(mPageList);
        mViewPager.setAdapter(mPageAdapter);

        //帧动画
        iv_guide_star = view1.findViewById(R.id.iv_guide_star);
        iv_guide_night = view2.findViewById(R.id.iv_guide_night);
        iv_guide_smile = view3.findViewById(R.id.iv_guide_smile);

        //播放帧动画
        AnimationDrawable animStar = (AnimationDrawable) iv_guide_star.getBackground();
        animStar.start();

        AnimationDrawable animNight = (AnimationDrawable) iv_guide_night.getBackground();
        animNight.start();

        AnimationDrawable animSmile = (AnimationDrawable) iv_guide_smile.getBackground();
        animSmile.start();

        //小圆点逻辑
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                seletePoint(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        //歌曲的逻辑
        startMusic();
    }

    /**
     * 播放音乐
     */
    private void startMusic() {

        mGuideMusic = new MediaPlayerManager();
        mGuideMusic.setLooping(true);
        final AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.guide);
        mGuideMusic.startPlay(file);

        mGuideMusic.setOnComplteionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mGuideMusic.startPlay(file);
            }
        });

        //旋转动画
        mAnim = AnimUtils.rotation(iv_music_switch);
        mAnim.start();
    }

    /**
     * 动态选择小圆点
     *
     * @param position
     */
    private void seletePoint(int position) {
        switch (position) {
            case 0:
                iv_guide_point_1.setImageResource(R.drawable.img_guide_point_p);
                iv_guide_point_2.setImageResource(R.drawable.img_guide_point);
                iv_guide_point_3.setImageResource(R.drawable.img_guide_point);
                break;
            case 1:
                iv_guide_point_1.setImageResource(R.drawable.img_guide_point);
                iv_guide_point_2.setImageResource(R.drawable.img_guide_point_p);
                iv_guide_point_3.setImageResource(R.drawable.img_guide_point);
                break;
            case 2:
                iv_guide_point_1.setImageResource(R.drawable.img_guide_point);
                iv_guide_point_2.setImageResource(R.drawable.img_guide_point);
                iv_guide_point_3.setImageResource(R.drawable.img_guide_point_p);
                break;
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.iv_music_switch:
                if (mGuideMusic.MEDIA_STATUS == MediaPlayerManager.MEDIA_STATUS_PAUSE) {
                    mAnim.start();
                    mGuideMusic.continuePlay();
                    iv_music_switch.setImageResource(R.drawable.img_guide_music);
                } else if (mGuideMusic.MEDIA_STATUS == MediaPlayerManager.MEDIA_STATUS_PLAY) {
                    mAnim.pause();
                    mGuideMusic.pausePlay();
                    iv_music_switch.setImageResource(R.drawable.img_guide_music_off);
                }
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mGuideMusic.stopPlay();
    }

}

五、源码

<font face=黑体>源码已上传至 github,有需要的可以去下载。


Maenj_Ba_lah
28 声望7 粉丝

真正的大师,永远怀着一颗学徒的心。