国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

android自定義view之模擬qq消息拖拽刪除效果

2019-12-12 03:17:44
字體:
來源:轉載
供稿:網友

這個模擬功能的實現主要依靠了PATH和二階貝塞爾曲線。首先上一張圖來簡單看一下:

這個模擬功能有以下幾個特點:

  1. 在開始的時候點擊圓以外的區域不會觸發拖動事件
  2. 點擊圓的時候可以拖拽,此時會有一個拉伸效果,連接大圓和小圓
  3. 拉伸到一定距離(自己設定)以后兩個圓會斷開,此時即使再拖拽進距離之內的時候也不會再產生已經斷開的連接
  4. 在距離之內松手的時候會回彈會原位置,并伴有一個彈跳動畫

介紹了這么多,看過我前邊文章的朋友應該會有一個基本思路。

暴露接口

這個模擬功能共分為三部分,一個是那個小圓,固定的位置,一個是那個大圓,可以移動,還有一部分就是中間的連接部分,會跟隨大圓一起延伸。

首先看一下都有哪些接口可以調用:

 public void setMinR(float minR) {    this.minR = minR;  }  public void setMaxR(float maxR) {    this.maxR = maxR;  }  public void setBrokeDistance(float distance) {    this.brokeDistance = distance;  }

第一個setMinR是設置小圓的半徑,第二個setMaxR是設置大圓的半徑,第三個setBrokeDistance是設置斷開的距離,也就是小圓和大圓的圓心之間的最大連接距離。

初始化

public Buble(Context context) {    super(context);    init();  }  public Buble(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);    init();  }

簡單的看一下初始化方法。

private void init() {    paint = new Paint();    paint.setStyle(Paint.Style.FILL);    paint.setColor(Color.GREEN);    paint.setAntiAlias(true);  }

其實只有一個畫筆,這里可以為各個區域分別設置畫筆。

繪制圖形

protected void onDraw(Canvas canvas) {    super.onDraw(canvas);    drawOriginalCircle(canvas);    if (!canBroke) {      drawMoveCircle(canvas);      drawBCurve(canvas);    }  }

這三個方法相對簡單,drawOriginalCircle是畫中心的小圓,然后canBroke是一個開關,控制是否執行畫移動的圓和畫弧線。

 private void drawOriginalCircle(Canvas canvas) {    canvas.drawCircle(getWidth() / 2, getHeight() / 2, minR, paint);  }  private void drawMoveCircle(Canvas canvas) {    canvas.drawCircle(moveX, moveY, maxR, paint);  }  private void drawBCurve(Canvas canvas) {    canvas.drawPath(path, paint);  }

注意,moveX, moveY和path都是變化的,所以在他們的值發生改變以后千萬不要忘記invalidate。

path的連接

關于path的文章網上一大堆。

此處的難點主要是大圓和小圓之間的連接。用一張圖簡單表示一下:

基本就是這個樣子,path的路徑就是那個黑色的類似于漏斗一樣的東西。此處要計算的角度需要用到三角函數關系式,簡單表示一下:

圖中標出的兩個角度是相等的

double angle = Math.atan((offsetX - minCircleX) / (offsetY - minCircleY));

求出這個角度(offsetX是移動圓心的坐標)。

這樣就可以算出四個點的坐標了。

private void setPath(float offsetX, float offsetY) {    float minCircleX = (float) getWidth() / 2;    float minCircleY = (float) getHeight() / 2;    double angle = Math.atan((offsetX - minCircleX) / (offsetY - minCircleY));    float x1 = (float) (minCircleX + Math.cos(angle) * minR);    float y1 = (float) (minCircleY - Math.sin(angle) * minR);    float x2 = (float) (offsetX + Math.cos(angle) * maxR);    float y2 = (float) (offsetY - Math.sin(angle) * maxR);    float x3 = (float) (offsetX - Math.cos(angle) * maxR);    float y3 = (float) (offsetY + Math.sin(angle) * maxR);    float x4 = (float) (minCircleX - Math.cos(angle) * minR);    float y4 = (float) (minCircleY + Math.sin(angle) * minR);    float centerX = minCircleX + (offsetX - minCircleX) / 2;    float centerY = minCircleY + (offsetY - minCircleY) / 2;    path.reset();    path.moveTo(minCircleX, minCircleY);    path.lineTo(x1, y1);    path.quadTo(centerX, centerY, x2, y2);    path.lineTo(x3, y3);    path.quadTo(centerX, centerY, x4, y4);    path.lineTo(minCircleX, minCircleY);    path.close();  }

