亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android貝塞爾曲線實現消息拖拽消失

2019-10-21 21:26:31
字體:
來源:轉載
供稿:網友

寫在前頭

寫消息拖拽效果的文章不少,但是大部分都把自定義View寫死了,我們要實現的是傳入一個View,每個View都可以實現拖拽消失爆炸的效果,當然我也是站在巨人的肩膀上來學習的。但個人覺得程序員本就應該敢于學習和借鑒。

源碼地址:源碼Github地址

效果圖

Android,貝塞爾曲線,消息拖拽

 分析(用到的知識點): 

(1)ValueAnimator (數值生成器) 用于生成數值,可以設置差值器來改變數字的變化幅度。

(2)ObjectAnimator (動畫生成器) 用于生成各種屬性,布局動畫,同樣也可以設置差值器來改變效果。

(3)貝塞爾一階曲線

(4)自定義View的基礎知識

(5)WindowManager 使view拖拽能顯示在整個屏幕的任何地方,而不是局限于父布局內

具體實現方法

一、首先我們要實現基礎效果

基礎效果是點擊屏幕任意一點能出現消息拖拽的效果,但是此時我們不用管我們拖動的View,只需要完成大致模型。該部分的難點在于貝塞爾一階曲線的怎么實現。

基礎效果圖

Android,貝塞爾曲線,消息拖拽

 分析:

(1)點擊任意一點畫出兩個圓,和一個有貝塞爾曲線組成的path路徑

(2)隨著拖動距離的增加原點的圓半徑逐漸縮小,當距離達到一定大以后原點的圓和貝塞爾曲線組成的path不再顯示

貝塞爾曲線的畫法

Android,貝塞爾曲線,消息拖拽

首先我們需要求出角a的大小,根據角a來求到A,B,C,D的坐標位子,然后求到控制點E點的坐標,通過Path.quadTo()方法來連接A,B和C,D兩條貝塞爾曲線。

各點坐標

A(c1.x+sina*c1半徑,c1.y-cina*c1半徑)

B(c2.x+sina*c2半徑,c2.y-cina*c2半徑)

C(c2.x-sina*c1半徑,c2.y+cina*c1半徑)

D(c1.x-sina*c2半徑,c1.y+cina*c2半徑)

E ((c1.x+c2.x)/2,(c1.y+c2.y)/2)

貝塞爾曲線的path代碼

private Path getBezeierPath() {  double distance = getDistance(mBigCirclePoint,mLittleCirclePoint);   mLittleCircleRadius = (int) (mLittleCircleRadiusMax - distance / 10);  if (mLittleCircleRadius < mLittleCircleRadiusMin) {   // 超過一定距離 貝塞爾和固定圓都不要畫了   return null;  }   Path bezeierPath = new Path();   // 求角 a  // 求斜率  float dy = (mBigCirclePoint.y-mLittleCirclePoint.y);  float dx = (mBigCirclePoint.x-mLittleCirclePoint.x);  float tanA = dy/dx;  // 求角a  double arcTanA = Math.atan(tanA);   // A  float Ax = (float) (mLittleCirclePoint.x + mLittleCircleRadius*Math.sin(arcTanA));  float Ay = (float) (mLittleCirclePoint.y - mLittleCircleRadius*Math.cos(arcTanA));   // B  float Bx = (float) (mBigCirclePoint.x + mBigCircleRadius*Math.sin(arcTanA));  float By = (float) (mBigCirclePoint.y - mBigCircleRadius*Math.cos(arcTanA));   // C  float Cx = (float) (mBigCirclePoint.x - mBigCircleRadius*Math.sin(arcTanA));  float Cy = (float) (mBigCirclePoint.y + mBigCircleRadius*Math.cos(arcTanA));   // D  float Dx = (float) (mLittleCirclePoint.x - mLittleCircleRadius*Math.sin(arcTanA));  float Dy = (float) (mLittleCirclePoint.y + mLittleCircleRadius*Math.cos(arcTanA));     // 拼裝 貝塞爾的曲線路徑  bezeierPath.moveTo(Ax,Ay); // 移動  // 兩個點  PointF controlPoint = getControlPoint();  // 畫了第一條 第一個點(控制點,兩個圓心的中心點),終點  bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);   // 畫第二條  bezeierPath.lineTo(Cx,Cy); // 鏈接到  bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);  bezeierPath.close();   return bezeierPath; }

 二、完善代碼

 這部分我們需要完善所有代碼,實現代碼的分離,使得所用View都能被拖動,且需要創建一個監聽器來監聽View是否拖動結束了,結束后調用回調方法以便需要做其他處理。

需要完成的功能:

(1)將傳入的View畫出來

(2)在手指抬起時判斷是爆炸還是回彈

(3)完成回彈和爆炸的代碼部分

(4)回彈或者爆炸結束后調用回調通知動畫結束

(5)使用WindowManager把自定義拖拽View加進去,隱藏原來得View實現View在任意地方拖動

完整代碼部分

(1)自定義View的代碼

public class MsgDrafitingView extends View{  private PointF mLittleCirclePoint; private PointF mBigCirclePoint; private Paint mPaint; //大圓半徑 private int mBigCircleRadius = 10; //小圓半徑 private int mLittleCircleRadiusMax = 10; private int mLittleCircleRadiusMin = 2; private int mLittleCircleRadius; private Bitmap dragBitmap; private OnToucnUpListener mOnToucnUpListener;   public MsgDrafitingView(Context context) {  this(context,null); }  public MsgDrafitingView(Context context, @Nullable AttributeSet attrs) {  this(context, attrs,0); }  public MsgDrafitingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  mBigCircleRadius = dip2px(mBigCircleRadius);  mLittleCircleRadiusMax = dip2px(mLittleCircleRadiusMax);  mLittleCircleRadiusMin = dip2px(mLittleCircleRadiusMin);  mPaint = new Paint();  mPaint.setColor(Color.RED);  mPaint.setAntiAlias(true);  mPaint.setDither(true); }  private int dip2px(int dip) {  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics()); }  @Override protected void onDraw(Canvas canvas) {  if (mBigCirclePoint == null || mLittleCirclePoint == null) {   return;  }  //畫大圓  canvas.drawCircle(mBigCirclePoint.x, mBigCirclePoint.y, mBigCircleRadius, mPaint);  //獲得貝塞爾路徑  Path bezeierPath = getBezeierPath();  if (bezeierPath!=null) {   // 小到一定層度就不見了(不畫了)   canvas.drawCircle(mLittleCirclePoint.x, mLittleCirclePoint.y, mLittleCircleRadius, mPaint);   // 畫貝塞爾曲線   canvas.drawPath(bezeierPath, mPaint);  }  // 畫圖片  if (dragBitmap != null) {   canvas.drawBitmap(dragBitmap, mBigCirclePoint.x - dragBitmap.getWidth() / 2,     mBigCirclePoint.y - dragBitmap.getHeight() / 2, null);  } }  private Path getBezeierPath() {  double distance = getDistance(mBigCirclePoint,mLittleCirclePoint);   mLittleCircleRadius = (int) (mLittleCircleRadiusMax - distance / 10);  if (mLittleCircleRadius < mLittleCircleRadiusMin) {   // 超過一定距離 貝塞爾和固定圓都不要畫了   return null;  }   Path bezeierPath = new Path();   // 求角 a  // 求斜率  float dy = (mBigCirclePoint.y-mLittleCirclePoint.y);  float dx = (mBigCirclePoint.x-mLittleCirclePoint.x);  float tanA = dy/dx;  // 求角a  double arcTanA = Math.atan(tanA);   // A  float Ax = (float) (mLittleCirclePoint.x + mLittleCircleRadius*Math.sin(arcTanA));  float Ay = (float) (mLittleCirclePoint.y - mLittleCircleRadius*Math.cos(arcTanA));   // B  float Bx = (float) (mBigCirclePoint.x + mBigCircleRadius*Math.sin(arcTanA));  float By = (float) (mBigCirclePoint.y - mBigCircleRadius*Math.cos(arcTanA));   // C  float Cx = (float) (mBigCirclePoint.x - mBigCircleRadius*Math.sin(arcTanA));  float Cy = (float) (mBigCirclePoint.y + mBigCircleRadius*Math.cos(arcTanA));   // D  float Dx = (float) (mLittleCirclePoint.x - mLittleCircleRadius*Math.sin(arcTanA));  float Dy = (float) (mLittleCirclePoint.y + mLittleCircleRadius*Math.cos(arcTanA));     // 拼裝 貝塞爾的曲線路徑  bezeierPath.moveTo(Ax,Ay); // 移動  // 兩個點  PointF controlPoint = getControlPoint();  // 畫了第一條 第一個點(控制點,兩個圓心的中心點),終點  bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);   // 畫第二條  bezeierPath.lineTo(Cx,Cy); // 鏈接到  bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);  bezeierPath.close();   return bezeierPath; } /**  * 獲得控制點距離  */ public PointF getControlPoint() {  return new PointF((mLittleCirclePoint.x+mBigCirclePoint.x)/2,(mLittleCirclePoint.y+mBigCirclePoint.y)/2); }  /**  * 獲得兩點之間的距離  */ private double getDistance(PointF point1, PointF point2) {  return Math.sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)); }  /**  * 綁定View  */ public static void attach(View view, MsgDrafitingListener.BubbleDisappearListener disappearListener) {  view.setOnTouchListener(new MsgDrafitingListener(view.getContext(),disappearListener)); }  public void initPoint(float x, float y) {  mBigCirclePoint = new PointF(x,y);  mLittleCirclePoint = new PointF(x,y); }  public void updatePoint(float x,float y) {  mBigCirclePoint.x = x;  mBigCirclePoint.y = y;  invalidate(); }  public void setDragBitmap(Bitmap dragBitmap) {  this.dragBitmap = dragBitmap; }  public void setOnToucnUpListener(OnToucnUpListener listener) {  mOnToucnUpListener = listener; }  public interface OnToucnUpListener {  // 還原  void restore();  // 消失爆炸  void dismiss(PointF pointF); }  /**  * 處理手指抬起后的操作  */ public void OnTouchUp() {  if (mLittleCircleRadius > mLittleCircleRadiusMin) {   // 回彈 ValueAnimator 值變化的動畫 0 變化到 1   ValueAnimator animator = ObjectAnimator.ofFloat(1);   animator.setDuration(250);   final PointF start = new PointF(mBigCirclePoint.x, mBigCirclePoint.y);   final PointF end = new PointF(mLittleCirclePoint.x, mLittleCirclePoint.y);   animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    @Override    public void onAnimationUpdate(ValueAnimator animation) {     float percent = (float) animation.getAnimatedValue();// 0 - 1     PointF pointF = Utils.getPointByPercent(start, end, percent);     //更新位子     updatePoint(pointF.x, pointF.y);    }   });   // 設置一個差值器 在結束的時候回彈   animator.setInterpolator(new OvershootInterpolator(3f));   animator.start();   // 還要通知 TouchListener   animator.addListener(new AnimatorListenerAdapter() {    @Override    public void onAnimationEnd(Animator animation) {     if(mOnToucnUpListener != null){      mOnToucnUpListener.restore();     }    }   });  } else {   // 爆炸   if(mOnToucnUpListener != null){    mOnToucnUpListener.dismiss(mBigCirclePoint);   }  } }}

 (2)自定義OnTouchListenner的代碼

public class MsgDrafitingListener implements View.OnTouchListener {  private WindowManager mWindowManager; private WindowManager.LayoutParams params; private MsgDrafitingView mMsgDrafitingView; private Context context; // 爆炸動畫 private FrameLayout mBombFrame; private ImageView mBombImage; private BubbleDisappearListener mDisappearListener;  public MsgDrafitingListener(Context context,BubbleDisappearListener disappearListener) {  mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  params = new WindowManager.LayoutParams();  mMsgDrafitingView = new MsgDrafitingView(context);  //背景透明  params.format = PixelFormat.TRANSPARENT;  this.context = context;   mBombFrame = new FrameLayout(context);  mBombImage = new ImageView(context);  mBombImage.setLayoutParams(new FrameLayout.LayoutParams(Utils.dip2px(30,context),    Utils.dip2px(30,context)));  mBombFrame.addView(mBombImage);  this.mDisappearListener = disappearListener; }   @Override public boolean onTouch(final View view, MotionEvent motionEvent) {   switch (motionEvent.getAction())  {   case MotionEvent.ACTION_DOWN:    //隱藏自己    view.setVisibility(View.INVISIBLE);    mWindowManager.addView(mMsgDrafitingView,params);    int[] location = new int[2];    view.getLocationOnScreen(location);    Bitmap bitmap = getBitmapByView(view);    //y軸需要減去狀態欄的高度    mMsgDrafitingView.initPoint(location[0] + view.getWidth() / 2,      location[1]+view.getHeight()/2 -Utils.getStatusBarHeight(context));    // 給消息拖拽設置一個Bitmap    mMsgDrafitingView.setDragBitmap(bitmap);    //設置OnTouchUpListener    mMsgDrafitingView.setOnToucnUpListener(new MsgDrafitingView.OnToucnUpListener() {     @Override     public void restore() {      //還原位子      // 把消息的View移除      mWindowManager.removeView(mMsgDrafitingView);      // 把原來的View顯示      view.setVisibility(View.VISIBLE);     }      @Override     public void dismiss(PointF pointF) {      //爆炸效果      // 要去執行爆炸動畫 (幀動畫)      //移除拖拽的view      mWindowManager.removeView(mMsgDrafitingView);      // 要在 mWindowManager 添加一個爆炸動畫      mWindowManager.addView(mBombFrame,params);      mBombImage.setBackgroundResource(R.drawable.anim_bubble_pop);       AnimationDrawable drawable = (AnimationDrawable) mBombImage.getBackground();      mBombImage.setX(pointF.x-drawable.getIntrinsicWidth()/2);      mBombImage.setY(pointF.y-drawable.getIntrinsicHeight()/2);      drawable.start();      // 等它執行完之后我要移除掉這個 爆炸動畫也就是 mBombFrame      mBombImage.postDelayed(new Runnable() {       @Override       public void run() {        mWindowManager.removeView(mBombFrame);        // 通知一下外面該消失        if(mDisappearListener != null){         mDisappearListener.dismiss(view);        }       }      },getAnimationDrawableTime(drawable));     }    });    break;   case MotionEvent.ACTION_MOVE:    mMsgDrafitingView.updatePoint(motionEvent.getRawX(),      motionEvent.getRawY() - Utils.getStatusBarHeight(context));    break;   case MotionEvent.ACTION_UP:    mMsgDrafitingView.OnTouchUp();    break;  }  return true; }  private Bitmap getBitmapByView(View view) {  view.buildDrawingCache();  Bitmap bitmap = view.getDrawingCache();  return bitmap; }   public interface BubbleDisappearListener {  void dismiss(View view); }  /**  * 獲取爆炸動畫畫的時間  * @param drawable  * @return  */ private long getAnimationDrawableTime(AnimationDrawable drawable) {  int numberOfFrames = drawable.getNumberOfFrames();  long time = 0;  for (int i=0;i<numberOfFrames;i++){   time += drawable.getDuration(i);  }  return time; }}

 (3)View的調用代碼

public class MsgDrafitingViewActivity extends AppCompatActivity{ private Button mButton; private TextView mText; @Override protected void onCreate(@Nullable Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.qq_msg_drafitingview_activity);  mButton = findViewById(R.id.mBtn);  mText = findViewById(R.id.mText);  MsgDrafitingView.attach(mButton, new MsgDrafitingListener.BubbleDisappearListener() {   @Override   public void dismiss(View view) {    }  });  MsgDrafitingView.attach(mText, new MsgDrafitingListener.BubbleDisappearListener() {   @Override   public void dismiss(View view) {    }  }); }}

源碼地址:源碼Github地址

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美肥婆姓交大片| 亚洲毛片在线免费观看| 欧美另类69精品久久久久9999| 不卡av电影在线观看| 亚洲综合中文字幕68页| 91牛牛免费视频| 亚洲性生活视频在线观看| 日韩精品极品在线观看| 国产原创欧美精品| 成人久久久久久| 亚洲a级在线播放观看| 国产精品日韩电影| 国产美女直播视频一区| 亚洲剧情一区二区| 久久艹在线视频| 九九热这里只有精品免费看| 国产高清在线不卡| 国产91精品最新在线播放| 日本精品视频在线观看| 亚洲成av人片在线观看香蕉| 精品福利在线视频| 欧美亚洲第一页| 国产偷亚洲偷欧美偷精品| 最近2019好看的中文字幕免费| 国产免费亚洲高清| 欧美精品第一页在线播放| 欧美激情aaaa| 狠狠久久亚洲欧美专区| 日本精品一区二区三区在线| 欧美日韩国产在线播放| 日本亚洲欧美三级| 宅男66日本亚洲欧美视频| 色777狠狠综合秋免鲁丝| 国产盗摄xxxx视频xxx69| 日韩av电影在线免费播放| 午夜免费在线观看精品视频| 国产精品av网站| 国产精品成人一区二区| 亚州国产精品久久久| 亚洲综合在线做性| 亚洲精品久久久久中文字幕二区| 亚洲成色999久久网站| 久久精品国产亚洲精品2020| 亚洲国产另类久久精品| 91免费视频国产| 精品女同一区二区三区在线播放| 中文字幕在线看视频国产欧美| 在线国产精品视频| 欧美福利视频在线观看| 国产又爽又黄的激情精品视频| 日韩在线中文视频| 51精品在线观看| 91精品国产高清自在线看超| 日韩高清免费在线| 欧美激情aaaa| 欧美在线欧美在线| 欧美孕妇与黑人孕交| 午夜精品一区二区三区在线| 亚洲欧美日韩在线高清直播| 欧美激情在线播放| 亚洲丝袜av一区| 欧美日韩中文字幕| 欧美激情视频三区| 国产精品久久精品| 久久成人亚洲精品| 日韩视频在线一区| 久久精品国产亚洲一区二区| 日韩视频永久免费观看| 国产一区二区三区在线视频| 日韩动漫免费观看电视剧高清| 亚洲伊人一本大道中文字幕| 91亚洲精品一区| 国产欧美日韩免费看aⅴ视频| 日韩av电影在线播放| 91久久久久久久久久久久久| 日韩福利在线播放| 日韩av一区二区在线| 国产一区二区三区欧美| 色狠狠久久aa北条麻妃| 日韩麻豆第一页| 久久精品夜夜夜夜夜久久| 国产精品久久久久高潮| 亚洲一区美女视频在线观看免费| 一二美女精品欧洲| 欧美精品一区在线播放| 欧美黄色片免费观看| 久久精品国产91精品亚洲| 成人免费高清完整版在线观看| 91精品一区二区| 国产精品第一区| 亚洲男女自偷自拍图片另类| 国产精品劲爆视频| 亚洲第一免费播放区| 日韩麻豆第一页| 97国产精品久久| 久久久最新网址| 中文日韩在线观看| 国产亚洲人成a一在线v站| 欧美激情手机在线视频| 国产精品久久久久久久9999| 亚洲美女自拍视频| 欧美一级成年大片在线观看| 亚洲精品美女在线观看| 日本午夜人人精品| 亚洲欧美日韩爽爽影院| 九九精品视频在线| 国产成人亚洲综合| 91免费综合在线| 欧美激情国产高清| 亚洲黄色免费三级| 亚洲自拍小视频免费观看| 久青草国产97香蕉在线视频| 热99精品里视频精品| 国产精品久久久久久久久久免费| 日韩精品在线观看网站| 亚洲在线免费观看| 久久久久久久久久婷婷| 日韩在线视频线视频免费网站| 国产高清视频一区三区| 久久精品国产69国产精品亚洲| 2019国产精品自在线拍国产不卡| 日韩大片在线观看视频| 欧美在线精品免播放器视频| 亚洲全黄一级网站| 91精品视频网站| 黑人巨大精品欧美一区二区三区| 精品视频久久久久久| 欧美日韩综合视频| 欧美另类交人妖| 国产精品久久久久久久久久免费| 91久热免费在线视频| 亚洲精品在线91| 日韩精品中文字| 久久综合久中文字幕青草| xxx成人少妇69| 欧美老妇交乱视频| 精品国产91久久久久久老师| 成人欧美一区二区三区在线湿哒哒| 亚洲欧美日韩国产中文专区| 中文字幕在线观看亚洲| 色爱av美腿丝袜综合粉嫩av| 国产亚洲aⅴaaaaaa毛片| 国产成人午夜视频网址| 欧美日韩一区二区免费视频| 这里只有精品在线观看| 久久综合88中文色鬼| 国产亚洲欧美日韩美女| 精品国产老师黑色丝袜高跟鞋| 日韩精品免费在线视频观看| 韩日精品中文字幕| 国产视频久久久| 亚洲天天在线日亚洲洲精| 91牛牛免费视频| 欧美在线一区二区视频| 国产日韩中文字幕| 欧美床上激情在线观看| 欧美激情中文网| 久久精品免费播放| 91高清免费视频| 亚洲国产精品成人va在线观看| 亚洲国产精彩中文乱码av在线播放| 国产亚洲欧美aaaa| 性欧美暴力猛交69hd| 亚洲国产欧美一区二区三区久久|