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


DrawLayout这个自己定义的空间非经常见。qq,网易新闻。知乎等等,都有这样的效果,那这样的效果是如何实现的呢?本篇博客将带你来如何实现它。

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51935896

废话不多说,先来看一下 效果

首先我们先来看一下我们要如何使用它

事实上只须要两个 步骤,使用起来 非常方便

1.在XML文件

DragLayout至少要有两个孩子。且都是 ViewGroup或者ViewGroup的实现类

<com.xujun.drawerLayout.drag.DragLayout
android:id="@+id/dl"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
app:range="480"
> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="50dp"
android:paddingLeft="10dp"
android:paddingRight="50dp"
android:paddingTop="50dp"> <ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/head"/> <ListView
android:id="@+id/lv_left"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout> <com.xujun.drawerLayout.drag.MyLinearLayout
android:id="@+id/mll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical"> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#18B6EF"
android:gravity="center_vertical"> <ImageView
android:id="@+id/iv_header"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="15dp"
android:src="@drawable/head"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Header"/>
</RelativeLayout> <ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</com.xujun.drawerLayout.drag.MyLinearLayout> </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 changedView, 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) {
mListener.onOpen();
}
}
} // * 伴随动画:
animViews(percent); }
  • 4)在void onViewReleased(View releasedChild, float xvel, float yvel)的时候处理要做的事情,包含更新状态, 伴随动画, 重绘界面等。这里就不列出代码了,有兴趣的话下载源代码看看

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51935896

源代码下载地址: https://github.com/gdutxiaoxu/drawLayout.git

关于很多其它自己定义View的样例,可查看以下我的一些博客。同一时候假设大家认为还能够的。欢迎在github上面 star或者fork,谢谢。

经常使用的自己定义View样例一(FlowLayout)

自己定义View经常使用样例二(点击展开隐藏控件,九宫格图片控件)

经常使用的自己定义View样例三(MultiInterfaceView多界面处理)

经常使用的自己定义控件四(QuickBarView)

使用ViewDragHelper打造属于自己的DragLayout(抽屉开关 )的更多相关文章

  1. Android笔记:DrawerLayout抽屉布局的使用

    DrawerLayout(抽屉布局),在各种app中经常出现,比如csdn.. 如下示,只要从屏幕侧边滑一下,或者点击左上角的图标,抽屉就会出来. DrawerLayout要点: 1.使用Drawer ...

  2. Android官方终于支持 Navigation Drawer(导航抽屉)模式

    在2013 google IO当天,Android团的更新了Support库,新版本(V13)的Support库中新加入了几个比较重要的功能. 添加 DrawerLayout 控件,支持创建  Nav ...

  3. Navigation Drawer介绍

    在2013 google IO当天,Android团的更新了Support库,新版本(V13)的Support库中新加入了几个比较重要的功能. 添加 DrawerLayout 控件,支持创建  Nav ...

  4. Android下DrawerLayout的使用

    Android下DrawerLayout的使用 DrawerLayout见名知意,就是一个具有抽屉效果的布局,看看这个效果图,是不是感觉很炫酷 这么炫的效果其实不一定非要用类似一些SlidingMen ...

  5. Android Theme的使用

    原文地址 http://www.cnblogs.com/Dentist/p/4369816.html Theme是一套UI控件和Activity的样式.可以给Application 和 activit ...

  6. Google 官方 侧滑 drawerlayout

    一.概述 目前侧滑框架已经很多了,但是我常用的也就那么2个 ,slidingmenu 和sidemenu-android, 但是项目要求使用官方的,所以就看了一下drawerlayout 二.代码 官 ...

  7. Android开发:使用ViewDragHelper实现抽屉拉伸效果

    事实上,有非常多方法能够实现一个Layout的抽屉拉伸效果,最常常的方法就是自己定义一个ViewGroup,然后控制点击事件.控制移动之类的,这样的方法的代码量多,并且实现起来复杂,后期维护添加其它效 ...

  8. ViewDragHelper实战 自己打造Drawerlayout

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/47396187: 本文出自:[张鸿洋的博客] 一.概述 中间拖了蛮长时间了,在上 ...

  9. Android 一步一步教你使用ViewDragHelper

    在自定义viewgroup的时候 要重写onInterceptTouchEvent和onTouchEvent 这2个方法 是非常麻烦的事情,好在谷歌后来 推出了ViewDragHelper这个类.可以 ...

随机推荐

  1. java中的dao模式

    java中Dao模式   什么是DAO   1.Data Access Object(数据存取对象) 2.位于业务逻辑和持久化数据之间 3.实现对持久化数据的访问 DAO模式的作用 1隔离业务逻辑代码 ...

  2. AORUS GA-Z270X-Gaming 5開箱

    「AORUS」這個品牌名稱由埃及神祇荷魯斯(戰爭與狩獵之神)的名字衍生而成.荷魯斯通常被勾勒為獵鷹的形象,因此獵鷹的頭形被用作AORUS品牌的商標於2014年,本來只做為用在電競筆電及一些週邊方面,但 ...

  3. 图形文件元数据管理工具exiv2

    图形文件元数据管理工具exiv2   图形文件通常都包含多种元数据,如Exif.IPTC.XMP.这些信息往往是渗透人员收集的目标.为了便于管理这些信息,Kali Linux内置了专用工具exiv2. ...

  4. Highmaps网页图表教程之绘图区显示标签显示数据标签定位

    Highmaps网页图表教程之绘图区显示标签显示数据标签定位 Highmaps数据标签定位 由于数据标签是和节点一一对应,所以数据标签是依据节点位置进行定位的.本节详细讲解如何对数据标签进行定位. H ...

  5. lnmp环境一键搭建及卸载

    系统需求: CentOS/Debian/Ubuntu Linux系统 需要2GB以上硬盘剩余空间 128M以上内存,OpenVZ的建议192MB以上(小内存请勿使用64位系统) VPS或服务器必须已经 ...

  6. 算法转AI平台工程师记录-0

    --- vim源码安装: 1. git clone https://github.com/vim/vim.git 2. cd vim && ./configure --prefix=x ...

  7. 升级Tornado到4后weibo oauth登录不了

    把 Tornado 升级到4后,发现正常运行的微博登录不可以了. 原因是4已经移除 RequestHandler.async_callback and WebSocketHandler.async_c ...

  8. Pycharm报错解决:error:please select a valid Python interpreter

    问题描述: 之前PC上安装的是Python2,后来工作需要转成Python3了.然后在用pycharm运行Python2的程序时发现源程序运行报错(出去语法错误) error:please selec ...

  9. Polly简介 — 2. 弹性策略

    和故障处理策略不同的是,弹性策略并不是针对委托执行过程中的异常进行处理,而是改变委托本身的行为,因此弹性策略并没有故障定义这一过程,它的处理流程为: 定义策略 应用策略 Polly对弹性策略也做了不少 ...

  10. 详解Google Chrome浏览器(操作篇)(上)

    开篇概述 在上篇博客中详解Google Chrome浏览器(理论篇)一文中,主要讲解了Chrome 搜索引擎使用.Chrome安装和基本操作.Chrome 基本架构.多线程等原理性问题,这篇将重点讲解 ...