使用ViewDragHelper打造属于自己的DragLayout(抽屉开关 )

</com.xujun.drawerLayout.drag.DragLayout>

在代码中若想为其设置监听器,

分别可以监听打开的 时候,关闭的时候,拖动的时候,可以在里面做相应的处理,同时我还加入了 自定义属性可以通过 app:range=”480”或者setRange()方法,即可设置打开抽屉的范围。

mDragLayout.setDragStatusListener(new OnDragStatusChangeListener() {

@Override

public void onOpen() {

Utils.showToast(MainActivity.this, “onOpen”);

// 左面板ListView随机设置一个条目

Random random = new Random();

Log.i(TAG, “onOpen:=” +mDragLayout.getRange());

int nextInt = random.nextInt(50);

mLeftList.smoothScrollToPosition(nextInt);

}

@Override

public void onDraging(float percent) {

Log.d(TAG, "onDraging: " + percent);// 0 -> 1

// 更新图标的透明度

// 1.0 -> 0.0

ViewHelper.setAlpha(mHeaderImage, 1 - percent);

}

@Override

public void onClose() {

Utils.showToast(MainActivity.this, “onClose”);

// 让图标晃动

ObjectAnimator mAnim = ObjectAnimator.ofFloat(mHeaderImage, “translationX”, 15.0f);

mAnim.setInterpolator(new CycleInterpolator(4));

mAnim.setDuration(500);

mAnim.start();

}

});


实现方式


关于ViewDragHelper的一些 讨论

DrawLayout在网上的 实现方式很多,千奇百怪,有一些是直接监听 onTouchEvent事件,处理Activon_Move,Action_Down,Action_up等动作,这样实现的话稍微有点复杂。本篇博客是使用ViewDragHelper来 处理触摸事件和拖拽事件的的,ViewDragHelper是2013Google IO大会推出的,目的是为了给开发者提供一个处理触摸事件,节省开发者的时间。

关于Google官方 关于ViewDragHelper的解释,简单来说就是处理ViewGroup的 触摸事件和拖拽事件

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.

实现思路

  • 1) 我是通过继承FrameLayout来实现的,相比较于继承ViewGroup来实现,这样有一个好处就是省去了自己重写 onMeasure (),onLayout ()方法

  • 2)在构造方法里面初始化mDragHelper,mSensitivity代表打开抽屉的 难易程度,是Float类型,至于mCallback是什么,下面会详细讲,这里先不着急。

public DragLayout(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

initAttars(context, attrs);

// a.初始化 (通过静态方法)

mDragHelper = ViewDragHelper.create(this, mSensitivity, mCallback);

}

  • 3)重写 onInterceptTouchEvent和onTouchevent 方法 ,将事件交给

// b.传递触摸事件

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

// 传递给mDragHelper

return mDragHelper.shouldInterceptTouchEvent(ev);

}

/***

  • 将事件交给mDragHelper处理

  • @param event

  • @return

*/

@Override

public boolean onTouchEvent(MotionEvent event) {

try {

mDragHelper.processTouchEvent(event);

} catch (Exception e) {

e.printStackTrace();

}

// 返回true, 持续接受事件

return true;

}

  • 4)重写onFinishInflate方法,在里面拿到 我们的侧滑菜单mLeftContent和主菜单mMainContent

@Override

protected void onFinishInflate() {

super.onFinishInflate();

// 容错性检查 (至少有俩子View, 子View必须是ViewGroup的子类)

if (getChildCount() < 2) {

throw new IllegalStateException("布局至少有俩孩子. Your ViewGroup must have 2 children at " +

“least.”);

}

if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) {

throw new IllegalArgumentException("子View必须是ViewGroup的子类. Your children must be an " +

“instance of ViewGroup”);

}

mLeftContent = (ViewGroup) getChildAt(0);

mMainContent = (ViewGroup) getChildAt(1);

}


下面我们一起来看一下这个mCallBack是什么东西

看之前我们需要了解Status和OnDragStatusChangeListener这两个东西。
  • Status代表DrawLayout 当前的状态,是否是打开,关闭还是拖拽。

  • OnDragStatusChangeListener是个监听器,在DrawLayout状态改变的时候会回调相关的方法,方便与外界进行通讯。

  • 我们可以通过 setDragStatusListener(OnDragStatusChangeListener mListener);这个方法设置监听

/**

  • 状态枚举

*/

public static enum Status {

Close, Open, Draging;

}

