美文网首页
组合控件拖动

组合控件拖动

作者: 因为我的心 | 来源:发表于2020-10-29 15:05 被阅读0次

一、前言:

我们在开发过程中,需要组合控件完成文字和图片的分别显示:


图片.png

1、自定义AttachButton

package com.sumansoul.teacher.utils.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.sumansoul.teacher.R;
import com.sumansoul.teacher.base.BaseApplication;

/**
 * 自定义View实现拖动并自动吸边效果
 * <p>
 * 处理滑动和贴边 {@link #onTouchEvent(MotionEvent)}
 * 处理事件分发 {@link #dispatchTouchEvent(MotionEvent)}
 * </p>
 *
 * @attr customIsAttach  //是否需要自动吸边
 * @attr customIsDrag    //是否可拖曳
 */
public class AttachButton extends RelativeLayout {
    private final Context context;
    private float mLastRawX;
    private float mLastRawY;
    private final String TAG = "AttachButton";
    private boolean isDrug = false;
    private int mRootMeasuredWidth = 0;
    private int mRootMeasuredHeight = 0;
    private int mRootTopY = 0;

    private RelativeLayout rl_attach;
    private ImageView iv_alarm;
    private TextView tv_alarm;
    /**
     * left是左侧,right是右侧
     */
    private boolean left = false;
    private OnCountDownLister onCountDownLister;
    boolean mScrolling;

    public AttachButton(Context context) {
        this(context, null);
    }

    public AttachButton(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AttachButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        setClickable(true);
        initAttrs(context, attrs);
    }

    /**
     * 初始化自定义属性
     */
    private void initAttrs(Context context, AttributeSet attrs) {
        View view = LayoutInflater.from(context).inflate(R.layout.attach_button, this);
        TypedArray mTypedAttay = context.obtainStyledAttributes(attrs, R.styleable.AttachButton);
        mTypedAttay.recycle();
        rl_attach = view.findViewById(R.id.rl_attach);
        iv_alarm = view.findViewById(R.id.iv_alarm);
        tv_alarm = view.findViewById(R.id.tv_alarm);

    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        super.dispatchTouchEvent(event);
        return true;
    }


    private float touchDownX;

