前言:不知不觉中又加班到了10点半,整个启动页面做了一天多的时间,一共有三个页面,每个页面都有动画效果,动画效果调试起来麻烦,既要跟ios统一,又要匹配各种不同的手机,然后产品经理还有可能在中途改需求,程序员各种苦逼有木有,在这个过程中也学到了蛮多东西的,所以写一篇博客跟大家分享一下.
下载:http://pan.baidu.com/s/1c1v6akw
先看效果图:
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 Listlist = new ArrayList(); 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=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.setBackgroundResource(R.drawable.page_indicator_focused); } else { tips.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 Listlist; public BaseFragmentAdapter(FragmentManager fm, Listlist) { 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点了,办公室一个人敲打着键盘,记录着这两天做过的东西,才发现这也是一件很惬意的事情。。。。闪人。。。回家.
来源:http://www.cnblogs.com/yishaochu/p/5078577.html