app引导页(背景图片切换加各个页面动画效果)

2023-05-16,,

前言:不知不觉中又加班到了10点半,整个启动页面做了一天多的时间,一共有三个页面,每个页面都有动画效果,动画效果调试起来麻烦,既要跟ios统一,又要匹配各种不同的手机,然后产品经理还有可能在中途改需求,程序员各种苦逼有木有,在这个过程中也学到了蛮多东西的,所以写一篇博客跟大家分享一下.

 

先看效果图:

1.显示三个页面的Activity  用view pager去加载三个fragment实现,控制点点点的切换,监听view pager的切换,控制fragment动画的开始跟结束,重写了view pager,实现了背景图片的移动效果.

    /**
    * 主Activity
    * @author ansen
    * @create time 2015-08-07
    */
    public class KaKaLauncherActivity extends FragmentActivity {
    private GuideViewPager vPager;
    private List<LauncherBaseFragment> list = new ArrayList<LauncherBaseFragment>();
    private BaseFragmentAdapter adapter;
    private ImageView[] tips;
    private int currentSelect;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_luancher_main);
    //初始化点点点控件
    ViewGroup group = (ViewGroup)findViewById(R.id.viewGroup);
    tips = new ImageView[3];
    for (int i = 0; i < tips.length; i++) {
    ImageView imageView = new ImageView(this);
    imageView.setLayoutParams(new LayoutParams(10, 10));
    if (i == 0) {
    imageView.setBackgroundResource(R.drawable.page_indicator_focused);
    } else {
    imageView.setBackgroundResource(R.drawable.page_indicator_unfocused);
    }
    tips[i]=imageView;
    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
    layoutParams.leftMargin = 20;//设置点点点view的左边距
    layoutParams.rightMargin = 20;//设置点点点view的右边距
    group.addView(imageView,layoutParams);
    }
    //获取自定义viewpager 然后设置背景图片
    vPager = (GuideViewPager) findViewById(R.id.viewpager_launcher);
    vPager.setBackGroud(BitmapFactory.decodeResource(getResources(),R.drawable.bg_kaka_launcher));
    /**
    * 初始化三个fragment  并且添加到list中
    */
    RewardLauncherFragment rewardFragment = new RewardLauncherFragment();
    PrivateMessageLauncherFragment privateFragment = new PrivateMessageLauncherFragment();
    StereoscopicLauncherFragment stereoscopicFragment = new StereoscopicLauncherFragment();
    list.add(rewardFragment);
    list.add(privateFragment);
    list.add(stereoscopicFragment);
    adapter = new BaseFragmentAdapter(getSupportFragmentManager(),list);
    vPager.setAdapter(adapter);
    vPager.setOffscreenPageLimit(2);
    vPager.setCurrentItem(0);
    vPager.setOnPageChangeListener(changeListener);
    }
    /**
    * 监听viewpager的移动
    */
    OnPageChangeListener changeListener=new OnPageChangeListener() {
    @Override
    public void onPageSelected(int index) {
    setImageBackground(index);//改变点点点的切换效果
    LauncherBaseFragment fragment=list.get(index);
    list.get(currentSelect).stopAnimation();//停止前一个页面的动画
    fragment.startAnimation();//开启当前页面的动画
    currentSelect=index;
    }
    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {}
    @Override
    public void onPageScrollStateChanged(int arg0) {}
    };
    /**
    * 改变点点点的切换效果
    * @param selectItems
    */
    private void setImageBackground(int selectItems) {
    for (int i = 0; i < tips.length; i++) {
    if (i == selectItems) {
    tips[i].setBackgroundResource(R.drawable.page_indicator_focused);
    } else {
    tips[i].setBackgroundResource(R.drawable.page_indicator_unfocused);
    }
    }
    }
    }