    /**
     * 点击事件和滑动事件冲突
     *
     * @param event
     * @return
     */

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchDownX = event.getX();
                mScrolling = false;
                break;
            case MotionEvent.ACTION_MOVE:
                //Log.d("LUO", "真实滑动距离:=====" + Math.abs(touchDownX - event.getX()));
                if (Math.abs(touchDownX - event.getX()) >= ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
                    mScrolling = true;
                    rl_attach.setBackground(BaseApplication.getInstance().getDrawable(R.drawable.class_center_round_4));
                } else {
                    mScrolling = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                mScrolling = false;
                break;
        }
        return mScrolling;
    }


    /**
     * 是否滑动
     */
    boolean isScroll = false;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //当前手指的坐标
        float mRawX = ev.getRawX();
        float mRawY = ev.getRawY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN://手指按下
                isScroll = false;
                isDrug = false;
                //记录按下的位置
                mLastRawX = mRawX;
                mLastRawY = mRawY;
                ViewGroup mViewGroup = (ViewGroup) getParent();
                if (mViewGroup != null) {
                    int[] location = new int[2];
                    mViewGroup.getLocationInWindow(location);
                    //获取父布局的高度
                    mRootMeasuredHeight = mViewGroup.getMeasuredHeight();
                    mRootMeasuredWidth = mViewGroup.getMeasuredWidth();
                    //获取父布局顶点的坐标
                    mRootTopY = location[1];
                }
                break;
            case MotionEvent.ACTION_MOVE://手指滑动
                if (mRawX >= 0 && mRawX <= mRootMeasuredWidth && mRawY >= mRootTopY && mRawY <= (mRootMeasuredHeight + mRootTopY)) {

                    //手指X轴滑动距离
                    float differenceValueX = mRawX - mLastRawX;
                    //手指Y轴滑动距离
                    float differenceValueY = mRawY - mLastRawY;

                    if (differenceValueX > 0 || differenceValueY > 0) {
                        isScroll = true;
                    }

                    //判断是否为拖动操作
                    if (!isDrug) {
                        if (Math.sqrt(differenceValueX * differenceValueX + differenceValueY * differenceValueY) < 2) {
                            isDrug = false;
                        } else {
                            isDrug = true;
                            rl_attach.setBackground(BaseApplication.getInstance().getDrawable(R.drawable.class_center_round_4));
                        }
                    }
                    //获取手指按下的距离与控件本身X轴的距离
                    float ownX = getX();
                    //获取手指按下的距离与控件本身Y轴的距离
                    float ownY = getY();
                    //理论中X轴拖动的距离
                    float endX = ownX + differenceValueX;
                    //理论中Y轴拖动的距离
                    float endY = ownY + differenceValueY;
                    //X轴可以拖动的最大距离
                    float maxX = mRootMeasuredWidth - getWidth();
                    //Y轴可以拖动的最大距离
                    float maxY = mRootMeasuredHeight - getHeight();
                    //X轴边界限制
                    endX = endX < 0 ? 0 : endX > maxX ? maxX : endX;
                    //Y轴边界限制
                    endY = endY < 0 ? 0 : endY > maxY ? maxY : endY;
                    //开始移动
                    setX(endX);
                    setY(endY);
                    //记录位置
                    mLastRawX = mRawX;
                    mLastRawY = mRawY;
                } else {
                    isScroll = false;
                }
                break;
            case MotionEvent.ACTION_UP://手指离开
                Log.d("LUO", "isDrug========" + isDrug);
                //判断是否为点击事件
                float center = mRootMeasuredWidth / 2;
                //自动贴边
                if (mLastRawX <= center) {
                    //向左贴边
                    AttachButton.this.animate()
                            .setInterpolator(new BounceInterpolator())
                            .setDuration(500)
                            .x(0)
                            .start();
                    left = true;
                    rl_attach.setBackground(BaseApplication.getInstance().getDrawable(R.drawable.class_left_round_4));
                } else {
                    //向右贴边
                    AttachButton.this.animate()
                            .setInterpolator(new BounceInterpolator())
                            .setDuration(500)
                            .x(mRootMeasuredWidth - getWidth())
                            .start();
                    left = false;
                    rl_attach.setBackground(BaseApplication.getInstance().getDrawable(R.drawable.class_right_round_4));
                }

                Log.d("LUO", "是否滑动=====isScroll=" + isScroll);
                if (!isScroll) {
                    if (onCountDownLister != null) {
                        //取消倒计时
                        onCountDownLister.OnCountDown(left);
                        rl_attach.setVisibility(GONE);
                    }

                }
                break;
        }

        //是否拦截事件
        return true;

    }

    /**
     * 设置倒计时
     *
     * @param second
     */
    public void setCountDown(int second) {
        rl_attach.setVisibility(VISIBLE);
        if (second > 0) {
            iv_alarm.setVisibility(GONE);
            tv_alarm.setVisibility(VISIBLE);
            tv_alarm.setText(String.valueOf(second) + "秒");
            //取消倒计时
            cancelDownTime();
            countDown(second * 1000 + 200);
        }
    }


    /**
     * 倒计时显示
     *
     * @param second
     */
    CountDownTimer timer;

    private void countDown(int second) {
        timer = new CountDownTimer(second, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                //每秒执行一次
                int value = (int) (millisUntilFinished / 1000);
                tv_alarm.setText(value + "秒");
            }

            @Override
            public void onFinish() {
                // 最后执行完操作
                //取消倒计时
                cancelDownTime();
                rl_attach.setVisibility(VISIBLE);
                iv_alarm.setVisibility(VISIBLE);
                tv_alarm.setVisibility(GONE);
            }
        }.start();

    }

    /**
     * 取消倒计时
     */
    private void cancelDownTime() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    /**
     * 倒计时按钮
     *
     * @param onCountDownLister
     */
    public void setOnButtonListener(OnCountDownLister onCountDownLister) {
        this.onCountDownLister = onCountDownLister;
    }

    /**
     * Java 接口
     */
    public interface OnCountDownLister {
        void OnCountDown(boolean left);
    }

}

2、attach_button.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_attach"
    android:layout_width="@dimen/dp_54"
    android:layout_height="@dimen/dp_54"
    android:background="@drawable/class_right_round_4">

    <ImageView
        android:id="@+id/iv_alarm"
        android:visibility="visible"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/class_alarm"
        android:layout_centerInParent="true"
        />
    <TextView
        android:id="@+id/tv_alarm"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        tools:text="60秒"
        android:textSize="@dimen/sp_14"
        android:padding="@dimen/dp_8"
        android:textColor="@color/color_FFFFFF"
        />
</RelativeLayout>

3、自定义view的使用

        <com.sumansoul.teacher.utils.view.AttachButton
            android:id="@+id/attach"
            android:layout_width="@dimen/dp_54"
            android:layout_height="@dimen/dp_54"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="@dimen/dp_71" />

4、styles布局中

<declare-styleable name="AttachButton">
        <!--是否需要自动吸边-->
        <attr name="customIsAttach" format="boolean" />
        <!--是否可拖曳-->
        <attr name="customIsDrag" format="boolean" />
    </declare-styleable>

