拖拽控件

作者: 我的阿福 | 来源:发表于2020-06-07 13:37 被阅读0次
  • 为了复习View的位置坐标,今天做一个可以在父布局内随意拖拽的小控件,值得一提的是,这个可以根据父布局来调整整体的效果,,如图所示:

    p3o9v-v4wdh.gif
  • 整个控件很简单,但能帮助回忆View的位置关系。

  • 还是说一下整体思路
    1.监听触摸事件,计算手指移动的距离。
    2.让view在x方向和y方向移动和手指滑动相同的距离。
    3.加上贴边动画。.

  • 获取滑动位置
    当手指按下时,记住当前手指位置,滑动事件产生时用新的坐标减去旧的坐标就是滑动的距离。
case MotionEvent.ACTION_DOWN:
                mLastTouchX = event.getRawX();//这里要注意,不能用getX,后面有解释
                mLastTouchY = event.getRawY();

 case MotionEvent.ACTION_MOVE:
                float nowX = event.getRawX();
                float nowY = event.getRawY();
                float delX = nowX - mLastTouchX;//x方向滑动距离
                float delY = nowY - mLastTouchY;//y方向滑动距离
  • 移动和手指相同的距离
    这里移动用setTranslationX和setTranslationY的方法来移动,这两个方法是指左顶点相对于父布局一开始的位置的偏移量。需要注意的是,视图钢加载出来的时候,不论view在父布局的什么位置,这两个偏移量都是0。
 float tranY = getTranslationY() + delY;
                float nowReY = getY();
              //这几句判断是为了不让自己垂直方向超出父视图的边界
                if (nowReY <= 0 && delY < 0) {
                    tranY = -getTop();
                } else if (nowReY + mHeight >= mParentHeight && delY > 0) {
                    tranY = mParentHeight - mHeight - getTop();
                }
                float tranX = getTranslationX() + delX;
                float nowReX = getX();
                //同理,不让自己水平方向超出父视图的边界
                if (nowReX <= 0 && delX < 0) {
                    tranX = -getLeft();
                } else if (nowReX + mWidth >= mParentWidth && delX > 0) {
                    tranX = mParentWidth - getRight();
                }

                setTranslationX(tranX);
                setTranslationY(tranY);
  • 手指离开屏幕,up事件时,判断边界距离,贴到边界。
if (getX() + (mWidth / 2) > (mParentWidth / 2)) {
            int rightMoveDistance = (int) (mParentWidth - getX() - mWidth / 2);
            //貼右边
            startSideAnimation(RIGTHSIDE, getTranslationX(), rightMoveDistance);
        } else {
            //貼左边
            int leftMoveDistance = (int) (getX() + mWidth / 2);
            startSideAnimation(LEFTSIDE, getTranslationX(), leftMoveDistance);
        } 
private void startSideAnimation(final int side, final float origTX, int moveDistance) {
        mSideAnimation = ValueAnimator.ofFloat(moveDistance);
        mSideAnimation.setDuration(200);
        mSideAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
        mSideAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                switch (side) {
                    case LEFTSIDE:
                        setTranslationX(origTX - value);
                        break;
                    default:
                        setTranslationX(origTX + value);
                }
            }
        });
        mSideAnimation.start();
    } 

整个过程其实很简单,但值得注意的地方有两个:
1.获取手指位置用的getRawX而不是getX,这两个的区别:getRawX是手指接触view的位置距离整个屏幕边界的距离,getX是手指触摸view的位置距离父布局边界的距离。如果这里用getX的话,当view移动的时候,getX也会相应改变,无法达到想要的效果。
2.移动view的位置,直接用setTranslationX,setTranslationY的方法。

  • 整个代码
package com.anthony.widgets;

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;

/**
 * Created by H.Anthony on 2020/06/06.
 */
public class MoveSpirit extends View {

    private static final String TAG = "MoveSpirit";
    private static final int LEFTSIDE = 0x1;
    private static final int RIGTHSIDE = 0x2;
    private float mLastTouchX, mLastTouchY;
    private int mParentWidth, mParentHeight, mWidth, mHeight;
    private ValueAnimator mSideAnimation;

    public MoveSpirit(Context context) {
        super(context);
    }