2.重写viewpager   在dispatchDraw方法中控制显示的背景图片区域,

    /**
    * 重写ViewPager  主要做一个切换背景的功能
    * @author ansen
    * @create time 2015-08-07
    */
    public class GuideViewPager extends ViewPager {
    private Bitmap bg;
    private Paint b = new Paint(1);
    public GuideViewPager(Context context) {
    super(context);
    }
    public GuideViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    }
    @Override
    protected void dispatchDraw(Canvas canvas) {
    if (this.bg != null) {
    int width = this.bg.getWidth();
    int height = this.bg.getHeight();
    int count = getAdapter().getCount();
    int x = getScrollX();
    // 子View中背景图片需要显示的宽度,放大背景图或缩小背景图。
    int n = height * getWidth() / getHeight();
    /**
    * (width - n) / (count - 1)表示除去显示第一个ViewPager页面用去的背景宽度,剩余的ViewPager需要显示的背景图片的宽度。
    * getWidth()等于ViewPager一个页面的宽度,即手机屏幕宽度。在该计算中可以理解为滑动一个ViewPager页面需要滑动的像素值。
    * ((width - n) / (count - 1)) /getWidth()也就表示ViewPager滑动一个像素时,背景图片滑动的宽度。
    * x * ((width - n) / (count - 1)) /  getWidth()也就表示ViewPager滑动x个像素时,背景图片滑动的宽度。
    * 背景图片滑动的宽度的宽度可以理解为背景图片滑动到达的位置。
    */
    int w = x * ((width - n) / (count - 1)) / getWidth();
    canvas.drawBitmap(this.bg, new Rect(w, 0, n + w, height), new Rect( x, 0, x + getWidth(), getHeight()), this.b);
    }
    super.dispatchDraw(canvas);
    }
    public void setBackGroud(Bitmap paramBitmap) {
    this.bg = paramBitmap;
    this.b.setFilterBitmap(true);
    }
    }

3.主体布局文件  上面放一个自定义的viewpager  下面放一个显示点点的RelativeLayout

    <RelativeLayout 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" >
    <com.example.view.GuideViewPager
    android:id="@+id/viewpager_launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
    <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <LinearLayout
    android:id="@+id/viewGroup"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="30dp"
    android:gravity="center_horizontal"
    android:orientation="horizontal" />
    </RelativeLayout>
    </RelativeLayout>

4.ViewPager适配器

    /**
    * Viewpager适配器
    * @author apple
    *
    */
    public class BaseFragmentAdapter extends FragmentStatePagerAdapter {
    private List<LauncherBaseFragment>list;
    public BaseFragmentAdapter(FragmentManager fm, List<LauncherBaseFragment> list) {
    super(fm);
    this.list = list;
    }
    public BaseFragmentAdapter(FragmentManager fm) {
    super(fm);
    }
    @Override
    public Fragment getItem(int arg0) {
    return list.get(arg0);
    }
    @Override
    public int getCount() {
    return list.size();
    }
    }

5.Fragment抽象类 有两个抽象方法,开启动画跟停止动画  所有的Fragment都继承这个类  Viewpager切换的时候可以更好的控制每个Fragment开启动画,结束动画

    /**
    * Fragment抽象类
    * @author ansen
    *
    */
    public abstract class LauncherBaseFragment extends Fragment{
    public abstract void  startAnimation();
    public abstract void  stopAnimation();
    }

6.打赏页Fragment  三个动画效果  硬币向下移动动画+打赏图片缩放动画+改变打赏图片透明度然后隐藏图片

    /**
    * 打赏页面
    * @author ansen
    * @create time 2015-08-07
    */
    public class RewardLauncherFragment extends LauncherBaseFragment{
    private ImageView ivReward;
    private ImageView ivGold;
    private Bitmap goldBitmap;
    private boolean started;//是否开启动画(ViewPage滑动时候给这个变量赋值)
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View rooView=inflater.inflate(R.layout.fragment_reward_launcher, null);
    ivGold=(ImageView) rooView.findViewById(R.id.iv_gold);
    ivReward=(ImageView) rooView.findViewById(R.id.iv_reward);
    //获取硬币的高度
    goldBitmap=BitmapFactory.decodeResource(getActivity().getResources(),R.drawable.icon_gold);
    startAnimation();
    return rooView;
    }
    public void startAnimation(){
    started=true;
    //向下移动动画 硬币的高度*2+80
    TranslateAnimation translateAnimation=new TranslateAnimation(0,0,0,goldBitmap.getHeight()*2+80);
    translateAnimation.setDuration(500);
    translateAnimation.setFillAfter(true);
    ivGold.startAnimation(translateAnimation);
    translateAnimation.setAnimationListener(new AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation){
    if(started){
    ivReward.setVisibility(View.VISIBLE);
    //硬币移动动画结束开启缩放动画
    Animation anim=AnimationUtils.loadAnimation(getActivity(),R.anim.reward_launcher);
    ivReward.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener(){
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationRepeat(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation) {
    //缩放动画结束 开启改变透明度动画
    AlphaAnimation alphaAnimation=new AlphaAnimation(1,0);
    alphaAnimation.setDuration(1000);
    ivReward.startAnimation(alphaAnimation);
    alphaAnimation.setAnimationListener(new AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationRepeat(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation) {
    //透明度动画结束隐藏图片
    ivReward.setVisibility(View.GONE);
    }
    });
    }
    });
    }
    }
    @Override
    public void onAnimationRepeat(Animation animation) {}
    });
    }
    @Override
    public void stopAnimation(){
    started=false;//结束动画时标示符设置为false
    ivGold.clearAnimation();//清空view上的动画
    }
    }

