Android 可拖拽的View,限制在父布局中随意拖拽;拖拽结束后可左右吸边;

2024-06-08 21:20

本文主要是介绍Android 可拖拽的View,限制在父布局中随意拖拽;拖拽结束后可左右吸边;,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实现方法一:自定义View

可随意拖动拖拽的View,限制拖动范围是父布局中;


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.ImageView;public class DragActionButton extends ImageView {private int parentHeight;private int parentWidth;private int lastX;private int lastY;private ViewGroup parent;public DragActionButton(Context context) {super(context);}public DragActionButton(Context context, AttributeSet attrs) {super(context, attrs);}public DragActionButton(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);lastX = rawX;lastY = rawY;if (getParent() != null) {parent = (ViewGroup) getParent();parentHeight = parent.getHeight();parentWidth = parent.getWidth();}break;case MotionEvent.ACTION_MOVE:if (parentHeight <= 0.2 || parentWidth <= 0.2) {break;}int dx = rawX - lastX;int dy = rawY - lastY;//这里修复一些华为手机无法触发点击事件int distance = (int) Math.sqrt(dx * dx + dy * dy);if (distance == 0) {break;}float x = getX() + dx;float y = getY() + dy;//检测是否到达边缘 左上右下x = x < 0 ? 0 : x + getWidth() > parentWidth  ? parentWidth - getWidth() : x;y = y < 0 ? 0 : y + getHeight() > parentHeight ? parentHeight - getHeight() : y;setX(x);setY(y);lastX = rawX;lastY = rawY;Log.i("ACTION_MOVE", "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth+ ";parentHeight=" + parentHeight);break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:this.setAlpha(1.0f);Log.i("ACTION_UP", "getX=" + getX() + ";getY=" + getY());break;}return true;}}

下面是可以左右吸边的效果;


import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.Toast;import androidx.annotation.Nullable;public class DragFloatActionButton extends ImageView {private int parentHeight;private int parentWidth;Handler handler = new Handler();Runnable runnable = new Runnable() {@Overridepublic void run() {setAlpha(0.3f);}};private int lastX;private int lastY;private ViewGroup parent;public DragFloatActionButton(Context context) {super(context);}public DragFloatActionButton(Context context,  AttributeSet attrs) {super(context, attrs);}public DragFloatActionButton(Context context,  AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}private int downX;private int downY;private long downTime;@Overridepublic boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:this.setAlpha(0.9f);getParent().requestDisallowInterceptTouchEvent(true);lastX = rawX;lastY = rawY;downX = rawX;downY = rawY;downTime = System.currentTimeMillis();if (getParent() != null) {parent = (ViewGroup) getParent();parentHeight = parent.getHeight();parentWidth = parent.getWidth();}break;case MotionEvent.ACTION_MOVE:if (parentHeight <= 0.2 || parentWidth <= 0.2) {break;}this.setAlpha(0.9f);int dx = rawX - lastX;int dy = rawY - lastY;//这里修复一些华为手机无法触发点击事件int distance = (int) Math.sqrt(dx * dx + dy * dy);if (distance == 0) {break;}float x = getX() + dx;float y = getY() + dy;//检测是否到达边缘 左上右下x = x < 0 ? 0 : x + getWidth() > parentWidth  ? parentWidth - getWidth() : x;y = y < 0 ? 0 : y + getHeight() > parentHeight ? parentHeight - getHeight() : y;setX(x);setY(y);lastX = rawX;lastY = rawY;Log.i("ACTION_MOVE", "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth+ ";parentHeight=" + parentHeight);break;case MotionEvent.ACTION_UP:moveHide(rawX);if(onClickListener != null&& Math.abs(downX - rawX) < 10&& Math.abs(downY - rawY) < 10&& System.currentTimeMillis() - downTime < 1000){Log.i("ACTION_UP", "触发点击事件 ");onClickListener.onClick(this);}break;}return true;}OnClickListener onClickListener;@Overridepublic void setOnClickListener(@Nullable OnClickListener l) {onClickListener = l;}private void moveHide(int rawX) {if (rawX >= parentWidth / 2) {//靠右吸附animate().setInterpolator(new DecelerateInterpolator()).setDuration(500).xBy(parentWidth - getWidth() - getX()).start();myRunable();} else {//靠左吸附ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);oa.setInterpolator(new DecelerateInterpolator());oa.setDuration(500);oa.start();myRunable();}}private void myRunable() {handler.removeCallbacks(runnable);handler.postDelayed(runnable, 2000);}

实现方法二:自定义ViewGroup

使用ViewDragHelper实现:

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;import androidx.customview.widget.ViewDragHelper;public class DragViewGroup extends RelativeLayout {private ViewDragHelper mDragger;private View mDragView;public DragViewGroup(Context context, AttributeSet attrs) {super(context, attrs);this.setGravity(Gravity.CENTER);mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {@Overridepublic boolean tryCaptureView(View child, int pointerId) {return true;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {final int leftBound = getPaddingLeft();final int rightBound = getWidth() - mDragView.getWidth();final int newLeft = Math.min(Math.max(left, leftBound), rightBound);return newLeft;}@Overridepublic int clampViewPositionVertical(View child, int top, int dy) {final int topBound = getPaddingTop();final int bottomBound = getHeight() - mDragView.getHeight();final int newTop = Math.min(Math.max(top, topBound), bottomBound);return newTop;}//手指释放的时候回调@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {Log.i("onViewReleased", "view: x :"+mDragView.getX() +"y:"+mDragView.getY()+"\nviewgroup: w: "+getWidth() + "h : "+getHeight());}});}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {return mDragger.shouldInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {mDragger.processTouchEvent(event);return true;}@Overrideprotected void onFinishInflate() {super.onFinishInflate();mDragView = getChildAt(0);}
}

这篇关于Android 可拖拽的View,限制在父布局中随意拖拽;拖拽结束后可左右吸边;的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043341

相关文章

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

基于Python实现数字限制在指定范围内的五种方式

《基于Python实现数字限制在指定范围内的五种方式》在编程中,数字范围限制是常见需求,无论是游戏开发中的角色属性值、金融计算中的利率调整,还是传感器数据处理中的异常值过滤,都需要将数字控制在合理范围... 目录引言一、基础条件判断法二、数学运算巧解法三、装饰器模式法四、自定义类封装法五、NumPy数组处理

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级