1. 程式人生 > >Android自定義view之實現仿抖音雙擊點贊單擊暫停特效

Android自定義view之實現仿抖音雙擊點贊單擊暫停特效

       2018年抖音、快手、火山等短視訊App比較火,最近自己做短視訊專案時有個需求,就是類似抖音的點贊特效,單擊螢幕時視訊暫停,再次點選時視訊恢復播放,雙擊或者連續多次點選時出現點贊特效(飄小心心特效),而且是全屏可以隨意點選,都是可以響應雙擊及多擊事件。我們的需求點贊效果出現的同時請求介面,所以還是遇到很多問題的,這裡記錄一下.先放上一張抖音的點贊效果截圖如下:

:

分析一下效果圖:

1.當手指單擊螢幕時,視訊暫停播放.

2.當連續點選螢幕時,響應雙擊事件出現小心心特效,而且點選次數可以大於2次.

3.單擊暫停視訊時同時可以響應雙擊事件互不衝突.

4.點選整個螢幕都可以響應單擊和雙擊事件.

5.多次點選時間間隔要設定在短時間內不響應,要不會出現多次響應,重複點贊(我們的需求是雙擊或者多擊時請求點贊介面).

實現過程如下:

1.自定義點贊view

/**

* 作者: njb

* 時間: 2018/9/20 0020-上午 11:44

* 描述: 雙擊點贊心形動畫

* 來源:

*/

public class Like extends RelativeLayout {
private Context mContext;
float[] num = {-30, -20, 0, 20, 30};//隨機心形圖片角度
//記錄上一次的點選時間
private long  lastClickTime = 0;
//點選的時間間隔
private long INTERVAL = 200;
private MyClickListener.MyClickCallBack onClickListener;

public Like(Context context) {
    super(context);
    initView(context);
}

public Like(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    initView(context);
}

public Like(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView(context);
}

private void initView(Context context) {
    mContext = context;
}

@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            //獲取點選時間
            long currTime = System.currentTimeMillis();
            //判斷點選之間的時間差
            long  interval = currTime - lastClickTime;
            lastClickTime = currTime;
            if(interval <INTERVAL ){//小於1秒,攔截事件,並做處理
                final ImageView imageView = new ImageView(mContext);
                //設定展示的位置,需要在手指觸控的位置上方,即觸控點是心形的右下角的位置
                LayoutParams params = new LayoutParams(300, 300);
                params.leftMargin = (int) event.getX() - 150;
                params.topMargin = (int) event.getY() - 300;
                //設定圖片資源
                imageView.setImageDrawable(getResources().getDrawable(R.mipmap.sp_dianzanhou_da));
                imageView.setLayoutParams(params);
                //把IV新增到父佈局當中
                addView(imageView);

                //設定控制元件的動畫
                AnimatorSet animatorSet = new AnimatorSet();
                //縮放動畫,X軸2倍縮小至0.9倍
                animatorSet.play(scale(imageView, "scaleX", 2f, 0.9f, 100, 0))
                        //縮放動畫,Y軸2倍縮放至0.9倍
                        .with(scale(imageView, "scaleY", 2f, 0.9f, 100, 0))
                        //旋轉動畫,隨機旋轉角
                        .with(rotation(imageView, 0, 0, num[new Random().nextInt(4)]))
                        //漸變透明動畫,透明度從0-1
                        .with(alpha(imageView, 0, 1, 100, 0))
                        //縮放動畫,X軸0.9倍縮小至
                        .with(scale(imageView, "scaleX", 0.9f, 1, 50, 150))
                        //縮放動畫,Y軸0.9倍縮放至
                        .with(scale(imageView, "scaleY", 0.9f, 1, 50, 150))
                        //位移動畫,Y軸從0上移至600
                        .with(translationY(imageView, 0, -600, 800, 400))
                        //透明動畫,從1-0
                        .with(alpha(imageView, 1, 0, 300, 400))
                        //縮放動畫,X軸1至3倍
                        .with(scale(imageView, "scaleX", 1, 3f, 700, 400))
                        //縮放動畫,Y軸1至3倍
                        .with(scale(imageView, "scaleY", 1, 3f, 700, 400));
                //開始動畫
                animatorSet.start();
                //設定動畫結束監聽
                animatorSet.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        //當動畫結束以後,需要把控制元件從父佈局移除
                        removeViewInLayout(imageView);
                    }
                });
            }
            break;
    }
    return super.dispatchTouchEvent(event);
}

/**
 * 縮放動畫
 * @param view
 * @param propertyName
 * @param from
 * @param to
 * @param time
 * @param delayTime
 * @return
 */