/**

  • 抽屉开关的监听器

*/

public interface OnDragStatusChangeListener {

void onClose();

void onOpen();

void onDraging(float percent);

}

接下来我们来看ViewDragHelper.Callback几个主要的方法


tryCaptureView(View child, int pointerId)

Called when the user’s input indicates that they want to capture the given child view with the pointer indicated by pointerId.

onViewCaptured(View capturedChild, int activePointerId)

Called when a child view is captured for dragging or settling.

getViewHorizontalDragRange(View child)

Return the magnitude of a draggable child view’s horizontal range of motion in pixels.

clampViewPositionHorizontal(View child, int left, int dx)

Restrict the motion of the dragged child view along the horizontal axis.

onViewPositionChanged(View changedView, int left, int top, int dx, int dy)

Called when the captured view’s position changes as the result of a drag or settle.

onViewReleased(View releasedChild, float xvel, float yvel)

Called when the child view is no longer being actively dragged.

谷歌官方的连接;https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper.Callback.html


下面的代码有关于这几个方法的中文解释,这里就不详细讲解了

ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {

// d. 重写事件

// 1. 根据返回结果决定当前child是否可以拖拽

// child 当前被拖拽的View

// pointerId 区分多点触摸的id

@Override

public boolean tryCaptureView(View child, int pointerId) {

Log.d(TAG, "tryCaptureView: " + child);

return mToogle;

}

@Override

public void onViewCaptured(View capturedChild, int activePointerId) {

Log.d(TAG, "onViewCaptured: " + capturedChild);

// 当capturedChild被捕获时,调用.

super.onViewCaptured(capturedChild, activePointerId);

}

@Override

public int getViewHorizontalDragRange(View child) {

// 返回拖拽的范围, 不对拖拽进行真正的限制. 仅仅决定了动画执行速度

Log.i(TAG, “getViewHorizontalDragRange:mRange=” +mRange);

return mRange;

}

// 2. 根据建议值 修正将要移动到的(横向)位置 (重要)

// 此时没有发生真正的移动

public int clampViewPositionHorizontal(View child, int left, int dx) {

// child: 当前拖拽的View

// left 新的位置的建议值, dx 位置变化量

// left = oldLeft + dx;

Log.d(TAG, "clampViewPositionHorizontal: "

  • "oldLeft: " + child.getLeft() + " dx: " + dx + " left: " + left);

if (child == mMainContent) {

left = fixLeft(left);

}

return left;

}

// 3. 当View位置改变的时候, 处理要做的事情 (更新状态, 伴随动画, 重绘界面)

// 此时,View已经发生了位置的改变

@Override

public void onViewPositionChanged(View c​
hangedView, int left, int top,

int dx, int dy) {

// changedView 改变位置的View

// left 新的左边值

// dx 水平方向变化量

super.onViewPositionChanged(changedView, left, top, dx, dy);

Log.d(TAG, "onViewPositionChanged: " + "left: " + left + " dx: " + dx);

int newLeft = left;

if (changedView == mLeftContent) {

// 把当前变化量传递给mMainContent

newLeft = mMainContent.getLeft() + dx;

}

// 进行修正

newLeft = fixLeft(newLeft);

if (changedView == mLeftContent) {

// 当左面板移动之后, 再强制放回去.

mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);

mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);

}

// 更新状态,执行动画

dispatchDragEvent(newLeft);

// 为了兼容低版本, 每次修改值之后, 进行重绘

invalidate();

}

// 4. 当View被释放的时候, 处理的事情(执行动画)

@Override

public void onViewReleased(View releasedChild, float xvel, float yvel) {

// View releasedChild 被释放的子View

// float xvel 水平方向的速度, 向右为+

// float yvel 竖直方向的速度, 向下为+

Log.d(TAG, "onViewReleased: " + "xvel: " + xvel + " yvel: " + yvel);

super.onViewReleased(releasedChild, xvel, yvel);

// 判断执行 关闭/开启

// 先考虑所有开启的情况,剩下的就都是关闭的情况

if (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f) {

open();

} else if (xvel > 0) {

open();

} else {

close();

}

}

@Override

public void onViewDragStateChanged(int state) {

// TODO Auto-generated method stub

super.onViewDragStateChanged(state);

}

};

