Android使用ViewDragHelper实现仿QQ6.0侧滑界面(一)

时间:2022-04-11 05:56:05

qq是大家离不开的聊天工具,方便既实用,自从qq更新至6.0之后,侧滑由原来的划出后主面板缩小变成了左右平滑,在外观上有了很大的提升,于是我就是尝试理解下里面的各种逻辑,结合相关资料,研究研究。

知道这里面的一个主要类是viewdraghelper,那么首先我们要先来了解一下这个viewdraghelper类,正所谓打蛇打七寸,我们就先来看看官方文档怎么介绍的,有什么奇特的功能。

首先继承:

java.lang.object
android.support.v4.widget.viewdraghelper
直接父类是object。

类概述

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.

他是一个编写自定义viewgroup的工具类,本省提供了许多有用的方法和状态允许用户去拖拽和绘制他们在父viewgroup中的轨迹和位置。

nested classes(嵌套类)

viewdraghelper.callback
a callback is used as a communication channel with the viewdraghelper back to the parent view using it.

一个回调是用作viewdraghelper和他的父view的通信的接口

一个公开静态方法:

Android使用ViewDragHelper实现仿QQ6.0侧滑界面(一) 

我们可以知道,viewdraghelper是通过create()方法构造出来。这个在后面会有详细介绍。

让我们在来看下需要用到的里面的几个方法:

?
1
2
3
4
5
public boolean trycaptureview(view child, int pointerid) {}
public int getviewhorizontaldragrange(view child) {}
public int clampviewpositionhorizontal(view child, int left, int dx) {}
public void onviewpositionchanged(view changedview, int left, int top, int dx, int dy) {}
public void onviewreleased(view releasedchild, float xvel, float yvel) {}

上面的几个方法,会在代码中有详细的注释,在这里只是看下我们需要重写的方法

好了哈,说了这么多,我们就先来个简单的,就是可以实现拖拽(相信当你的两个view可以拖拽的时候,你会发现,哦这么简单几步么):