    public MoveSpirit(Context context, @androidx.annotation.Nullable AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        ViewParent viewParent = getParent();
        if (viewParent != null) {
            if (viewParent instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) viewParent;
                mParentWidth = viewGroup.getWidth();
                mParentHeight = viewGroup.getHeight();
                mWidth = getWidth();
                mHeight = getHeight();
                Log.d(TAG, "onSizeChanged: " + mParentWidth + ";" + mParentHeight + ";" + mWidth + ";" + mHeight);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        closeAnimation();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastTouchX = event.getRawX();
                mLastTouchY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                float nowX = event.getRawX();
                float nowY = event.getRawY();
                float delX = nowX - mLastTouchX;
                float delY = nowY - mLastTouchY;

                float tranY = getTranslationY() + delY;
                float nowReY = getY();
                if (nowReY <= 0 && delY < 0) {
                    tranY = -getTop();
                } else if (nowReY + mHeight >= mParentHeight && delY > 0) {
                    tranY = mParentHeight - mHeight - getTop();
                }
                float tranX = getTranslationX() + delX;
                float nowReX = getX();
                if (nowReX <= 0 && delX < 0) {
                    tranX = -getLeft();
                } else if (nowReX + mWidth >= mParentWidth && delX > 0) {
                    tranX = mParentWidth - getRight();
                }

                setTranslationX(tranX);
                setTranslationY(tranY);

                mLastTouchX = nowX;
                mLastTouchY = nowY;
                break;
            case MotionEvent.ACTION_UP:
                autoTouchSide();
                break;
        }

        return true;
    }


    /**
     * 左右動畫
     */
    private void autoTouchSide() {
        if (getX() + (mWidth / 2) > (mParentWidth / 2)) {
            int rightMoveDistance = (int) (mParentWidth - getX() - mWidth / 2);
            //貼右边
            startSideAnimation(RIGTHSIDE, getTranslationX(), rightMoveDistance);
        } else {
            //貼左边
            int leftMoveDistance = (int) (getX() + mWidth / 2);
            startSideAnimation(LEFTSIDE, getTranslationX(), leftMoveDistance);
        }

    }

    private void startSideAnimation(final int side, final float origTX, int moveDistance) {
        mSideAnimation = ValueAnimator.ofFloat(moveDistance);
        mSideAnimation.setDuration(200);
        mSideAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
        mSideAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                switch (side) {
                    case LEFTSIDE:
                        setTranslationX(origTX - value);
                        break;
                    default:
                        setTranslationX(origTX + value);
                }
            }
        });
        mSideAnimation.start();
    }

    private void closeAnimation() {
        if (mSideAnimation != null && mSideAnimation.isRunning()) {
            mSideAnimation.removeAllUpdateListeners();
            mSideAnimation.cancel();
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.anthony.widgets.MoveSpirit
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:background="@mipmap/panda"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

相关文章

  • Flutter-26- Draggable控件

    提供了强大的拖拽控件,可以灵活定制 Draggable Widget Draggable控件负责就是拖拽,父层使用...

  • 3

    结构 1:定义一个半透明的Activity 2:使控件可以拖拽,并记录下拖拽后控件的位置

  • 控件拖拽

    JavaScript实现最简单的拖拽效果 HTML5 drag & drop 拖拽与拖放简介 基于HTML5 dr...

  • 拖拽控件

    为了复习View的位置坐标,今天做一个可以在父布局内随意拖拽的小控件,值得一提的是,这个可以根据父布局来调整整体的...

  • HTML5 input 新增的表单控件

    必填项验证 颜色选择控件 日期选择控件 时间选择控件 电子邮件控件:提交表单时有格式验证 数字控件 文件控件 拖拽...

  • Storyboard 文本框(UITextField)

    一、在storyboard里放置文本框控件 右键点住控件,或者按住option拖拽控件到ViewControlle...

  • C#中实现控件拖拽效果(How to DragDrop Cont

    当产品间需要交互实现数据传递,或产品需要从外部导入文件时,通过控件拖拽来实现是个不错的选择。在UI上支持控件拖拽,...

  • 地图的解析与反向解析

    一 拖拽控件并关联 注意: 在拖拽按钮时 需在Connection中选中Action 二 导入头文件 三 在...

  • iOS 自定义拖拽式控件:QiDragView

    级别: ★★☆☆☆标签:「iOS」「拖拽式控件」「QiDragView」作者: MrLiuQ审校: QiShare...

  • 自定义控件生成问题

    问题: 前端自己拖拽控件的界面,后台将界面中的控件字段传来,有些是EditText,有些是时间控件,有些是Text...

网友评论

    本文标题:拖拽控件

    本文链接:https://www.haomeiwen.com/subject/rnqctktx.html