import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatTextView; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.LinearInterpolator;
  import com.example.administrator.myqq.R;
 
 
 
  public class DragMsgView extends AppCompatTextView {
      private DragDotView mDragDotView;     private float mWidth, mHeight;     private OnDragListener mDragListener;
      public DragMsgView(Context context) {         this(context, null);     }
      public DragMsgView(Context context, AttributeSet attrs) {         super(context, attrs);     }
      @Override     protected void onSizeChanged(int w, int h, int oldw, int oldh) {         mWidth = w;         mHeight = h;         super.onSizeChanged(w, h, oldw, oldh);     }
      @Override     public boolean onTouchEvent(MotionEvent event) {                  View rootView = getRootView();                  float mRawX = event.getRawX();         float mRawY = event.getRawY();         switch (event.getAction()){             case MotionEvent.ACTION_DOWN:                                  getParent().requestDisallowInterceptTouchEvent(true);                                  int[] cLocation = new int[2];                 getLocationOnScreen(cLocation);
                  if(rootView instanceof ViewGroup){                     mDragDotView = new DragDotView(getContext());
                                           mDragDotView.setDragPoint(cLocation[0] + mWidth / 2, cLocation[1] + mHeight / 2, mRawX, mRawY);
                      Bitmap bitmap = getBitmapFromView(this);                     if(bitmap != null){                         mDragDotView.setCacheBitmap(bitmap);                         ((ViewGroup) rootView).addView(mDragDotView);                         setVisibility(INVISIBLE);                     }                 }                 break;             case MotionEvent.ACTION_MOVE:                 getParent().requestDisallowInterceptTouchEvent(true);                 if(mDragDotView != null){                     mDragDotView.move(mRawX, mRawY);                 }                 break;             case MotionEvent.ACTION_UP:                 getParent().requestDisallowInterceptTouchEvent(false);                 if(mDragDotView != null){                     mDragDotView.up();                 }                 break;         }         return true;     }
      
 
 
 
      public Bitmap getBitmapFromView(View view) {         Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);         Canvas canvas = new Canvas(bitmap);         view.draw(canvas);         return bitmap;     }
      public class DragDotView extends View {
                   private final int BUBBLE_STATE_DEFAULT = 0;                  private final int BUBBLE_STATE_CONNECT = 1;                  private final int BUBBLE_STATE_APART = 2;                  private final int BUBBLE_STATE_DISMISS = 3;
                   private float mBubbleRadius = dpToPx(10);                  private int mBubbleColor;
                   private float mBubbleStillRadius;                  private float mBubbleMoveRadius;                  private PointF mBubStillCenter;                  private PointF mBubMoveCenter;
                   private Paint mBubblePaint;                  private Path mBezierPath;
          private Paint mBurstPaint;         private Rect mBurstRect;
                   private int mBubbleState = BUBBLE_STATE_DEFAULT;                  private float mDist;                  private float mMaxDist;                  private float MOVE_OFFSET;
          private Bitmap mCacheBitmap;                  private float mWidth, mHeight;
                   private Bitmap[] mBurstBitmapArray;                  private boolean mIsBurstAnimStart = false;                  private int mCurDrawableIndex;                  private final int[] mBurstDrawablesArray = {R.drawable.explosion_one, R.drawable.explosion_two                 , R.drawable.explosion_three, R.drawable.explosion_four, R.drawable.explosion_five};
          public DragDotView(Context context) {             this(context, null);         }
          public DragDotView(Context context, @Nullable AttributeSet attrs) {             this(context, attrs, 0);         }
          public DragDotView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {             this(context, attrs, defStyleAttr, 0);         }
          public DragDotView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {             super(context, attrs, defStyleAttr, defStyleRes);             init(context, attrs, defStyleAttr);         }
          private void init(Context context, AttributeSet attrs, int defStyleAttr){             TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DragBubbleView, defStyleAttr, 0);             mBubbleColor = array.getColor(R.styleable.DragBubbleView_bubbleColor, Color.RED);             array.recycle();
                           mBubblePaint = new Paint(Paint.ANTI_ALIAS_FLAG);             mBubblePaint.setColor(mBubbleColor);             mBubblePaint.setStyle(Paint.Style.FILL);
              mBezierPath = new Path();
              mBurstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);             mBurstPaint.setFilterBitmap(true);             mBurstRect = new Rect();             mBurstBitmapArray = new Bitmap[mBurstDrawablesArray.length];             for (int i = 0; i < mBurstDrawablesArray.length; i++) {                                  Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mBurstDrawablesArray[i]);                 mBurstBitmapArray[i] = bitmap;             }         }
          @Override         protected void onDraw(Canvas canvas) {             if(mDist < mMaxDist && mBubbleState == BUBBLE_STATE_CONNECT){                                  canvas.drawCircle(mBubStillCenter.x, mBubStillCenter.y, mBubbleStillRadius, mBubblePaint);
                                   int controlX = (int) ((mBubStillCenter.x + mBubMoveCenter.x) / 2);                 int controlY = (int) ((mBubStillCenter.y + mBubMoveCenter.y) / 2);
                  float sin = (mBubMoveCenter.y - mBubStillCenter.y) / mDist;                 float cos = (mBubMoveCenter.x - mBubStillCenter.x) / mDist;
                                                    float bubbleStillStartX = mBubStillCenter.x + mBubbleStillRadius * sin;                 float bubbleStillStartY = mBubStillCenter.y - mBubbleStillRadius * cos;                                  float bubbleMoveStartX = mBubMoveCenter.x + mBubbleMoveRadius * sin;                 float bubbleMoveStartY = mBubMoveCenter.y - mBubbleMoveRadius * cos;                                  float bubbleMoveEndX = mBubMoveCenter.x - mBubbleMoveRadius * sin;                 float bubbleMoveEndY = mBubMoveCenter.y + mBubbleMoveRadius * cos;                                  float bubbleStillEndX = mBubStillCenter.x - mBubbleStillRadius * sin;                 float bubbleStillEndY = mBubStillCenter.y + mBubbleStillRadius * cos;
                  mBezierPath.reset();                                  mBezierPath.moveTo(bubbleStillStartX, bubbleStillStartY);                 mBezierPath.quadTo(controlX, controlY, bubbleMoveStartX, bubbleMoveStartY);                                  mBezierPath.lineTo(bubbleMoveEndX, bubbleMoveEndY);                 mBezierPath.quadTo(controlX, controlY, bubbleStillEndX, bubbleStillEndY);                 mBezierPath.close();                 canvas.drawPath(mBezierPath, mBubblePaint);             }
                           if (mCacheBitmap != null && mBubbleState != BUBBLE_STATE_DISMISS) {                 canvas.drawBitmap(mCacheBitmap,                         mBubMoveCenter.x - mWidth / 2,                         mBubMoveCenter.y - mHeight / 2,                         mBubblePaint);             }
              if(mBubbleState == BUBBLE_STATE_DISMISS){                 if(mIsBurstAnimStart){                     mBurstRect.set((int)(mBubMoveCenter.x - mBubbleMoveRadius), (int)(mBubMoveCenter.y - mBubbleMoveRadius),                             (int)(mBubMoveCenter.x + mBubbleMoveRadius), (int)(mBubMoveCenter.y + mBubbleMoveRadius));                     canvas.drawBitmap(mBurstBitmapArray[mCurDrawableIndex], null, mBurstRect, mBurstPaint);                 }             }         }
          
 
          private void startBubbleBurstAnim() {             ValueAnimator animator = ValueAnimator.ofInt(0, mBurstDrawablesArray.length);             animator.setInterpolator(new LinearInterpolator());             animator.setDuration(500);             animator.addUpdateListener(animation -> {                 mCurDrawableIndex = (int) animator.getAnimatedValue();                 invalidate();             });             animator.addListener(new AnimatorListenerAdapter() {                 @Override                 public void onAnimationEnd(Animator animation) {                     mIsBurstAnimStart = false;                     if(mDragListener != null){                         mDragListener.onDismiss();                     }                 }             });             animator.start();         }
          
 
          private void startBubbleRestAnim() {             mBubbleStillRadius = mBubbleRadius;             ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new PointF(mBubMoveCenter.x, mBubMoveCenter.y), new PointF(mBubStillCenter.x, mBubStillCenter.y));             animator.setDuration(300);             animator.setInterpolator(input -> {
 
                  float f = 0.571429f;                 return (float) (Math.pow(2, -4 * input) * Math.sin((input - f / 4) * (2 * Math.PI) / f) + 1);             });             animator.addUpdateListener(animation -> {                 mBubMoveCenter = (PointF) animation.getAnimatedValue();                 invalidate();             });             animator.addListener(new AnimatorListenerAdapter() {                 @Override                 public void onAnimationEnd(Animator animation) {                     mBubbleState = BUBBLE_STATE_DEFAULT;                     removeDragView();                     if(mDragListener != null){                         mDragListener.onRestore();                     }                 }             });             animator.start();         }
          
 
 
          public void setCacheBitmap(Bitmap bitmap){             this.mCacheBitmap = bitmap;             mWidth = mCacheBitmap.getWidth();             mHeight = mCacheBitmap.getHeight();             mBubbleRadius = Math.min(mWidth, mHeight) / 2;         }
          
 
 
 
 
 
          public void setDragPoint(float stillX,                                  float stillY,                                  float moveX,                                  float moveY) {             mBubbleStillRadius = mBubbleRadius;             mBubbleMoveRadius = mBubbleStillRadius;             mMaxDist = mBubbleRadius * 8;             MOVE_OFFSET = mMaxDist / 4;             if(mBubStillCenter == null){                 mBubStillCenter = new PointF(stillX, stillY);             }else {                 mBubStillCenter.set(stillX, stillY);             }             if(mBubMoveCenter == null){                 mBubMoveCenter = new PointF(moveX, moveY);             }else {                 mBubMoveCenter.set(moveX, moveY);             }             mBubbleState = BUBBLE_STATE_CONNECT;             invalidate();         }
          public void move(float curX, float curY) {             mBubMoveCenter.x = curX;             mBubMoveCenter.y = curY;                          mDist = (float) Math.hypot(curX - mBubStillCenter.x, curY - mBubStillCenter.y);             if(mBubbleState == BUBBLE_STATE_CONNECT){                 if(mDist < mMaxDist - MOVE_OFFSET){                     mBubbleStillRadius = mBubbleRadius - mDist / 10;                 }else {                     mBubbleState = BUBBLE_STATE_APART;                 }             }             invalidate();         }
          public void up() {             if(mBubbleState == BUBBLE_STATE_CONNECT){                 startBubbleRestAnim();             }else if(mBubbleState == BUBBLE_STATE_APART){                 if(mDist < 3 * mBubbleRadius){                     startBubbleRestAnim();                 }else {                     mBubbleState = BUBBLE_STATE_DISMISS;                     mIsBurstAnimStart = true;                     startBubbleBurstAnim();                 }             }             invalidate();         }
          
 
          private void removeDragView() {             ViewGroup viewGroup = (ViewGroup) getParent();             viewGroup.removeView(DragDotView.this);             DragMsgView.this.setVisibility(VISIBLE);         }
          
 
 
 
 
          protected int dpToPx(float dp) {             DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();             return (int) (dp * metrics.density + 0.5f);         }     }
      public interface OnDragListener{         void onRestore();         void onDismiss();     }
      public void setOnDragListener(OnDragListener listener){         this.mDragListener = listener;     } }
   |