第一步实现拖拽功能(简单3步实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//1、通过静态方法初始化操作
mdraghelper = viewdraghelper.create(this, mcallback);
/**
* 2、传递触摸事件
*
* @param ev
* @return
*/
@override
public boolean onintercepttouchevent(motionevent ev) {
//让自己的控件自行判断是否去拦截
return mdraghelper.shouldintercepttouchevent(ev);
}
@override
public boolean ontouchevent(motionevent event) {
try {
//自己去处理触摸事件
mdraghelper.processtouchevent(event);
} catch (exception e) {
e.printstacktrace();
}
//返回true,这样才能持续接收,要不然我们不会传递而是被拦截了
return true;
}
 
viewdraghelper.callback mcallback = new viewdraghelper.callback() {
/**
* 根据返回结果决定当前child是否可以拖拽
* 尝试捕获的时候调用,如果返回的是主面板,那么负面版是不能被调用的
* @param child 当前被拖拽的view
* @param pointerid 区分多点触摸的id
* @return 返回true 是都可以拖拽
* 返回child == mleftcontent 左侧可以移动
* 返回child == mmaincontent 右侧可以移动
*/
@override
public boolean trycaptureview(view child, int pointerid) {
//这里要返回true,要不然不能拖拽
return true;
}
/**
* 根据建议值修正将要移动的位置 此时并没有发生真正的移动(左右)
*
* @param child 当前拖拽的view
* @param left 新的位置的建议值 oldleft + dx
* @param dx 变化量 和变化之前位置的差值
* @return
*/
@override
public int clampviewpositionhorizontal(view child, int left, int dx) {
//返回的拖拽的范围,不对拖拽进行真正的限制,仅仅决定了动画执行的速度
return left;
}
};

好了,第一个效果图可以出来了,就是可以拖拽了,是不是 很简单的就实现了呢:

Android使用ViewDragHelper实现仿QQ6.0侧滑界面(一)

但是,你要是做成这样提交任务,是不是不想干活了哈,好了下面我们就来控制一下拖拽位置,不能让他乱拖拽了哈。、

第二步,控制拖拽范围

我们想要控制拖拽范围,首先我们得需要拿到这两个控件,取到有关这两个控件的属性,我们才能去操作。于是我们重写了一下的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* finalize inflating a view from xml. this is called as the last phase
* of inflation, after all child views have been added.
* 当xml填充完的时候去掉用,在这里我们可以找到我们要去操控的那两个布局
* <p>even if the subclass overrides onfinishinflate, they should always be
* sure to call the super method, so that we get called.
*/
@override
protected void onfinishinflate() {
super.onfinishinflate();
/**
* 根据索引来找
*/
/**
* 得到左边的布局
*/
mleftcontent = (viewgroup) getchildat(0);
/**
* 得到主main布局
*/
mmaincontent = (viewgroup) getchildat(1);
}

接下来我们就要来得到有关宽和高了,我们知道onmessure()中可以获取,不过查看了一下,下面的这个方法也是可以获取到的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 当尺寸变化的时候去调用
* this is called during layout when the size of this view has changed
* @param w
* @param h
* @param oldw
* @param oldh
*/
@override
protected void onsizechanged(int w, int h, int oldw, int oldh) {
super.onsizechanged(w, h, oldw, oldh);
/**
* 屏幕的宽度和高度
*/
mheight = getmeasuredheight();
mwidth = getmeasuredwidth();
/**
* 自定义左侧view拖拽出来的距离
*/
mrange = (int) (mwidth * 0.7);
}

好了,有关我们需要的宽、高、拖拽距离我们已经获取到了,那么我们接下来就要来限制了。

我们只需要在clampviewpositionhorizontal()方法中加入这个,就能限制主面板不能往左移,左面板只能移动到右侧的mrange位置。(这里可以去尝试一下哈)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (child == mmaincontent) {
left = fixleft(left);
}
 
/**
* 修正方法
* 根据范围去修正左侧的view的可见
*
* @param left
* @return
*/
private int fixleft(int left) {
if (left < 0) {
return 0;
} else if (left > mrange) {
return mrange;
}
return left;
}

这个时候,我们基本上的大致框架已经出来了,但是还是有一个问题,那就是虽然我们达到了滑动过的效果(右侧的定了,但是当我们把leftview滑动出来的时候,还是可以往右滑)我们要想办法去限制,就是限制,左侧的view我只能右滑mrange的距离这个时候我们就要查看方法了,哪个方法可以拿到变化的position的值,然后去改变:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 当view位置改变的时候,处理要做的事情,更新状态,伴随动画,重绘界面
*
* 此时view已经发生了位置的改变
*
* @param changedview 改变的位置view
* @param left 新的左边值
* @param top
* @param dx 水平变化量
* @param dy
*/
@override
public void onviewpositionchanged(view changedview, int left, int top, int dx, int dy) {
super.onviewpositionchanged(changedview, left, top, dx, dy);
int newleft = left;
//如果我们拖拽的是左面板
if (changedview == mleftcontent) {
//新的左侧位置是我们的主面板的左侧加上水平变化量
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);
}
//兼容低版本 强制重绘
invalidate();
}

好了,这个时候我们的大致效果已经出来了,看下效果图。(这里的中间出来的白到属于录制问题,亲测没问题)

Android使用ViewDragHelper实现仿QQ6.0侧滑界面(一)

到这里,大致的拖拽已经可以实现了,当然了哈,我的现在布局就是下面的简单的实现(要先加一些控件,这个自己在两个linearlayout中加入即可)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="utf-8"?>
<com.example.qqsliding.draglayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.qqsliding.mainactivity">
<linearlayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/sidebar_bg">
</linearlayout>
<linearlayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">
<relativelayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#0cb8f6"
android:gravity="center_vertical">
<imageview
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginleft="15dp"
android:src="@mipmap/icon_avatar_white"/>
<textview
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerhorizontal="true"
android:text="header"/>
<imageview
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignparentright="true"
android:layout_marginright="15dp"
android:src="@mipmap/icon_search"/>
</relativelayout>
</linearlayout>
</com.example.qqsliding.draglayout>

但是细心的可能会发现,我们拖拽的时候,特别生硬,那是因为我们没有加上动画效果,只是生生的给拖拽出来了,具体的加入动画,定义回调效果,请通过本文学习android滑动优化高仿qq6.0侧滑菜单(二),希望本文分享对大家有所帮助。