其实主要思路就是

  • 1)在方法public boolean tryCaptureView(View child, int pointerId)处理那些child可以被捕捉,这里我们返回true表示所有的都可以被捕捉

  • 2)在public int clampViewPositionHorizontal(View child, int left, int dx)方法中根据child返回将要移动的水平位置的偏移量

  • 3)在 void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)方法中处理要做的事情 包括更新状态, 伴随动画, 重绘界面等

public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {

// 进行修正

newLeft = fixLeft(newLeft);

if (changedView == mLeftContent) {

// 当左面板移动之后, 再强制放回去.

mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);

mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);

if (changedView == mLeftContent) {

// 把当前变化量传递给mMainContent

newLeft = mMainContent.getLeft() + dx;

}

}

// 更新状态,执行动画

dispatchDragEvent(newLeft);

// 为了兼容低版本, 每次修改值之后, 进行重绘

invalidate();

}

protected void dispatchDragEvent(int newLeft) {

float percent = newLeft * 1.0f / mRange;

//0.0f -> 1.0f

Log.d(TAG, "percent: " + percent);

if (mListener != null) {

mListener.onDraging(percent);

}

// 更新状态, 执行回调

Status preStatus = mStatus;

mStatus = updateStatus(percent);

if (mStatus != preStatus) {

// 状态发生变化

if (mStatus == Status.Close) {

// 当前变为关闭状态

if (mListener != null) {

mListener.onClose();

}

} else if (mStatus == Status.Open) {

if (mListener != null) {

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
" + percent);

if (mListener != null) {

mListener.onDraging(percent);

}

// 更新状态, 执行回调

Status preStatus = mStatus;

mStatus = updateStatus(percent);

if (mStatus != preStatus) {

// 状态发生变化

if (mStatus == Status.Close) {

// 当前变为关闭状态

if (mListener != null) {

mListener.onClose();

}

} else if (mStatus == Status.Open) {

if (mListener != null) {

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-ltiQLwvx-1719098040066)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式linux系统中LCD屏驱动实现思路分析

在 Linux 下 LCD 的使用更加广泛,在搭配 QT 这样的 GUI 库下可以制作出非常精美的 UI 界面。接下来就来学习一下如何在 Linux 下驱动 LCD 屏幕。 第一:Framebuffer设备简介 先来回顾一下裸机的时候 LCD 驱动是怎么编写的,裸机 LCD 驱动编写流程如下: ①、初始化 I.MX6U 的…

Selenium WebDriver - 网络元素

本文翻译整理自&#xff1a;https://www.selenium.dev/documentation/webdriver/elements/ 文章目录 一、文件上传二、定位策略1、传统定位器2、创建定位器3、类名4、CSS选择器5、id6、NAME7、链接文本8、部分链接文本9、标签名称10、xpath11、相对定位器它是如何工作的可用相对…

three.js 第八节 - gltf加载器、解码器

// ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入hdr加载器&#xff08;专门加载hdr的&#xff09; import { RGBELoader } from three/examples/jsm/loaders…

npm全局安装依赖指定存放文件目录

引言 学校机房&#xff0c;每次默认在c盘装完 电脑关机重启都得重新安装&#xff0c;十分麻烦 1-创建依赖安装/缓存目录 2-打开终端输入设置安装目录位置的命令 npm set cache \你创建的缓存文件目录\ npm set prefix \你创建的global全局安装依赖文件目录\至此以后npm -g 依…

自动驾驶仿真Carla -ACC功能测试

我将详细说明如何使用Carla进行ACC&#xff08;自适应巡航控制&#xff09;测试&#xff0c;确保每个步骤贴合实际的Carla自动驾驶仿真标准&#xff0c;并提供相应的代码示例。 使用Carla进行ACC测试的步骤&#xff1a; 1. 环境设置和启动Carla 首先&#xff0c;确保你已经安装…

巴鲁夫MacroBuilder2.0.0.0软件巴鲁夫和使用手侧

巴鲁夫MacroBuilder2.0.0.0软件巴鲁夫和使用手侧

Linux下多进程访问同一个共享库处理流程

两个测试程序实现调用同一个SO库: ​​​​​​​ #include <stdio.h> #include "a/a.h" #include <unistd.h> int main() { int a = 4,b = 5; sum(a, b); int ret = get(); printf("ret=%d\n", ret); sleep(100)…

数据库新技术【分布式数据库】

文章目录 第一章 概述1.1 基本概念1.1.1 分布式数据库1.1.2 数据管理的透明性1.1.3 可靠性1.1.4 分布式数据库与集中式数据库的区别 1.2 体系结构1.3 全局目录1.4 关系代数1.4.1 基操1.4.2 关系表达式1.4.3 查询树 第二章 分布式数据库的设计2.1 设计策略2.2 分布设计的目标2.3…

【Linux】基础IO_2

文章目录 六、基础I/O2. 系统文件I/O磁盘的存储结构 未完待续 六、基础I/O 2. 系统文件I/O 磁盘的存储结构 系统中不是所有对文件都是打开的状态&#xff0c;大部分的文件都是没有被打开的。这些文件一般都被存储在磁盘当中。磁盘通过柱面&#xff0c;扇面&#xff0c;扇区确…

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

初始化数据 */ private void initData(Bundle savedInstanceState) { if (savedInstanceState ! null) mDatas savedInstanceState.getStringArrayList(“mDatas”); if (mDatas null) { mLoadingDialog new LoadingDialog(); mLoadingDialog.show(getFragmentMana…

Renesas MCU使用定时器之实现1ms定时中断

目录 概述 1 软硬件介绍 1.1 软件版本信息 1.2 硬件介绍 2 FSP配置项目 2.1 项目参数配置 2.2 配置定时器参数 3 功能实现 3.1 软件架构实现 3.2 实现功能函数 4 测试 概述 本文主要介绍使用Renesas MCU定时器之实现1ms定时中断功能&#xff0c;文中介绍使用FSP配…

理解 iOS 开发中的 NS_ENUM 和 NS_OPTIONS

在开发 iOS 应用程序时&#xff0c;理解 NS_ENUM 和 NS_OPTIONS 的使用至关重要&#xff0c;因为它们在定义和管理枚举和选项方面起着重要作用。在本文中&#xff0c;我们将深入探讨 NS_ENUM 和 NS_OPTIONS 之间的区别、使用场景以及如何有效地实现它们。 NS_ENUM NS_ENUM 用…

对兼容各操作系统的Anki选择题模板的更新——提供更方便的笔记修改功能

2021年当我想做一个兼容各操作系统的Anki选择题模板的时候&#xff0c;到处搜索茧中网&#xff0c;根本找不到相关内容&#xff0c;直到偶然在github上看到Simon Lammer的Anki持久化模块&#xff0c;才算真正实现。现在再在茧中网上搜索兼容各种操作系统的Anki选择题模板&#…

Android蓝牙开发(一)之打开蓝牙和设备搜索

private BluetoothManager bluetoothmanger; private​ BluetoothAdapter bluetoothadapter; /** 判断设备是否支持蓝牙 */ bluetoothmanger (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); bluetoothadapter bluetoothmanger.getAdapter(); if (bl…

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell Jenkins弱口令 默认用户一般为jenkins/jenkins 使用admin/admin123登陆成功 Jenkins反弹shell 格式为 println"命令".execute().text 在/tmp目录中生成shell.sh文件&#xff0c;并向其中写入反弹shell的语句 new…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 局域网中的服务器个数(200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

keepalived高可用,nginx+keepalived+apache架构的实现

目 录 一、概述&#xff1a; 二、实验架构图如图所示&#xff1a; 三、实验环境&#xff1a; 四、实现效果&#xff1a; 五、实验解析及步骤&#xff1a; 六、具体实现&#xff1a; 6.1 先关闭防火墙和核心防护&#xff1a;两条命令&#xff1a; 6.2 后端apache服务…

头歌资源库(16)分苹果

一、 问题描述 二、算法思想 首先&#xff0c;我们可以初始化一个数组apple来记录每个孩子分配的苹果数量&#xff0c;将所有元素初始化为1&#xff0c;表示每个孩子至少分配到一个苹果。 然后&#xff0c;从左到右遍历评分数组ratings&#xff0c;判断当前孩子的评分与前一个…

文件操作<C语言>

导言 平时我们在写程序时&#xff0c;在运行时申请内存空间&#xff0c;运行完时内存空间被收回&#xff0c;如果想要持久化的保存&#xff0c;我们就可以使用文件&#xff0c;所以下文将要介绍一些在程序中完成一些文件操作。 目录 导言 文件流 文件指针 文件的打开与关闭 …

Android进程间通信 Messenger详解

//这里服务端Service是运行在单独的进程中的 android:process“:other” class MessengerService : Service() { private lateinit var mMessenger: Messenger override fun onBind(intent: Intent): IBinder { log(TAG, “onBind~”) //传入Handler实例化Messenger mMes…