Android 利用ViewPager开发引导页并加入帧动画、属性动画和音乐播放

一、前言

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

功能目录如下:

二、最终效果展示

最终效果如下所示:

三、实现步骤

3.1、引入ViewPager

布局采用 ConstraintLayout 实现,整体还是很简单的,就是一个全屏的 ViewPager加上右上角的音乐播放按钮以及底部的小圆点。



    

    

    

    

    

    

3.2、实现 ViewPager 效果

3.2.1、引入ViewPager的三个子View

三个子View的布局都一样,这里就以一个为例子,子View的布局也很简单,就是一个图片加上一个文字。



    

    

3.2.2、创建ViewPager适配器

public class BasePageAdapter extends PagerAdapter {

    private List mList;

    public BasePageAdapter(List 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 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、效果展示

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

3.3、实现小圆点逻辑

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

监听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) {

    }
});

动态选择小圆点:

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;
    }
}

效果展示:

可以看到,下面的小圆点已经随着我们的滑动而动态的变化了。

3.4、实现帧动画

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

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




    
    
    
    
    
    
    

3.4.2、在布局中引入

3.4.3、开启帧动画

开启帧动画需要获取背景,并将其强转成 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、实现音乐播放

音乐播放是用 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、属性动画

当我们在播放音乐的时候,需要让音乐图片一直旋转,我们这里就用属性动画的 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;
    }

}

在播放音乐的时候开启:

 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();
}

效果展示:

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

四、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 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();
    }

}

五、源码

源码已上传至 github,有需要的可以去下载。