Android——仿QQ聊天撒花特效
2016-04-05 09:46
597 查看
实现这样的效果,你要知道贝塞尔曲线,何谓贝塞尔曲线?其实就是曲线,嘿嘿,关于曲线的概念大家可以去 Android绘图机制(二)——自定义View绘制形,圆形,三角形,扇形,椭圆,曲线,文字和图片的坐标讲解 中看下,我们这里就直接写了
1.activity_main.xml
<relativelayoutxmlns: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"> //撒花的区域 <relativelayout android:id="@+id/rlt_animation_layout" android:layout_width="match_parent" android:layout_height="match_parent"></relativelayout> <button android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentbottom="true" android:layout_centerhorizontal="true" android:layout_marginbottom="23dp" android:text="开始撒花"> </button> </relativelayout>
2.Fllower
packagecom.lgl.test; importandroid.graphics.Bitmap; importandroid.graphics.Path; importjava.io.Serializable; publicclassFllowerimplementsSerializable{ privatestaticfinallongserialVersionUID=1L; privateBitmapimage; privatefloatx; privatefloaty; privatePathpath; privatefloatvalue; publicBitmapgetResId(){ returnimage; } publicvoidsetResId(Bitmapimg){ this.image=img; } publicfloatgetX(){ returnx; } publicvoidsetX(floatx){ this.x=x; } publicfloatgetY(){ returny; } publicvoidsetY(floaty){ this.y=y; } publicPathgetPath(){ returnpath; } publicvoidsetPath(Pathpath){ this.path=path; } publicfloatgetValue(){ returnvalue; } publicvoidsetValue(floatvalue){ this.value=value; } @Override publicStringtoString(){ return"Fllower[x="+x+",y="+y+",path="+path+",value=" +value+"]"; } }
3.FllowerAnimation 动画类
packagecom.lgl.test; importjava.util.ArrayList; importjava.util.List; importjava.util.Random; importandroid.animation.ObjectAnimator; importandroid.animation.ValueAnimator; importandroid.animation.ValueAnimator.AnimatorUpdateListener; importandroid.content.Context; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.graphics.Canvas; importandroid.graphics.Paint; importandroid.graphics.Path; importandroid.graphics.PathMeasure; importandroid.util.Log; importandroid.util.TypedValue; importandroid.view.View; importandroid.view.WindowManager; importandroid.view.animation.AccelerateInterpolator; /** *撒花用到的知识点:1、android属性动画2、Path路径绘制3、贝塞尔曲线 */ publicclassFllowerAnimationextendsViewimplementsAnimatorUpdateListener{ /** *动画改变的属性值 */ privatefloatphase1=0f; privatefloatphase2=0f; privatefloatphase3=0f; /** *小球集合 */ privateList<fllower>fllowers1=newArrayList<fllower>(); privateList<fllower>fllowers2=newArrayList<fllower>(); privateList<fllower>fllowers3=newArrayList<fllower>(); /** *动画播放的时间 */ privateinttime=4000; /** *动画间隔 */ privateintdelay=400; int[]ylocations={-100,-50,-25,0}; /** *资源ID */ //privateintresId=R.drawable.fllower_love; publicFllowerAnimation(Contextcontext){ super(context); init(context); //this.resId=resId; } @SuppressWarnings("deprecation") privatevoidinit(Contextcontext){ WindowManagerwm=(WindowManager)context .getSystemService(Context.WINDOW_SERVICE); width=wm.getDefaultDisplay().getWidth(); height=(int)(wm.getDefaultDisplay().getHeight()*3/2f); mPaint=newPaint(); mPaint.setAntiAlias(true); //mPaint.setStrokeWidth(2); //mPaint.setColor(Color.BLUE); //mPaint.setStyle(Style.STROKE); pathMeasure=newPathMeasure(); builderFollower(fllowerCount,fllowers1); builderFollower(fllowerCount,fllowers2); builderFollower(fllowerCount,fllowers3); } /** *宽度 */ privateintwidth=0; /** *高度 */ privateintheight=0; /** *曲线高度个数分割 */ privateintquadCount=10; /** *曲度 */ privatefloatintensity=0.2f; /** *第一批个数 */ privateintfllowerCount=4; /** *创建花 */ privatevoidbuilderFollower(intcount,List<fllower>fllowers){ intmax=(int)(width*3/4f); intmin=(int)(width/4f); Randomrandom=newRandom(); for(inti=0;i<count;i++){ ints=random.nextInt(max)%(max-min+1)+min; Pathpath=newPath(); CPointCPoint=newCPoint(s,ylocations[random.nextInt(3)]); List<cpoint>points=builderPath(CPoint); drawFllowerPath(path,points); Fllowerfllower=newFllower(); fllower.setPath(path); Bitmapbitmap=BitmapFactory.decodeResource(getResources(), R.drawable.lift_flower); fllower.setResId(bitmap); fllowers.add(fllower); } } /** *画曲线 * *@parampath *@parampoints */ privatevoiddrawFllowerPath(Pathpath,List<cpoint>points){ if(points.size()>1){ for(intj=0;j<points.size();j++){ CPointpoint=points.get(j); if(j==0){ CPointnext=points.get(j+1); point.dx=((next.x-point.x)*intensity); point.dy=((next.y-point.y)*intensity); }elseif(j==points.size()-1){ CPointprev=points.get(j-1); point.dx=((point.x-prev.x)*intensity); point.dy=((point.y-prev.y)*intensity); }else{ CPointnext=points.get(j+1); CPointprev=points.get(j-1); point.dx=((next.x-prev.x)*intensity); point.dy=((next.y-prev.y)*intensity); } //createthecubic-splinepath if(j==0){ path.moveTo(point.x,point.y); }else{ CPointprev=points.get(j-1); path.cubicTo(prev.x+prev.dx,(prev.y+prev.dy),point.x -point.dx,(point.y-point.dy),point.x,point.y); } } } } /** *曲线摇摆的幅度 */ privateintrange=(int)TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP,70,getResources() .getDisplayMetrics()); /** *画路径 * *@parampoint *@return */ privateList<cpoint>builderPath(CPointpoint){ List<cpoint>points=newArrayList<cpoint>(); Randomrandom=newRandom(); for(inti=0;i<quadCount;i++){ if(i==0){ points.add(point); }else{ CPointtmp=newCPoint(0,0); if(random.nextInt(100)%2==0){ tmp.x=point.x+random.nextInt(range); }else{ tmp.x=point.x-random.nextInt(range); } tmp.y=(int)(height/(float)quadCount*i); points.add(tmp); } } returnpoints; } /** *画笔 */ privatePaintmPaint; /** *测量路径的坐标位置 */ privatePathMeasurepathMeasure=null; @Override protectedvoidonDraw(Canvascanvas){ super.onDraw(canvas); drawFllower(canvas,fllowers1); drawFllower(canvas,fllowers2); drawFllower(canvas,fllowers3); } /** *高度往上偏移量,把开始点移出屏幕顶部 */ privatefloatdy=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40,getResources().getDisplayMetrics()); /** *@paramcanvas *@paramfllowers */ privatevoiddrawFllower(Canvascanvas,List<fllower>fllowers){ for(Fllowerfllower:fllowers){ float[]pos=newfloat[2]; //canvas.drawPath(fllower.getPath(),mPaint); pathMeasure.setPath(fllower.getPath(),false); pathMeasure.getPosTan(height*fllower.getValue(),pos,null); //canvas.drawCircle(pos[0],pos[1],10,mPaint); canvas.drawBitmap(fllower.getResId(),pos[0],pos[1]-dy,null); } } ObjectAnimatormAnimator1; ObjectAnimatormAnimator2; ObjectAnimatormAnimator3; publicvoidstartAnimation(){ if(mAnimator1!=null&&mAnimator1.isRunning()){ mAnimator1.cancel(); } mAnimator1=ObjectAnimator.ofFloat(this,"phase1",0f,1f); mAnimator1.setDuration(time); mAnimator1.addUpdateListener(this); mAnimator1.start(); mAnimator1.setInterpolator(newAccelerateInterpolator(1f)); if(mAnimator2!=null&&mAnimator2.isRunning()){ mAnimator2.cancel(); } mAnimator2=ObjectAnimator.ofFloat(this,"phase2",0f,1f); mAnimator2.setDuration(time); mAnimator2.addUpdateListener(this); mAnimator2.start(); mAnimator2.setInterpolator(newAccelerateInterpolator(1f)); mAnimator2.setStartDelay(delay); if(mAnimator3!=null&&mAnimator3.isRunning()){ mAnimator3.cancel(); } mAnimator3=ObjectAnimator.ofFloat(this,"phase3",0f,1f); mAnimator3.setDuration(time); mAnimator3.addUpdateListener(this); mAnimator3.start(); mAnimator3.setInterpolator(newAccelerateInterpolator(1f)); mAnimator3.setStartDelay(delay*2); } /** *跟新小球的位置 * *@paramvalue *@paramfllowers */ privatevoidupdateValue(floatvalue,List<fllower>fllowers){ for(Fllowerfllower:fllowers){ fllower.setValue(value); } } /** *动画改变回调 */ @Override publicvoidonAnimationUpdate(ValueAnimatorarg0){ updateValue(getPhase1(),fllowers1); updateValue(getPhase2(),fllowers2); updateValue(getPhase3(),fllowers3); Log.i(tag,getPhase1()+""); invalidate(); } publicfloatgetPhase1(){ returnphase1; } publicvoidsetPhase1(floatphase1){ this.phase1=phase1; } publicfloatgetPhase2(){ returnphase2; } publicvoidsetPhase2(floatphase2){ this.phase2=phase2; } publicfloatgetPhase3(){ returnphase3; } publicvoidsetPhase3(floatphase3){ this.phase3=phase3; } privateStringtag=this.getClass().getSimpleName(); privateclassCPoint{ publicfloatx=0f; publicfloaty=0f; /** *x-axisdistance */ publicfloatdx=0f; /** *y-axisdistance */ publicfloatdy=0f; publicCPoint(floatx,floaty){ this.x=x; this.y=y; } } }
4.MainActivity
packagecom.lgl.test; importandroid.app.Activity; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; importandroid.widget.RelativeLayout; publicclassMainActivityextendsActivity{ privateButtonbtn_start; //撒花特效 privateRelativeLayoutrlt_animation_layout; privateFllowerAnimationfllowerAnimation; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //撒花初始化 rlt_animation_layout=(RelativeLayout)findViewById(R.id.rlt_animation_layout); rlt_animation_layout.setVisibility(View.VISIBLE); fllowerAnimation=newFllowerAnimation(this); RelativeLayout.LayoutParamsparams=newRelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); fllowerAnimation.setLayoutParams(params); rlt_animation_layout.addView(fllowerAnimation); btn_start=(Button)findViewById(R.id.btn_start); btn_start.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //开始撒花 fllowerAnimation.startAnimation(); } }); } }
相关文章推荐
- Android Studio第一次启动的Fetching android sdk component information加载问题
- android中的color使用总结
- Android 避免内存溢出的方案总结
- Android中资源文件的Shape使用总结
- android系统方法裁剪图片 华为手机显示为圆
- Android 内存监测工具 DDMS --> Heap
- android快速入门
- Android 仿新版QQ的tab下面拖拽标记为已读的效果
- Android 自定义View 三板斧之三——重写View来实现全新控件
- Android APK反编译详解
- android学习路线
- Android入门——Bitmap和BitmapFactory
- android 开发小工具
- Android 字体使用dp单位避免设置系统字体大小对排版的影响
- Android回到底部和返回顶部实现
- Android studio中导入第三方类库
- Android 常用系统服务 学习总结
- Android 消息循环机制源码分析
- Android 有些机型hint不显示
- Android 中进程、线程的概念