直播送花特效
點擊上方“程序員大咖”,選擇“置頂公眾號”
關鍵時刻,第一時間送達!
引言
這是作者第一個CSDN的文章,寫的不好的地方,請大家多多提提意見哈。今天寫的文章是關于動畫的,前幾天公司要求我寫一個觀看直播點擊送花與主播收到花朵的效果。
主播接到花朵的效果?
觀眾送花效果
思路
好了看見了效果之后想必大家都應該知道肯定想到要用Animator(4.0增加的動畫類),想必很多人都接觸到了,但是剛接觸Android的兄弟可能就沒怎么使用這個。網上有很多文章對這個類的介紹,所以這里就不注重解釋了。
主要的思路如下:
1.確定起點(主播是隨機起點)與終點位置;
2.為其添加動畫特效,從起點運動到終點,然后銷毀View。
代碼
我就不拿以前的代碼了,我一邊敲一邊說,首先創建個項目(不多說了哈),然后我們創建一個工具類FlowerAnimation.java。我們就對這個類瘋狂擼吧。
固定起點->固定終點特效實現
? ? private static final String TAG = "FlowerAnimation";
? ? //上下文
? ? private Context mContext;
? ? //生成的View添加在的ViewGroup
? ? private ViewGroup rootView;
? ? private ViewGroup.LayoutParams layoutParams;
? ? //資源文件
? ? private Drawable[] drawables;
? ? //插值器
? ? private Interpolator[] interpolators;
上面代碼的注釋很清楚了,大家一看就明白了 ? ?,TGA這是為了測試打印log使用的(AS快捷鍵-logt+enter)。
? ? public FlowerAnimation(Context mContext, ViewGroup rootView) {
? ? ? ? ?this.mContext = mContext;
? ? ? ? ?this.rootView = rootView;
? ? ? ? ?init();
? ? } ??
? ? private void init() {
? ? ? ? drawables = new Drawable[8];
? ? ? ? drawables[0] = mContext.getResources().getDrawable(R.mipmap.flower_01);
? ? ? ? drawables[1] = mContext.getResources().getDrawable(R.mipmap.flower_02);
? ? ? ? drawables[2] = mContext.getResources().getDrawable(R.mipmap.flower_03);
? ? ? ? drawables[3] = mContext.getResources().getDrawable(R.mipmap.flower_04);
? ? ? ? drawables[4] = mContext.getResources().getDrawable(R.mipmap.flower_05);
? ? ? ? drawables[5] = mContext.getResources().getDrawable(R.mipmap.flower_06);
? ? ? ? drawables[6] = mContext.getResources().getDrawable(R.mipmap.flower_07);
? ? ? ? drawables[7] = mContext.getResources().getDrawable(R.mipmap.flower_08);
? ? ? ? interpolators = new Interpolator[4];
? ? ? ? interpolators[0] = new LinearInterpolator();//線性
? ? ? ? interpolators[1] = new AccelerateInterpolator();//加速
? ? ? ? interpolators[2] = new DecelerateInterpolator();//減速
? ? ? ? interpolators[3] = new AccelerateDecelerateInterpolator();//先加速后減速
? ? ? ? layoutParams = new ViewGroup.LayoutParams(DensityUtil.dip2px(mContext, 50), DensityUtil.dip2px(mContext, 50));
? ? }
上面就是一些初始化的東西了,大家看看就好。
準備工作做好了,我們寫最主要的方法,那就是生成動畫了,上代碼
/**
? ? /**
? ? ?* 開啟動畫
? ? ?*
? ? ?* @param view ? 執行動畫的view
? ? ?* @param startP 起點 如果傳null 默認從view位置開始
? ? ?* @param stopP ?終點
? ? ?*/
? ? public void startAnim(@NonNull final View view, @Nullable PointF startP, @NonNull PointF stopP) {
? ? ? ? if (startP == null) {
? ? ? ? ? ? startP = new PointF(view.getX(), view.getY());
? ? ? ? }
? ? ? ? //透明度變化
? ? ? ? ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(view, "alpha", 0, 1);
? ? ? ? animatorAlpha.setDuration(200);
? ? ? ? //位移動畫
? ? ? ? ObjectAnimator animatorX = ObjectAnimator.ofFloat(view, "translationX", startP.x, stopP.x);
? ? ? ? animatorX.setDuration(1000);
? ? ? ? ObjectAnimator animatorY = ObjectAnimator.ofFloat(view, "translationY", startP.y, stopP.y);
? ? ? ? animatorY.setDuration(1000);
? ? ? ? //生成動畫集合
? ? ? ? AnimatorSet set = new AnimatorSet();
? ? ? ? //開啟透明度動畫然后執行位移動畫
? ? ? ? set.play(animatorAlpha).before(animatorX).with(animatorY);
? ? ? ? //加入植入器
? ? ? ? set.setInterpolator(interpolators[rand.nextInt(interpolators.length)]);
? ? ? ? //添加動畫監聽事件 為了移除view 防止造成oom
? ? ? ? set.addListener(new AnimatorListenerAdapter() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationEnd(Animator animation) {
? ? ? ? ? ? ? ? super.onAnimationEnd(animation);
? ? ? ? ? ? ? ? rootView.removeView(view);
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? set.start();
? ? }
? ? /**
? ? ?* 開啟動畫
? ? ?*
? ? ?* @param view ?執行動畫的view
? ? ?* @param stopP 終點
? ? ?*/
? ? public void startAnim(@NonNull final View view, @NonNull PointF stopP) {
? ? ? ? startAnim(view, null, stopP);
? ? }
? ? /**
? ? ?* 添加花朵
? ? ?*
? ? ?* @param startPoint
? ? ?*/
? ? public void addFlower(@NonNull PointF startPoint, @NonNull PointF stopP) {
? ? ? ? ImageView flower = new ImageView(mContext);
? ? ? ? flower.setX(startPoint.x);
? ? ? ? flower.setY(startPoint.y);
? ? ? ? Drawable drawable = drawables[rand.nextInt(drawables.length)];
? ? ? ? flower.setBackground(drawable);
? ? ? ? rootView.addView(flower, layoutParams);
? ? ? ? startAnim(flower, startPoint, stopP);
? ? }
好了,我們看到,生成這個動畫需要View(這是必然的)終點也是必然,起點就無所謂了。每一步的注釋都寫的很清楚,ObjectAnimator這個類功能很強大(4.0+)。下面開始寫個界面來看看效果啦。界面代碼我直接上代碼 不講解了!對了有關于view位置的問題大家直接去看別的文章吧,很多的哈,我這里貼個我認為不錯的:
http://blog.csdn.net/jason0539/article/details/42743531
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
? ? //終點坐標imageView
? ? private ImageView endFlowerIv;
? ? //開啟動畫按鈕 也是起點坐標
? ? private Button startFlowerBt;
? ? private FlowerAnimation flowerAnimation;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? initView();
? ? }
? ? private void initView() {
? ? ? ? endFlowerIv = (ImageView) findViewById(R.id.main_end_flower_iv);
? ? ? ? startFlowerBt = (Button) findViewById(R.id.main_start_flower_bt);
? ? ? ? startFlowerBt.setOnClickListener(this);
? ? ? ? flowerAnimation = new FlowerAnimation(this, (ViewGroup) findViewById(R.id.activity_main));
? ? }
? ? @Override
? ? public void onClick(View v) {
? ? ? ? flowerAnimation.addFlower(new PointF(v.getX(), v.getY()), new PointF(endFlowerIv.getX(), endFlowerIv.getY()));
? ? }
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:id="@+id/activity_main"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <ImageView
? ? ? ? android:id="@+id/main_end_flower_iv"
? ? ? ? android:layout_width="50dp"
? ? ? ? android:layout_height="50dp"
? ? ? ? android:layout_gravity="center_horizontal"
? ? ? ? android:background="@mipmap/flower_01" />
? ? <Button
? ? ? ? android:id="@+id/main_start_flower_bt"
? ? ? ? android:layout_width="50dp"
? ? ? ? android:layout_height="50dp"
? ? ? ? android:layout_gravity="center_horizontal|bottom"
? ? ? ? android:background="@mipmap/flower_01" />
</FrameLayout>
下面是效果圖
隨即起點->固定終點特效實現
這個問題想必大家在上面的基礎上就完全可以寫出來 直接把代碼貼上來與效果
? ? ?/**
? ? ?* 添加花朵 隨即生成起點(rootView范圍)
? ? ?*
? ? ?* @param stopP 終點
? ? ?*/
? ? public void addFlowerByScope(@NonNull PointF stopP) {
? ? ? ? float x = rand.nextFloat() * rootView.getWidth();
? ? ? ? float y = rand.nextFloat() * rootView.getHeight();
? ? ? ? addFlower(new PointF(x, y), stopP);
? ? }
? ? ?/**
? ? ?* 添加花朵 隨即生成起點
? ? ?*
? ? ?* @param stopP ?終點
? ? ?* @param scopeP 范圍 ?隨即生成的點將會按照此范圍隨即取值
? ? ?*/
? ? public void addFlowerByScope(@NonNull PointF stopP, @NonNull PointF scopeP) {
? ? ? ? float x = rand.nextFloat() * scopeP.x;
? ? ? ? float y = rand.nextFloat() * scopeP.y;
? ? ? ? addFlower(new PointF(x, y), stopP);
? ? }
界面的點擊事件換成對應的方法
@Override
? ? public void onClick(View v) {
? ? ? ? flowerAnimation.addFlowerByScope(new PointF(endFlowerIv.getX(), endFlowerIv.getY()));
? ? }
到此,你會發現已經完成主播接到花朵的效果(就是隨即從各個地方出現花朵飛到花朵出)以上就是主播界面顯示的效果了。代碼比較簡單,下面實現觀眾點擊送花的效果。
思路呢?其實跟上面差不多,觀眾送花的效果類似固定點到固定點的效果(類似哈哈),為什么說類似呢?因為從圖上可以看到,路徑是不同的,很明顯發現 觀眾送花的效果的路徑是隨即的(亂飄)。這里就引出來了ValueAnimator 這個東西你會發現他是ObjectAnimator的父類。
ValueAnimator 顧名思義哈 就是針對數值的動畫,他能幫我完成什么呢?
比如我我想讓一個數值從0-10 時間是10s,我們來寫寫看我們新建一個ValueAnimActivity.java來實現觀眾的界面,順便在里面測試demo。
public class ValueAnimActivity extends AppCompatActivity implements View.OnClickListener {
? ? private TextView countTv;
? ? private Button startBt;
? ? @Override
? ? protected void onCreate(@Nullable Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_value);
? ? ? ? initView();
? ? }
? ? private void initView() {
? ? ? ? countTv = (TextView) findViewById(R.id.value_count_tv);
? ? ? ? startBt = (Button) findViewById(R.id.value_start_bt);
? ? ? ? startBt.setOnClickListener(this);
? ? }
? ? @Override
? ? public void onClick(View v) {
? ? ? ? startValueAnim();
? ? }
? ? private void startValueAnim() {
? ? ? ? //從0-10 時間10s
? ? ? ? ValueAnimator countAnim = ValueAnimator.ofInt(0, 10)
? ? ? ? ? ? ? ? .setDuration(10000);
? ? ? ? countAnim.setInterpolator(new LinearInterpolator());
? ? ? ? countAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
? ? ? ? ? ? ? ? countTv.setText(animation.getAnimatedValue().toString());
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? countAnim.start();
? ? }
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:orientation="vertical">
? ? <TextView
? ? ? ? android:id="@+id/value_count_tv"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_gravity="center"
? ? ? ? android:textSize="30dp" ?/>
? ? <Button
? ? ? ? android:id="@+id/value_start_bt"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_gravity="center_horizontal|bottom"
? ? ? ? android:text="start" />
</FrameLayout>
代碼跟布局都很簡單 我們簡單寫了個ValueAnimator的例子 直接看效果
那怎么實現我們的效果呢?這里就用到一個方法
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
他能讓一個對象進行改變,那怎么改變呢?其實隨便是什么?都要跟時間掛鉤(重要),為什么跟時間掛鉤,這還要解釋么?onAnimationUpdate回調的方法你打印log你會發現他會調用N多次,10S回調了496次,可想而知我們對象改變也會跟時間有關系,那么我們看看TypeEvaluator(類型評估者)這是一個接口,我們來看看它要我們實現的方法
? ?
public T evaluate(float fraction, T startValue, T endValue);
?這個方法看到之后,后面2個我肯定知道是什么意思,動畫的起始值,那第一個是什么?(英文翻譯是分數)咱們說過肯定跟時間有關的,那么這個是不是就是時間呢?看了官方解釋之后,這個意思就是當前完成動畫的百分比。
? ? 還不懂?那好我們還看看官方有沒有默認給我們實現的類,一看,有很多,我們直接拿來一個用看看效果,上代碼
private void startValueAnim1() {
? ? ? ? //從0-10 時間10s
? ? ? ? ValueAnimator countAnim = ValueAnimator.ofObject(new IntEvaluator() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
? ? ? ? ? ? ? ? Log.e(TAG, "evaluate() called with: fraction = [" + fraction + "], startValue = [" + startValue + "], endValue = [" + endValue + "]");
? ? ? ? ? ? ? ? return super.evaluate(fraction, startValue, endValue);
? ? ? ? ? ? }
? ? ? ? }, 0, 10)
? ? ? ? ? ? ? ? .setDuration(10000);
? ? ? ? countAnim.setInterpolator(new LinearInterpolator());
? ? ? ? countAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
? ? ? ? ? ? ? ? countTv.setText(animation.getAnimatedValue().toString());
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? countAnim.start();
? ? }
下面重點來了,如果要實現這種效果,那就要一個公式(貝塞爾曲線)這個當初我也是一頭霧水啊,先不管直接套用公式就行了(先上一個圖片)?
這個公式只要理解我們給出4個點,他就算出當前的點。這里有4個點,但是我們只有2個點,另外2個點是為了控制曲線的走向,我隨即取就可以咯。好了,我們先不管點,先把TypeEvaluator寫好
?/**
? ? ?* 自定義的估值器
? ? ?*/
? ? public static class MyTypeEvaluator implements TypeEvaluator<PointF> {
? ? ? ? private PointF pointF1, pointF2;
? ? ? ? public MyTypeEvaluator(PointF pointF1, PointF pointF2) {
? ? ? ? ? ? this.pointF1 = pointF1;
? ? ? ? ? ? this.pointF2 = pointF2;
? ? ? ? }
? ? ? ? @Override
? ? ? ? public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
? ? ? ? ? ? float timeLeft = 1.0f - fraction;
? ? ? ? ? ? PointF pointF = new PointF();//結果
? ? ? ? ? ? pointF.x = timeLeft * timeLeft * timeLeft * (startValue.x)
? ? ? ? ? ? ? ? ? ? + 3 * timeLeft * timeLeft * fraction * (pointF1.x)
? ? ? ? ? ? ? ? ? ? + 3 * timeLeft * fraction * fraction * (pointF2.x)
? ? ? ? ? ? ? ? ? ? + fraction * fraction * fraction * (endValue.x);
? ? ? ? ? ? pointF.y = timeLeft * timeLeft * timeLeft * (startValue.y)
? ? ? ? ? ? ? ? ? ? + 3 * timeLeft * timeLeft * fraction * (pointF1.y)
? ? ? ? ? ? ? ? ? ? + 3 * timeLeft * fraction * fraction * (pointF2.y)
? ? ? ? ? ? ? ? ? ? + fraction * fraction * fraction * (endValue.y);
? ? ? ? ? ? return pointF;
? ? ? ? }
? ? }
只是簡單的套用公式,就不用多講了直接復制就好,要不然能看的眼花。
下面放上取中間控制點的代碼
注釋也簡單,我覺得很好弄懂,下面上主要代碼
下面是最終效果啦
到這里差不多可以結束了。想必大家可能會問,如果動畫還沒執行完就退出了,那就內存泄漏了啊。所以我再來個方法。
好了,跑起來沒有問題了。
源碼地址:https://github.com/CFlingchen/CSDN1
來自:CSDN-CF凌晨
http://blog.csdn.net/qq_27495349/article/details/53008975
程序員大咖整理發布,轉載請聯系作者獲得授權
【點擊成為Python大神】
總結
- 上一篇: 自动驾驶工具箱简易教程
- 下一篇: 介绍两个ios手机测试的辅助工具