5、class_center_round_4

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- 外层边框1 -->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1_2"
                android:left="@dimen/dp_1_2"
                android:right="@dimen/dp_1_2"
                android:top="@dimen/dp_1_2" />
            <solid android:color="@color/color_FF535461" />
            <corners
                android:topRightRadius="@dimen/dp_27"
                android:bottomRightRadius="@dimen/dp_27"
                android:topLeftRadius="@dimen/dp_27"
                android:bottomLeftRadius="@dimen/dp_27"/>
        </shape>
    </item>


    <!-- 边框颜色值 -->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1"
                android:left="@dimen/dp_1"
                android:right="@dimen/dp_1"
                android:top="@dimen/dp_1" />
            <solid android:color="@color/color_66000000" />
            <size android:width="@dimen/dp_52"
                android:height="@dimen/dp_52"/>
            <corners
                android:topLeftRadius="@dimen/dp_26"
                android:bottomLeftRadius="@dimen/dp_26"
                android:topRightRadius="@dimen/dp_26"
                android:bottomRightRadius="@dimen/dp_26"
                />
        </shape>
    </item>
    <!-- 主体背景颜色值 -->
    <item>

        <!-- 边框里面背景颜色 白色 -->
        <shape>
            <solid android:color="@color/color_66000000" />
            <size android:width="@dimen/dp_52"  android:height="@dimen/dp_52"/>
            <corners
                android:topLeftRadius="@dimen/dp_26"
                android:bottomLeftRadius="@dimen/dp_26"
                android:topRightRadius="@dimen/dp_26"
                android:bottomRightRadius="@dimen/dp_26"
                />
        </shape>

    </item>

</layer-list>

6、class_left_round_4

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <!--外层边框1 -->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1_2"
                android:left="0dp"
                android:right="@dimen/dp_1_2"
                android:top="@dimen/dp_1_2" />
            <solid android:color="@color/color_FF535461" />

            <corners
                android:topRightRadius="@dimen/dp_27"
                android:bottomRightRadius="@dimen/dp_27"
                />
        </shape>
    </item>
    <!--外层边框2 -->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1"
                android:left="0dp"
                android:right="@dimen/dp_1"
                android:top="@dimen/dp_1" />
            <solid android:color="@color/color_66000000" />
            <corners
                android:topRightRadius="@dimen/dp_26"
                android:bottomRightRadius="@dimen/dp_26" />
        </shape>
    </item>
    <!-- 主体背景颜色值 -->
    <item>
        <!-- 边框里面背景颜色 白色 -->
        <shape>
            <solid android:color="@color/color_66000000" />
            <size android:width="@dimen/dp_52"  android:height="@dimen/dp_52"/>
            <corners
                android:topRightRadius="@dimen/dp_26"
                android:bottomRightRadius="@dimen/dp_26"
                />
        </shape>

    </item>

</layer-list>

7、class_right_round_4

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 外层边框1 -->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1_2"
                android:left="@dimen/dp_1_2"
                android:right="0dp"
                android:top="@dimen/dp_1_2" />
            <solid android:color="@color/color_FF535461" />
            <corners
                android:bottomLeftRadius="@dimen/dp_27"
                android:topLeftRadius="@dimen/dp_27" />
        </shape>
    </item>

    <!-- 外层边框2 #535461-->
    <item>
        <shape>
            <padding
                android:bottom="@dimen/dp_1"
                android:left="@dimen/dp_1"
                android:right="0dp"
                android:top="@dimen/dp_1" />
            <solid android:color="@color/color_66000000" />
            <corners
                android:bottomLeftRadius="@dimen/dp_26"
                android:topLeftRadius="@dimen/dp_26" />
        </shape>
    </item>



    <!-- 主体背景颜色值 -->
    <item>

        <!-- 边框里面背景颜色 白色 -->
        <shape>
            <solid android:color="@color/color_66000000" />
            <size
                android:width="@dimen/dp_52"
                android:height="@dimen/dp_52" />
            <corners
                android:bottomLeftRadius="@dimen/dp_26"
                android:topLeftRadius="@dimen/dp_26" />
        </shape>

    </item>

</layer-list>

7、点击事件

 /**
  * 点击了倒计时
 */
attach.setOnButtonListener(new AttachButton.OnCountDownLister() {
  @Override
   public void OnCountDown(boolean left) {
     //点击事件
    }
   });

8、注意:

我在写腾讯直播上课时,用了两个组合控件,用同一个AttachButton 在布局引用两次,出现第二个可以正常使用,写两个AttachButton 就可以了。

相关文章

网友评论

      本文标题:组合控件拖动

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