Android自定義view之實現仿抖音雙擊點贊單擊暫停特效
阿新 • • 發佈:2019-01-03
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實現,由於時間問題後面會加上視訊播放、單擊暫停等,慢慢完善例子,並給出原始碼,寫得不好,還望大家見諒,有問題大家可以提出一起探討解決.