Home >Java >javaTutorial >Android uses ViewDragHelper to implement a side-sliding interface imitating QQ6.0 (1)
QQ is a chat tool that everyone cannot live without. It is both convenient and practical. Since QQ was updated to 6.0, the main panel has been reduced from the original scratched-out main panel to left and right smooth, which has greatly improved the appearance. , so I tried to understand the various logics inside, combined with relevant information, and researched.
We know that one of the main classes here is ViewDragHelper, so first we need to understand this ViewDragHelper class. As the saying goes, hit the snake with seven inches, let’s first take a look at how the official document introduces it. What is there? Strange features.
First inherit:
java.lang.Object
android.support.v4.widget.ViewDragHelper
The direct parent class is Object.
Class Overview
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.
It is a tool class for writing custom ViewGroup. This province provides many useful methods and states to allow users to drag and draw their trajectories and positions in the parent ViewGroup.
Nested Classes
ViewDragHelper.Callback
A Callback is used as a communication channel with the ViewDragHelper back to the parent view using it.
A callback is used as an interface for communication between ViewDragHelper and his parent view
A public static method:
We can know that ViewDragHelper is constructed through the create() method. This will be introduced in detail later.
Let us take a look at several methods that need to be used:
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) {}
The above methods will have detailed comments in the code. Here we just take a look. Methods that need to be rewritten
Okay, having said so much, let’s start with a simple one that can realize dragging (I believe that when your two views can be dragged, You will find that it is so simple in just a few steps):
The first step is to implement the drag and drop function (3 simple steps to implement)
//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; } };
Okay, the first rendering is ready. It can be dragged and dropped. Isn’t it very simple to implement?
But if you submit the task like this, you won’t want to work anymore, okay? Next, let's control the dragging position so that he can't drag it randomly. ,
The second step is to control the drag range
We want to control the drag range. First we need to get these two controls and get the properties of these two controls. Only then can we operate. So we rewrote the method:
/** * 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); }
Next we have to get the width and height. We know that it can be obtained in onMessure(), but after checking it, the following method can also be obtained. Arrived:
/** * 当尺寸变化的时候去调用 * 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); }
Okay, we have obtained the width, height, and drag distance we need, then we will limit it next.
We only need to add this to the clampViewPositionHorizontal() method to restrict the main panel from moving to the left, and the left panel can only move to the mRange position on the right. (You can try it here)
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; }
At this time, our basic general framework has come out, but there is still a problem, that is, although we have achieved the sliding effect (the one on the right is fixed , but when we slide the LeftView out, we can still slide it to the right) We have to find a way to limit it, that is, to limit it. I can only slide the view on the left to the right by the distance of mRange. At this time we have to check the method, which method You can get the value of the changed position, and then change it:
/** * 当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(); }
Okay, at this time our general effect has come out, let’s take a look at the rendering. (The white color in the middle here is a recording problem, and there is no problem in personal testing)
At this point, a rough drag and drop can be achieved. Of course, my current layout is the simple implementation below ( You need to add some controls first, just add them to the two LinearLayouts yourself)
<?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>
But those who are careful may find that when we drag, it is particularly stiff. That is because we have not added animation effects. It's just dragged out. To add specific animations and define callback effects, please read this article to learn Android sliding optimization high imitation QQ6.0 side sliding menu (2). I hope sharing this article will be helpful to everyone.
For more Android using ViewDragHelper to implement a QQ6.0-like side sliding interface (1), please pay attention to the PHP Chinese website for related articles!