亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 實現(xiàn)仿QQ拖拽氣泡效果的示例

 更新時間:2021年04月01日 09:26:12   作者:loren325  
這篇文章主要介紹了Android 實現(xiàn)仿QQ拖拽氣泡效果的示例,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下

效果圖:

一、實現(xiàn)思路

在列表中默認使用自定義的TextView控件來展示消息氣泡,在自定義的TextView控件中重寫onTouchEvent方法,然后在DOWN、MOVE、UP事件中分別處理拖拽效果。

整個拖拽效果我們可以拆分成以下幾步來實現(xiàn):
1.默認狀態(tài)
2.兩氣泡相連狀態(tài)
3.兩氣泡分離狀態(tài)
4.氣泡消失狀態(tài)

二、功能實現(xiàn)

默認狀態(tài):用來做一個狀態(tài)的標(biāo)識,無需特別處理。

兩氣泡相連狀態(tài):繪制一個固定圓和一個移動圓,使用兩條貝塞爾曲線來實現(xiàn)兩氣泡連接的曲線,兩條貝塞爾曲線共用同一個控制點,然后根據(jù)MOVE事件中的坐標(biāo)不斷重繪移動圓。

實現(xiàn)兩氣泡連接的效果,需要先計算出一些點的坐標(biāo),這也是整個拖拽氣泡效果的核心部分,具體如下圖:

如圖,A點到B點是一條二階貝塞爾曲線,C點到D點也是一條二階貝塞爾曲線,它們共用同一個控制點,所以我們要計算出A點、B點、C點、D點以及控制點的坐標(biāo)。

首先來計算控制點的坐標(biāo),控制點的坐標(biāo)和容易計算出,也就是固定圓的x坐標(biāo)加上移動圓的x坐標(biāo),再除以2,固定圓的y坐標(biāo)同理得出。

int controlX = (int) ((mBubStillCenter.x + mBubMoveCenter.x) / 2);
int controlY = (int) ((mBubStillCenter.y + mBubMoveCenter.y) / 2);

根據(jù)圖中所標(biāo)注的信息得知,∠a=∠d,∠b=∠c,∠a=∠θ,由此可知,我們求出∠θ所在的直角三角形的sin和cos值,就可以計算出A點、B點、C點、D點的坐標(biāo)。

sin值可以通過移動圓的y坐標(biāo)減去固定圓的y坐標(biāo),再除以兩圓心的距離,也就是O1到O2的距離。

cos值可以通過移動圓的x坐標(biāo)減去固定圓的x坐標(biāo),再除以兩圓心的距離。

float sin = (mBubMoveCenter.y - mBubStillCenter.y) / mDist;
float cos = (mBubMoveCenter.x - mBubStillCenter.x) / mDist;

有了sin和cos值,對應(yīng)的A點、B點、C點、D點的坐標(biāo)就好計算了

// A點
float bubbleStillStartX = mBubStillCenter.x + mBubbleStillRadius * sin;
float bubbleStillStartY = mBubStillCenter.y - mBubbleStillRadius * cos;
// B點
float bubbleMoveStartX = mBubMoveCenter.x + mBubbleMoveRadius * sin;
float bubbleMoveStartY = mBubMoveCenter.y - mBubbleMoveRadius * cos;
// C點
float bubbleMoveEndX = mBubMoveCenter.x - mBubbleMoveRadius * sin;
float bubbleMoveEndY = mBubMoveCenter.y + mBubbleMoveRadius * cos;
// D點
float bubbleStillEndX = mBubStillCenter.x - mBubbleStillRadius * sin;
float bubbleStillEndY = mBubStillCenter.y + mBubbleStillRadius * cos;

接下來就是把這些貝塞爾曲線和直線連起來,就實現(xiàn)了兩氣泡相連的效果。

兩氣泡分離狀態(tài):當(dāng)拖拽的移動圓超出固定圓一定范圍時,就進入了兩氣泡分離狀態(tài),此時我們只需要繪制移動圓即可。當(dāng)拖拽的移動圓回到固定圓一定范圍時,此時會進入兩氣泡相連狀態(tài),并且需要實現(xiàn)一個氣泡還原的效果。(這里會有個難點,就是移動圓我們可以在屏幕上任意拖動而不被遮擋,這里放到后面來實現(xiàn)。)

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();
}

mDist就是兩圓心的距離。

/**
 * 氣泡還原動畫
 */
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(200);
  animator.setInterpolator(input -> {
    float factor = 0.4f;
    return (float) (Math.pow(2, -10 * factor) * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) + 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();
}

分享一個可視化插值器的網(wǎng)站,其中內(nèi)置了一些插值器公式,還可以查看動畫演示效果。http://inloop.github.io/interpolator/

氣泡消失狀態(tài):當(dāng)拖拽的移動圓超出一定范圍時,并且松開了手指后,此時進入氣泡消失狀態(tài),此時我們需要實現(xiàn)一個爆炸的動畫。

爆炸的動畫通過繪制一組圖片來實現(xiàn)

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);
  }
}

mCurDrawableIndex是圖片的索引,是通過屬性動畫來改變

/**
 * 氣泡爆炸動畫
 */
private void startBubbleBurstAnim() {
  ValueAnimator animator = ValueAnimator.ofInt(0, mBurstDrawablesArray.length);
  animator.setInterpolator(new LinearInterpolator());
  animator.setDuration(1000);
  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();
}

三、全屏拖拽效果實現(xiàn)

首先在DOWN事件中獲取當(dāng)前觸摸位置在全屏所在位置,然后將當(dāng)前view緩存為bitmap,并把此bitmap添加到rootview中,拖動的時候直接繪制此bitmap。

//獲得當(dāng)前View在屏幕上的位置
int[] cLocation = new int[2];
getLocationOnScreen(cLocation);

if(rootView instanceof ViewGroup){
  mDragDotView = new DragDotView(getContext());

  //設(shè)置固定圓和移動圓的圓心坐標(biāo)
  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);
  }
}

/**
 * 將當(dāng)前view緩存為bitmap,拖動的時候直接繪制此bitmap
 * @param view
 * @return
 */
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;
}

至此,整個消息氣泡拖拽效果的核心部分就實現(xiàn)了

源碼地址:

https://github.com/loren325/CustomerView

以上就是Android 實現(xiàn)仿QQ拖拽氣泡效果的示例的詳細內(nèi)容,更多關(guān)于Android 實現(xiàn)仿QQ拖拽氣泡效果的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論