7.私信页面   四个动画效果   并且四个动画都相同,其实只要我们实现了一个,其他的基本都很容易了.   依次实现四个图片的放大然后还原

    /**
    * 私信
    * @author ansen
    */
    public class PrivateMessageLauncherFragment extends LauncherBaseFragment{
    private ImageView ivLikeVideo,ivThinkReward,ivThisWeek,ivWatchMovie;
    private Animation likeAnimation,thinkAnimation,watchAnimation,thisWeekAnimation;
    private boolean started;//是否开启动画
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View rooView=inflater.inflate(R.layout.fragment_private_message_launcher, null);
    ivLikeVideo=(ImageView) rooView.findViewById(R.id.iv_private_message_like_video);
    ivThinkReward=(ImageView) rooView.findViewById(R.id.iv_private_message_think_reward);
    ivWatchMovie=(ImageView) rooView.findViewById(R.id.iv_private_message_watch_movie);
    ivThisWeek=(ImageView) rooView.findViewById(R.id.private_message_this_week);
    return rooView;
    }
    public void stopAnimation(){
    //动画开启标示符设置成false
    started=false;
    /**
    * 清空所有控件上的动画
    */
    ivLikeVideo.clearAnimation();
    ivThinkReward.clearAnimation();
    ivWatchMovie.clearAnimation();
    ivThisWeek.clearAnimation();
    }
    public void startAnimation(){
    started=true;
    /**
    * 每次开启动画前先隐藏控件
    */
    ivLikeVideo.setVisibility(View.GONE);
    ivThinkReward.setVisibility(View.GONE);
    ivWatchMovie.setVisibility(View.GONE);
    ivThisWeek.setVisibility(View.GONE);
    new Handler().postDelayed(new Runnable() {//延时0.5秒之后开启喜欢视频动画
    @Override
    public void run(){
    if(started)
    likeVideoAnimation();
    }
    },500);
    }
    /**
    * 好喜欢你的视频
    */
    private void likeVideoAnimation(){
    ivLikeVideo.setVisibility(View.VISIBLE);
    likeAnimation = AnimationUtils.loadAnimation(getActivity(),R.anim.private_message_launcher);
    ivLikeVideo.startAnimation(likeAnimation);//开启动画
    likeAnimation.setAnimationListener(new AnimationListener(){
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationRepeat(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation) {//监听动画结束
    if(started)
    thinkReward();
    }
    });
    }
    /**
    * 谢谢你的打赏
    */
    private void thinkReward(){
    ivThinkReward.setVisibility(View.VISIBLE);
    thinkAnimation = AnimationUtils.loadAnimation(getActivity(),R.anim.private_message_launcher);
    ivThinkReward.startAnimation(thinkAnimation);
    thinkAnimation.setAnimationListener(new AnimationListener(){
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationRepeat(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation) {
    if(started)
    watchMovie();
    }
    });
    }
    /**
    * 一起看个电影呗
    */
    private void watchMovie(){
    ivWatchMovie.setVisibility(View.VISIBLE);
    watchAnimation = AnimationUtils.loadAnimation(getActivity(),R.anim.private_message_launcher);
    ivWatchMovie.startAnimation(watchAnimation);
    watchAnimation.setAnimationListener(new AnimationListener(){
    @Override
    public void onAnimationStart(Animation animation) {}
    @Override
    public void onAnimationRepeat(Animation animation) {}
    @Override
    public void onAnimationEnd(Animation animation) {
    if(started)
    thisWeek();
    }
    });
    }
    /**
    * 好啊  这周末有空
    */
    private void thisWeek(){
    ivThisWeek.setVisibility(View.VISIBLE);
    thisWeekAnimation = AnimationUtils.loadAnimation(getActivity(),R.anim.private_message_launcher);
    ivThisWeek.startAnimation(thisWeekAnimation);
    }
    }