注意quadTo的四個參數的意義,前兩個是你的錨點的坐標,后兩個是你要移動到那個點的位置的坐標。

觸摸事件

這個直接上代碼來實現思路吧,沒什么好講的。

 switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        this.canBroke = false;        moveX = event.getX();        moveY = event.getY();        touchArea = !setCanBroke(moveX, moveY, maxR);        break;      case MotionEvent.ACTION_MOVE:        if (touchArea) {          moveX = event.getX();          moveY = event.getY();          if (setCanBroke(moveX, moveY, brokeDistance)) {            touchArea = false;            this.canBroke = true;          } else {            setPath(moveX, moveY);          }          invalidate();        }        break;      case MotionEvent.ACTION_UP:        Log.d("aaa", "actionUp" + touchArea);        if (touchArea) {          resetCircle(event.getX(), event.getY());        }        break;    }    return true;

這里主要說明一下這個setCanBroke:

 private boolean setCanBroke(float offsetX, float offsetY, float brokeDistance) {    float minCircleX = (float) getWidth() / 2;    float minCircleY = (float) getHeight() / 2;    return (offsetX - minCircleX) * (offsetX - minCircleX) +        (offsetY - minCircleY) * (offsetY - minCircleY) > brokeDistance * brokeDistance;  }

這個表示是否超出了最大移動距離,超出則返回真,未超出則返回假。同時在touchArea的設定中它也用用到了,主要是判斷點擊區域是否在圓圈上。

最后還要講一下這個resetCicle,這個是一個屬性動畫來控制返回原點的彈性動畫:

private void resetCircle(float x, float y) {    valueAnimatorX = ValueAnimator.ofFloat(x, (float) getWidth() / 2);    valueAnimatorY = ValueAnimator.ofFloat(y, (float) getHeight() / 2);    valueAnimatorX.removeAllUpdateListeners();    valueAnimatorY.removeAllUpdateListeners();    valueAnimatorX.setInterpolator(new BounceInterpolator());    valueAnimatorY.setInterpolator(new BounceInterpolator());    valueAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        tempX = (float) animation.getAnimatedValue();        moveX = tempX;      }    });    valueAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        tempY = (float) animation.getAnimatedValue();        moveY = tempY;        setPath(tempX, tempY);        postInvalidate();      }    });    set.playTogether(valueAnimatorX, valueAnimatorY);    set.start();  }

其中的插值器是BounceInterpolator,類似于小球彈跳的動畫,在我前邊的文章中有介紹。

最后來看一下不會斷開的效果,相當有意思:

關于自定義view的文章會暫時到這里,下一步準備更新自定義viewgroup的文章。相對于自定義view會稍微簡單一點。

demo下載地址:PathApplication_jb51.rar

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 景宁| 定边县| 秦皇岛市| 潮安县| 关岭| 东宁县| 南投县| 墨江| 博爱县| 炎陵县| 吴忠市| 潞城市| 蒲城县| 洪湖市| 新宾| 瑞金市| 抚州市| 茂名市| 梁山县| 磐石市| 元江| 定西市| 罗定市| 泰宁县| 三穗县| 乡宁县| 建始县| 鄱阳县| 云安县| 兴义市| 静海县| 泰兴市| 余干县| 三门县| 略阳县| 赤水市| 通化县| 绵竹市| 页游| 福州市| 申扎县|