public static ObjectAnimator scale(View view, String propertyName, float from, float to, long time, long delayTime) {
    ObjectAnimator translation = ObjectAnimator.ofFloat(view
            , propertyName
            , from, to);
    translation.setInterpolator(new LinearInterpolator());
    translation.setStartDelay(delayTime);
    translation.setDuration(time);
    return translation;
}

/**
 * 位移動畫
 * @param view
 * @param from
 * @param to
 * @param time
 * @param delayTime
 * @return
 */
public static ObjectAnimator translationX(View view, float from, float to, long time, long delayTime) {
    ObjectAnimator translation = ObjectAnimator.ofFloat(view
            , "translationX"
            , from, to);
    translation.setInterpolator(new LinearInterpolator());
    translation.setStartDelay(delayTime);
    translation.setDuration(time);
    return translation;
}

public static ObjectAnimator translationY(View view, float from, float to, long time, long delayTime) {
    ObjectAnimator translation = ObjectAnimator.ofFloat(view
            , "translationY"
            , from, to);
    translation.setInterpolator(new LinearInterpolator());
    translation.setStartDelay(delayTime);
    translation.setDuration(time);
    return translation;
}

/**
 * 透明度動畫
 * @param view
 * @param from
 * @param to
 * @param time
 * @param delayTime
 * @return
 */
public static ObjectAnimator alpha(View view, float from, float to, long time, long delayTime) {
    ObjectAnimator translation = ObjectAnimator.ofFloat(view
            , "alpha"
            , from, to);
    translation.setInterpolator(new LinearInterpolator());
    translation.setStartDelay(delayTime);
    translation.setDuration(time);
    return translation;
}

public static ObjectAnimator rotation(View view, long time, long delayTime, float... values) {
    ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "rotation", values);
    rotation.setDuration(time);
    rotation.setStartDelay(delayTime);
    rotation.setInterpolator(new TimeInterpolator() {
        @Override
        public float getInterpolation(float input) {
            return input;
        }
    });
    return rotation;
}

public void setOnClickListener(MyClickListener.MyClickCallBack onClickListener) {
    this.onClickListener = onClickListener;
}

public MyClickListener.MyClickCallBack getOnClickListener() {
    return onClickListener;
}

}

2.事件監聽類MyClickListener

 

/**

* 作者: njb

* 時間: 2018/11/30 18:18

* 描述:單擊和多次點選事件監聽回撥

* 來源:

*/

public class MyClickListener implements View.OnTouchListener {
    private static int timeout=400;//雙擊間四百毫秒延時
    private int clickCount = 0;//記錄連續點選次數
    private Handler handler;
    private MyClickCallBack myClickCallBack;

    public interface MyClickCallBack{
        void oneClick();//點選一次的回撥
        void doubleClick();//雙擊及連續多次點選的回撥
    }


    public MyClickListener(MyClickCallBack myClickCallBack) {
        this.myClickCallBack = myClickCallBack;
        handler = new Handler();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            clickCount++;
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (clickCount == 1) {
                        myClickCallBack.oneClick();
                    }else if(clickCount>=2){
                        myClickCallBack.doubleClick();
                    }
                    handler.removeCallbacksAndMessages(null);
                    //清空handler延時,並防記憶體洩漏
                    clickCount = 0;//計數清零
                }
            },timeout);//延時timeout後執行run方法中的程式碼
        }
        return false;//讓點選事件繼續傳播,方便再給View新增其他事件監聽
    }
}

3.佈局檔案程式碼:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/banner">
    <com.example.administrator.timertask.view.Like
        android:id="@+id/like"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.example.administrator.timertask.view.Like>
</android.support.constraint.ConstraintLayout>

 

4.Activity程式碼:

/**
 * 作者: njb
 * 時間: 2018/12/13 14:55
 * 描述:
 * 來源:
 */
public class LikeActivity extends AppCompatActivity {
    private Like like;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_like);
        initView();
    }

    private void initView() {
        like = findViewById(R.id.like);
        like.setOnClickListener(new MyClickListener.MyClickCallBack() {
            @Override
            public void oneClick() {
                Toast.makeText(LikeActivity.this,"單擊事件",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void doubleClick() {
                Toast.makeText(LikeActivity.this,"雙擊或多擊事件",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

5.以上就是單擊擊和雙擊及多擊事件監聽和點贊動畫自定義view實現,由於時間問題後面會加上視訊播放、單擊暫停等,慢慢完善例子,並給出原始碼,寫得不好,還望大家見諒,有問題大家可以提出一起探討解決.