8.最后一个引导页  就两个动画  图片的放大跟缩小,其实用xml布局的话一个动画就能搞定,跟私信页面的动画差不多.小伙伴写的代码.这里换了一种方式.代码比较多.

    /**
    * 最后一个
    * @author apple
    */
    public class StereoscopicLauncherFragment extends LauncherBaseFragment implements OnClickListener{
    private static final float ZOOM_MAX = 1.3f;
    private static final  float ZOOM_MIN = 1.0f;
    private ImageView imgView_immediate_experience;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View rooView=inflater.inflate(R.layout.fragment_stereoscopic_launcher, null);
    imgView_immediate_experience=(ImageView) rooView.findViewById(R.id.imgView_immediate_experience);
    imgView_immediate_experience.setOnClickListener(this);
    return rooView;
    }
    public void playHeartbeatAnimation(){
    /**
    * 放大动画
    */
    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(new ScaleAnimation(ZOOM_MIN, ZOOM_MAX, ZOOM_MIN, ZOOM_MAX, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f));
    animationSet.addAnimation(new AlphaAnimation(1.0f, 0.8f));
    animationSet.setDuration(500);
    animationSet.setInterpolator(new AccelerateInterpolator());
    animationSet.setFillAfter(true);
    animationSet.setAnimationListener(new AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
    }
    @Override
    public void onAnimationEnd(Animation animation) {
    /**
    * 缩小动画
    */
    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(new ScaleAnimation(ZOOM_MAX, ZOOM_MIN, ZOOM_MAX,ZOOM_MIN, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f));
    animationSet.addAnimation(new AlphaAnimation(0.8f, 1.0f));
    animationSet.setDuration(600);
    animationSet.setInterpolator(new DecelerateInterpolator());
    animationSet.setFillAfter(false);
    // 实现心跳的View
    imgView_immediate_experience.startAnimation(animationSet);
    }
    });
    // 实现心跳的View
    imgView_immediate_experience.startAnimation(animationSet);
    }
    @Override
    public void onClick(View v) {
    //      Intent intent = new Intent();
    //      intent.setClass(getActivity(),MainActivity.class);
    //      startActivity(intent);
    //      getActivity().finish();
    }
    @Override
    public void startAnimation() {
    playHeartbeatAnimation();
    }
    @Override
    public void stopAnimation() {
    }
    }

最后总结:以上就是三个引导页的核心代码了,还有一些布局文件,动画效果的布局文件我就不一一贴出来的,大家可以去下载我的源码,在这个过程中碰到的几个大的问题说明一下.

1.viewpager切换的时候要结束上个fragment的动画   我是通过boolean变量去控制的

2.背景图片移动的效果    之前自己走了很多弯路,后面在网上找了一个demo拿过来用了.因为大家都有开源精神所以这里省了很多功夫

3.图片放大缩小以前居然不知道一个xml动画布局就能搞定.之前一直想办法用两个动画实现

看看时间一篇博客写了一个半小时,都12点了,办公室一个人敲打着键盘,记录着这两天做过的东西,才发现这也是一件很惬意的事情。。。。闪人。。。回家.
推荐下自己创建的android QQ群:202928390 欢迎大家的加入.

点击下载源码

如果你想第一时间看我们的后期文章,扫码关注公众号,每个周末都会推送Android开发实战教程一篇,其余时间我们会推出一些互联网行业新闻,你还等什么,赶快关注吧,既能学到技术,还能长逼格,出任ceo,赢取白富美。。。。。 

app引导页(背景图片切换加各个页面动画效果)的相关教程结束。

《app引导页(背景图片切换加各个页面动画效果).doc》

下载本文的Word格式文档,以方便收藏与打印。