Personal blog on programming | mainly Android, Java
Each Navigation Drawer Hides a ViewDragHelper
Recently, at the Google I/O 2013, two new layouts have been introduced: SlidingPaneLayout, a view that can be dragged from bottom to top and vice versa and the DrawerLayout, now used in almost all Google applications. Both of these use a new concept to more easily manage dragging: the ViewDragHelper.
In this article, I’m going to talk about the ViewDragHelper (aka VDH) because making a custom layout with dragging child view may be pain sometimes. First, I will show you how to use it and how it works (the main lines). Secondly, I will expose you a use case where the VDH is really useful.
It uses some common classes of the framework : a VelocityTracker for tracking fingling and other touch events and a Scroller to scroll views when it’s needed.
You must read the source code as much as possible because first, it’s very interesting and then if you know how it works, you will be able to use it in a better way.
Using the VDH
In this section, I’m going to show you a few examples of what is possible to configure on a VDH. Let’s begin with some initializations and then, I will explain a few possible configurations.
VDH’s initialization
A custom ViewGroup extending a LinearLayout (DragLayout) with a simple child View (named mDragView).
Create a VDH with its callback. Note that you can specify the sensivity (official documentation says Multiplier for how sensitive the helper should be about detecting the start of a drag. Larger values are more sensitive. 1.0f is normal.)
Now, you can change the VDH behavior just by configuring the callback.
Horizontal only
Implements clampViewPositionHorizontal to allow horizontal drag and to bound the drag motion. Note that documentation says The default implementation does not allow horizontal motion.
You have to take margins and parent padding into consideration.
Implements clampViewPositionVertical to allow horizontal drag and to bound the drag motion. Note that documentation says The default implementation does not allow vertical motion.
You have to take margins and parent padding into consideration. Not like in the code below
Implements tryCaptureView to allow a child view to be captured. Here, there are two child views (mDragView1 and mDragView2) but only one (mDragView1) is draggable.
Implements getViewHorizontalDragRange or getViewVerticalDragRange to returns the range of horizontal|vertical drag in pixels. This range is used by the VDH when you call smoothSlideViewTo or settleCapturedViewAt to calculate the scroll duration. Also, it’s used to check the horizontal|vertical touch slop.
Edge dragging
This feature is used in the DrawerLayout with EDGE_LEFT and EDGE_RIGHT.
Implements onEdgeDragStarted called when a real drag from the configured edge has started. Note that at this time, no child view is currently captured. In this method, you have to capture a child view manually.
Recently, I’ve received an update of the Youtube app on my phone. Before this update, the most annoying thing was to not be able to watch a video and search the next video at the same time. They fixed this by implementing a nice layout in which you can minimize the video view from top to bottom.
I’m going to show how to do it and how it’s simple thanks to VDH.
Here is the expected result
Key points:
tryCaptureView returns true only for the header view
drag range is calculated onLayout
use VDH’s methods in onInterceptTouchEvent and onTouchEvent
call continueSettling in computeScroll (because VDH uses a scroller)
use smoothSlideViewTo to finish the drag motion
Be careful, this layout is not well made: it’s a draft. There is still work to do; on the scale part, touch event when the header is scaled, onLayout and onMeasure are badly written too. Also, I don’t know if calling requestLayout in onViewPositionChanged is good solution… Anyway, if you have remarks or ideas to improve this layout, please tell me!).
The VDH is one of the useful but unknown class in the framework. Don’t hesitate to try it, use it and to appreciate it because it saves a lot of time and a lot of code!