前一段时间感觉直播很火,因此我就下了七八个直播软件去看了,不要问我为啥下这么多,我会告诉你我想看看哪个平台的妹子颜值高吗,好吧最终喜欢上看映客的一个妹子。妹子歌唱的很好听可惜屌丝的我涮不起礼物,只能在屏幕下方狂点赞了。好了这次当然不是讨论直播平台的妹子,而是看下直播软件点赞的效果怎样实现的。
说一下思想吧,首先利用属性动画的放大效果让生成的图片不至于太突兀的显示,接着让生成的图片沿着三阶贝塞尔曲线的轨迹去移动,在移动的过程中逐渐设置图片的透明效果至0,最后在动画结束的时候remove掉新生成的图片。
下面看下效果图吧
效果就是这么个效果,下面看具体实现吧
首先看下布局文件
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jh.mymathsintext.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="@color/colorPrimary"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="@string/app_name">
</android.support.v7.widget.Toolbar>
<com.jh.mymathsintext.MyRelative
android:id="@+id/rela"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_marginBottom="100dp"
android:layout_below="@id/toolbar"
android:gravity="bottom|center_horizontal">
</com.jh.mymathsintext.MyRelative>
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pl_blue"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="50dp"/>
</RelativeLayout>
布局文件没什么好讲的,接着看下具体实现,先把整体代码贴出来在分步细讲
public class MyRelative extends RelativeLayout{
private int []drawable={R.drawable.pl_blue,R.drawable.pl_red,R.drawable.pl_yellow};
private LayoutParams params;
private Random random=new Random();
private int height,width;
public MyRelative(Context context) {
super(context);
init();
}
public MyRelative(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyRelative(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 获取图片的大小
*/
public void init(){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.pl_red,options);
width=options.outWidth;
height=options.outHeight;
// width=ContextCompat.getDrawable(getContext(),R.drawable.pl_red).getIntrinsicWidth();
// height=ContextCompat.getDrawable(getContext(),R.drawable.pl_red).getIntrinsicHeight();
params=new LayoutParams(width, height);
}
/**
* 生成图片,并设置动画
*/
public void addView(){
final ImageView imageView=new ImageView(getContext());
imageView.setLayoutParams(params);
imageView.setBackgroundResource(drawable[random.nextInt(3)]);
addView(imageView);
AnimatorSet set=getAnimation(imageView);//属性动画控制放大效果
ValueAnimator valueAnimator=setPoint(imageView);//控制移动
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playSequentially(set,valueAnimator);//设置属性动画按顺序执行
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
removeView(imageView);
}
});
animatorSet.start();
}
/**
* 属性动画控制放大效果
* @param imageView
* @return
*/
public AnimatorSet getAnimation(ImageView imageView){
ObjectAnimator objectAnimator1=ObjectAnimator.ofFloat(imageView,SCALE_X,0f,1f);
ObjectAnimator objectAnimator2=ObjectAnimator.ofFloat(imageView,SCALE_Y,0f,1f);
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playTogether(objectAnimator1,objectAnimator2);//控制动画一起执行
animatorSet.setDuration(500);
return animatorSet;
}
/**
* 创建贝塞尔轨迹并让图片沿着贝塞尔轨迹运行
* @param imageView
* @return
*/
public ValueAnimator setPoint(final View imageView){
MyPoint myPoint=new MyPoint(getPoint(2),getPoint(1));
ValueAnimator valueAnimator=ValueAnimator.ofObject(myPoint,new PointF((this.getWidth()-width)/2,this.getHeight()-height),
new PointF(random.nextInt(getWidth()),0));
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF= (PointF) animation.getAnimatedValue();
Log.i("tag","dfdgdfadfsdsdd"+pointF.x);
imageView.setX(pointF.x);
imageView.setY(pointF.y);
imageView.setAlpha(1-animation.getAnimatedFraction());
}
});
return valueAnimator;
}
/**
* 随机生成点
* @param height
* @return
*/
public PointF getPoint(int height){
PointF pointF=new PointF();
pointF.x=random.nextInt(getWidth()-50);
pointF.y=random.nextInt(getHeight()-50)/height;
return pointF;
}
}
首先需要获取一下图片的大小然后设置给新创建的imageview
/**
* 获取图片的大小
*/
public void init(){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.pl_red,options);
width=options.outWidth;
height=options.outHeight;
// width=ContextCompat.getDrawable(getContext(),R.drawable.pl_red).getIntrinsicWidth();
// height=ContextCompat.getDrawable(getContext(),R.drawable.pl_red).getIntrinsicHeight();
params=new LayoutParams(width, height);
}
然后就是给新创建的imageview设置放大动画了,这里利用属性动画实现
/**
* 属性动画控制放大效果
* @param imageView
* @return
*/
public AnimatorSet getAnimation(ImageView imageView){
ObjectAnimator objectAnimator1=ObjectAnimator.ofFloat(imageView,SCALE_X,0f,1f);
ObjectAnimator objectAnimator2=ObjectAnimator.ofFloat(imageView,SCALE_Y,0f,1f);
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playTogether(objectAnimator1,objectAnimator2);//控制动画一起执行
animatorSet.setDuration(500);
return animatorSet;
}
接下来就是要让新生成的imageview实现沿着三阶贝塞尔曲线轨迹去移动了,重点是获取三阶贝塞尔曲线的轨迹坐标,这个可以利用实现TypeEvaluator接口和PointF实现。这是会重写TypeEvaluator接口里的evaluate()方法,看下方法实现吧
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
float timeDel=1-fraction;
PointF pointF=new PointF();
//三阶贝塞尔曲线公式
pointF.x=timeDel*timeDel*timeDel*startValue.x+3*pointF1.x*timeDel*timeDel*fraction+3*pointF2.x*fraction*fraction*timeDel+
endValue.x*fraction*fraction*fraction;
pointF.y=timeDel*timeDel*timeDel*startValue.y+3*pointF1.y*timeDel*timeDel*fraction+3*pointF2.y*fraction*fraction*timeDel+
endValue.y*fraction*fraction*fraction;
return pointF;
}
对就是在上述方法里实现获取三阶贝塞尔曲线轨迹坐标,先看下贝塞尔曲线的公式
三阶贝塞尔曲线公式如上,因为evaluate()方法里有起始点和终点,只需要在创建两个点就可以了,为了让每一个生成的imageview运行轨迹大致不相同,我们可以随机生成这两个点,代码如下所示
/**
* 随机生成点
* @param height
* @return
*/
public PointF getPoint(int height){
PointF pointF=new PointF();
pointF.x=random.nextInt(getWidth()-50);
pointF.y=random.nextInt(getHeight()-50)/height;
return pointF;
}
接下来需要做的事情就是将轨迹坐标赋给imageview,代码如下
/**
* 创建贝塞尔轨迹并让图片沿着贝塞尔轨迹运行
* @param imageView
* @return
*/
public ValueAnimator setPoint(final View imageView){
MyPoint myPoint=new MyPoint(getPoint(2),getPoint(1));
ValueAnimator valueAnimator=ValueAnimator.ofObject(myPoint,new PointF((this.getWidth()-width)/2,this.getHeight()-height),
new PointF(random.nextInt(getWidth()),0));
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF= (PointF) animation.getAnimatedValue();
Log.i("tag","dfdgdfadfsdsdd"+pointF.x);
imageView.setX(pointF.x);
imageView.setY(pointF.y);
imageView.setAlpha(1-animation.getAnimatedFraction());
}
});
return valueAnimator;
}
然后需要做的就是将放大动画和轨迹动画设置给imageview去实现即可,
/**
* 生成图片,并设置动画
*/
public void addView(){
final ImageView imageView=new ImageView(getContext());
imageView.setLayoutParams(params);
imageView.setBackgroundResource(drawable[random.nextInt(3)]);
addView(imageView);
AnimatorSet set=getAnimation(imageView);//属性动画控制放大效果
ValueAnimator valueAnimator=setPoint(imageView);//控制移动
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playSequentially(set,valueAnimator);//设置属性动画按顺序执行
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
removeView(imageView);
}
});
animatorSet.start();
}
注意的一点是在动画结束的时候最好将生成的imageview给移除掉,到此为止点赞效果就做出来了,别拦我,我要去跟女神表白了