ViewDragHelper practices-similar to the official Android navigation menu NavigationDrawer effect and android navigation menu
I believe that users who often use mobile apps are familiar with application scenarios such as slide menu bar, drop-down, pop-up, and pop-up. Almost mainstream mobile apps can be seen in IOS and Android. in the past 2.3, many third parties, such as sjavasingmenu, MenuDrawer, and ActionbarSherlock, enriched and deepened the interaction concept to a large extent. allows a small screen to accommodate more interactive interfaces. this is also the trend. Android officially launched DrawerLayout in v4. it indicates the importance and affirmation of the slide.
Nagging at this point. I checked the source code and official example of DrawerLayout. the officially provided DrawerLayout has been well encapsulated and can be used as an example. the implementation principle is to use ViewDragHelper mentioned above to implement it. viewDragHelper uses View and Scroller to achieve the real dragging and moving effect. to deepen my understanding of ViewDragHelper, I will also follow the official NavigationDrawer example. if you have not done well, please advise. thank you.
The principle is the same as that of NavigationDrawer, and most of the images use official examples. the key to implementing NavigationDrawer is DrawerLayout, which uses ViewDragHelper. of course, not only ViewDragHelper, but also other helper classes. because NavigationDrawer is made in combination with ActionBar, ActionBarDrawerToggle is also used as the switch to switch the slide menu. but in fact, it is enough to achieve the effect similar to the official one by using ViewDragHelper. because our goal is to learn ViewDragHelper. if you do not know about ViewDragHelper, you can go to the previous article or the official website to see the document for more information.
First (amount, screen recording on the mobile phone, and then horizontal screen effect changed =, =)
The code below:
Layout file:
Activity_main.xm
<com.alextam.simpleslidingmenu.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg" /> <LinearLayout android:id="@+id/ly_main_a" android:layout_width="240dp" android:layout_height="match_parent" android:orientation="vertical" android:background="#888888" android:alpha="0.0" android:layout_gravity="start" android:layout_marginLeft="-220dp" android:padding="20dp" > <ListView android:id="@+id/listview_main" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout></com.alextam.simpleslidingmenu.SlidingMenu>
After reading the source code of the official example, it is not difficult to understand why the Layout is like this, because we need to hide the Layout of the side menu bar outside the screen. I also used this idea for reference.
DrawerLayout is an officially encapsulated View class, so I also used ViewDragHelper to encapsulate similar views, sjavasingmenu.
Public class sshortingmenu extends FrameLayout {private static final String TAG = "sshortingmenu"; private ViewDragHelper mDragHelper; private int minValue = 20; // edge touchable critical value private int leftEdgeMinSize = minValue; // The LEFT (LEFT) value of the sub-view is private int leftValue; private View childViewA; // private View childViewBG; private boolean slidingMenuOpenSate = false; // note the following points, // If mDragHelper. settleCapturedViewAt (le Ft, top); Method to move the View. You must use invalidate () to refresh the View. public SlidingMenu (Context context) {this (context, null);} public SlidingMenu (Context context, AttributeSet attrs) {this (context, attrs, 0 );} public SlidingMenu (Context context, AttributeSet attrs, int defStyleAttr) {super (context, attrs, defStyleAttr); init ();} private void init () {// to improve compatibility, the new ViewDragHelper () creation method is private. You can only Create an object using the Create () Factory method. MDragHelper = ViewDragHelper. create (this, 1.0f, new sCallBack (); int eValue = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, minValue, getResources (). getDisplayMetrics (); leftEdgeMinSize = eValue> minValue? EValue: minValue;} @ Override protected void onFinishInflate () {childViewA = findViewById (R. id. ly_main_a); // childViewBG = findViewById (R. id. content_frame);}/*** drag listener interface. To use ViewDragHelper, you must implement this interface class */private class sCallBack extends ViewDragHelper. callback {// This method must be implemented @ Override public boolean tryCaptureView (View child, int pointerId) {return childViewA = child ;}@ Override public void onViewD RagStateChanged (int state) {if (state = ViewDragHelper. STATE_IDLE) {// IDLE if (childViewA. getLeft ()> = 0) {slidingMenuOpenSate = true ;}} else if (state = ViewDragHelper. STATE_DRAGGING) {// Drag} else if (state = ViewDragHelper. STATE_SETTLING) {// Settle }}@ Override public void onViewPositionChanged (View changedView, int left, int top, int dx, int dy) {if (changedView! = Null) {float phosphatase = (float) (1 + (float) Math. abs (left)/leftValue); if (left <= leftValue) {changedView. setAlpha (0.0f);} else {changedView. setAlpha (phosphatase) ;}}@ Override public void onViewCaptured (View capturedChild, int activePointerId) {} // The method @ Override public void onViewReleased (view releasedChild, float xvel, float yvel) {if (xvel <leftValue/3) will be called back when the gesture is released) {closeMenu ();} else if (xvel + LeftValue/3)> 0) {openMenu ();} else {if (releasedChild. getLeft ()> (leftValue-leftValue/3) {openMenu ();} else {closeMenu () ;}}@ Override public void onEdgeTouched (int edgeFlags, int pointerId) {}@ Override public boolean onEdgeLock (int edgeFlags) {return false ;}@ Override public void onEdgeDragStarted (int edgeFlags, int pointerId) {}@ Override public int getOrderedChildIndex (int inde X) {return index ;}@ Override public int getViewHorizontalDragRange (View child) {return 0 ;}@ Override public int getViewVerticalDragRange (View child) {return leftValue ;} // an important method to implement horizontal drag. The returned value is the value that implements horizontal drag of the child view @ Override public int clampViewPositionHorizontal (View child, int left, int dx) {final int paddingLeft = getPaddingLeft (); // you can drag a child view without exceeding the left and right edges of the parent view. // If you directly return left. however, the child view can be dragged out of the parent Outside view. // final int resultLeft = Math. min (Math. max (paddingLeft, left), // getWidth ()-getChildAt (1 ). getWidth (); // return resultLeft; final int resultLeft = Math. max (leftValue, Math. min (left, 0); return resultLeft;} // an important method for vertical dragging @ Override public int clampViewPositionVertical (View child, int top, int dy) {final int paddingTop = getPaddingTop (); final int resultTop = Math. min (Math. max (paddingTo P, top), getHeight ()-childViewA. getHeight (); return resultTop ;}@ Override public boolean onInterceptTouchEvent (MotionEvent ev) {final int action = MotionEventCompat. getActionMasked (ev); if (action = MotionEvent. ACTION_CANCEL | action = MotionEvent. ACTION_UP) {if (mDragHelper! = Null) // cancel or open your finger. Both should be cancel () mDragHelper. cancel (); return false;} return mDragHelper. shouldInterceptTouchEvent (ev);} @ Override public boolean onTouchEvent (MotionEvent ev) {if (ev. getAction () = MotionEvent. ACTION_UP | ev. getAction () = MotionEvent. ACTION_CANCEL) {if (childViewA! = Null) {if (slidingMenuOpenSate & ev. getX ()> childViewA. getWidth () {closeMenu () ;}} if (mDragHelper! = Null) {mDragHelper. processTouchEvent (ev); return true;} return false;} @ Override public void computeScroll () {if (mDragHelper. continueSettling (true) {ViewCompat. postInvalidateOnAnimation (this) ;}@ Override protected void onLayout (boolean changed, int l, int t, int r, int B) {super. onLayout (changed, l, t, r, B); leftValue = leftEdgeMinSize-childViewA. getWidth ();} protected void openMenu () {If (mDragHelper! = Null) {if (mDragHelper. smoothSlideViewTo (childViewA,) {ViewCompat. postInvalidateOnAnimation (this); listener = true ;}} protected void closeMenu () {if (mDragHelper! = Null) {if (mDragHelper. smoothSlideViewTo (childViewA, leftValue, 0) {ViewCompat. postInvalidateOnAnimation (this); slidingMenuOpenSate = false ;}}// you can call this operation to obtain the public boolean getSlidingMenuOpenSate () {return slidingMenuOpenSate ;}}
There are three key points in the entire process. One is to implement the CallBack interface class provided in ViewDragHelper. The CallBack interface provides a listening method for the entire View dragging or settle process, very effective. the second is to handle the onTouch event of gesture touch. third, set the edge size of the View to be dragged as needed. this value directly determines whether the view can quickly and effectively locate the gesture.
The purpose is to familiarize yourself with the ViewDragHelper class and implement the slide effect. Therefore, the SimpleSlidingMenu class is not encapsulated and complicated. You can still see the idea clearly. for unfamiliar things, I usually come out first. As for good performance, how to optimize it, and so on, I will wait for a rough product to come out first. otherwise, Hu thinks a lot of things and does not do anything. The efficiency is too low. of course, the main idea is still necessary.
Reference array in strings. xml:
<string-array name="planets_array"> <item>Mercury</item> <item>Venus</item> <item>Earth</item> <item>Mars</item> <item>Jupiter</item> <item>Saturn</item> <item>Uranus</item> <item>Neptune</item> </string-array>
Then MainAcitvity
Public class MainActivity extends Activity {private LinearLayout linearLayout; private ListView; private SlidingMenu drawerLayout; private String [] mPlanetTitles; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); drawerLayout = (SlidingMenu) findViewById (R. id. drawer_layout); linearLayout = (LinearLayou T) findViewById (R. id. ly_main_a); mPlanetTitles = getResources (). getStringArray (R. array. planets_array); listView = (ListView) findViewById (R. id. listview_main); listView. setAdapter (new ArrayAdapter <String> (this, R. layout. drawer_list_item, mPlanetTitles); listView. setOnItemClickListener (new DrawerItemClickListener ();} private class DrawerItemClickListener implements ListView. onItemClickListener {@ Ove Rride public void onItemClick (AdapterView <?> Parent, View view, int position, long id) {listView. setSelection (position); selectItem (position) ;}} private void selectItem (int position) {// update the main content by replacing fragments Fragment fragment = new PlanetFragment (); bundle args = new Bundle (); args. putInt (PlanetFragment. ARG_PLANET_NUMBER, position); fragment. setArguments (args); FragmentManager fragmentManager = getFragmentManager (); fragmentManager. beginTransaction (). replace (R. id. content_frame, fragment ). commit (); // update selected item and title, then close the drawer listView. setItemChecked (position, true); // equivalent to drawerLayout of the slide menu. closeMenu ();} public static class PlanetFragment extends Fragment {public static final String ARG_PLANET_NUMBER = "planet_number"; public PlanetFragment () {// Empty constructor required for fragment subclasses} @ Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View rootView = inflater. inflate (R. layout. fragment_planet, container, false); int I = getArguments (). getInt (ARG_PLANET_NUMBER); String planet = getResources (). getStringArray (R. array. planets_array) [I]; int imageId = getResources (). getIdentifier (planet. toLowerCase (Locale. getDefault (), "drawable", getActivity (). getPackageName (); (ImageView) rootView. findViewById (R. id. image )). setImageResource (imageId); getActivity (). setTitle (planet); return rootView ;}}}
Similar to NavigationDrawer, MainActivity uses Fragment to replace Layout of an ID. the source code of the methods used to create and replace PlanetFragment in MainActivity is the source code of the example. after all, I don't want to repeat the wheel. the difference is that the ListView is used as the slide menu directly, but this is a little limited. so I used Layout to install the ListView, so that the ListView can also be referenced. of course, other views or Layout can be installed in Layout.
Finally, it is a good choice for interaction to return to the side slide. the official team also provides guidance on this. Place the menu navigation of the application on the left and the function on the right. slide has been popular for a long time, and many mature third-party products are available now. viewDragHelper only provides an option for self-implementation. whether it is a good choice depends on the specific requirements and implementation. of course, ViewDragHelper is not limited to simply dragging a View or something. combine it with ViewGroup or some Layout classes to generate a container with rich drag and drop sliding effects. then, this article is written here. Thank you for reading this article. ^