溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在android應(yīng)用中利用view實(shí)現(xiàn)一個(gè)拖拽刪除功能

發(fā)布時(shí)間:2020-11-30 16:53:08 來(lái)源:億速云 閱讀:245 作者:Leah 欄目:移動(dòng)開(kāi)發(fā)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么在android應(yīng)用中利用view實(shí)現(xiàn)一個(gè)拖拽刪除功能,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

功能有以下幾個(gè)特點(diǎn):

  1. 在開(kāi)始的時(shí)候點(diǎn)擊圓以外的區(qū)域不會(huì)觸發(fā)拖動(dòng)事件

  2. 點(diǎn)擊圓的時(shí)候可以拖拽,此時(shí)會(huì)有一個(gè)拉伸效果,連接大圓和小圓

  3. 拉伸到一定距離(自己設(shè)定)以后兩個(gè)圓會(huì)斷開(kāi),此時(shí)即使再拖拽進(jìn)距離之內(nèi)的時(shí)候也不會(huì)再產(chǎn)生已經(jīng)斷開(kāi)的連接

  4. 在距離之內(nèi)松手的時(shí)候會(huì)回彈會(huì)原位置,并伴有一個(gè)彈跳動(dòng)畫(huà)

介紹了這么多,看過(guò)我前邊文章的朋友應(yīng)該會(huì)有一個(gè)基本思路。

暴露接口

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

首先看一下都有哪些接口可以調(diào)用:

 public void setMinR(float minR) {
    this.minR = minR;
  }

  public void setMaxR(float maxR) {
    this.maxR = maxR;
  }

  public void setBrokeDistance(float distance) {
    this.brokeDistance = distance;
  }

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

初始化

public Buble(Context context) {
    super(context);
    init();
  }

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

簡(jiǎn)單的看一下初始化方法。

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

其實(shí)只有一個(gè)畫(huà)筆,這里可以為各個(gè)區(qū)域分別設(shè)置畫(huà)筆。

繪制圖形

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

這三個(gè)方法相對(duì)簡(jiǎn)單,drawOriginalCircle是畫(huà)中心的小圓,然后canBroke是一個(gè)開(kāi)關(guān),控制是否執(zhí)行畫(huà)移動(dòng)的圓和畫(huà)弧線。

 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都是變化的,所以在他們的值發(fā)生改變以后千萬(wàn)不要忘記invalidate。

path的連接

關(guān)于path的文章網(wǎng)上一大堆。

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

怎么在android應(yīng)用中利用view實(shí)現(xiàn)一個(gè)拖拽刪除功能

基本就是這個(gè)樣子,path的路徑就是那個(gè)黑色的類(lèi)似于漏斗一樣的東西。此處要計(jì)算的角度需要用到三角函數(shù)關(guān)系式,簡(jiǎn)單表示一下:

怎么在android應(yīng)用中利用view實(shí)現(xiàn)一個(gè)拖拽刪除功能

圖中標(biāo)出的兩個(gè)角度是相等的

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

求出這個(gè)角度(offsetX是移動(dòng)圓心的坐標(biāo))。

這樣就可以算出四個(gè)點(diǎn)的坐標(biāo)了。

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的四個(gè)參數(shù)的意義,前兩個(gè)是你的錨點(diǎn)的坐標(biāo),后兩個(gè)是你要移動(dòng)到那個(gè)點(diǎn)的位置的坐標(biāo)。

觸摸事件

這個(gè)直接上代碼來(lái)實(shí)現(xiàn)思路吧,沒(méi)什么好講的。

 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;

這里主要說(shuō)明一下這個(gè)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;
  }

這個(gè)表示是否超出了最大移動(dòng)距離,超出則返回真,未超出則返回假。同時(shí)在touchArea的設(shè)定中它也用用到了,主要是判斷點(diǎn)擊區(qū)域是否在圓圈上。

最后還要講一下這個(gè)resetCicle,這個(gè)是一個(gè)屬性動(dòng)畫(huà)來(lái)控制返回原點(diǎn)的彈性動(dòng)畫(huà):

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

上述就是小編為大家分享的怎么在android應(yīng)用中利用view實(shí)現(xiàn)一個(gè)拖拽刪除功能了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI