Android DrawerLayout Plus enhanced drawer menu, androiddrawerlayout

Source: Internet
Author: User

Android DrawerLayout Plus enhanced drawer menu, androiddrawerlayout
Version: 1.0 Date: 2015/2/2 2015/2/3 2015/2/26 2015/2/27 copyright:©Kince
I. Overview
DrawerLayout is an official slide menu, which is more lightweight than SliddingMenu. By default, DrawerLayout can be set to slide the menu from the left or right. As follows,

Xml layout:

<!--<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. --><com.sys.app.uikit.drawerlayoutplus.DrawerLayoutPlus xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/drawer_layout"    android:layout_width= "match_parent"    android:layout_height= "match_parent" >    <!--         As the main content view, the view below consumes the entire         space available using match_parent in both dimensions.    -->    <FrameLayout        android:id="@+id/content_frame"        android:layout_width="match_parent"        android:layout_height="match_parent" />    <!--         android:layout_gravity="start" tells DrawerLayout to treat         this as a sliding drawer on the left side for left-to-right         languages and on the right side for right-to-left languages.         The drawer is given a fixed width in dp and extends the full height of         the container. A solid background is used for contrast         with the content view.    -->    <!-- Left drawer -->    <ListView        android:id="@+id/left_drawer"        android:layout_width="240dp"        android:layout_height="match_parent"        android:layout_gravity="left"        android:background="#111"        android:choiceMode="singleChoice"        android:divider="@android:color/transparent"        android:dividerHeight="0dp" />    <!-- Right drawer -->    <ListView        android:id="@+id/right_drawer"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="right"        android:choiceMode="singleChoice" /></com.sys.app.uikit.drawerlayoutplus.DrawerLayoutPlus>
Activity Code:
package com.sys.app.uikit.drawerlayoutplus;import java.util.Locale;import android.app.Activity;import android.app.Fragment;import android.app.FragmentManager;import android.app.SearchManager;import android.content.Intent;import android.content.res.Configuration;import android.os.Bundle;import android.support.v4.view.GravityCompat;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.Toast;public class MainActivity extends Activity {     private DrawerLayoutPlus mDrawerLayout;     private ListView mLeftDrawerList, mRightDrawerList;     private ActionBarDrawerToggle mDrawerToggle;     private CharSequence mDrawerTitle;     private CharSequence mTitle;     private String[] mPlanetTitles;     @Override     protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          mTitle = mDrawerTitle = getTitle();          mPlanetTitles = getResources().getStringArray(R.array.planets_array);          mDrawerLayout = (DrawerLayoutPlus) findViewById(R.id.drawer_layout);          mLeftDrawerList = (ListView) findViewById(R.id.left_drawer);          mRightDrawerList = (ListView) findViewById(R.id.right_drawer);          // set a custom shadow that overlays the main content when the drawer          // opens          mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);          // set up the drawer's list view with items and click listener          mLeftDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles));          mLeftDrawerList.setOnItemClickListener(new DrawerItemClickListener());          mRightDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles));          mRightDrawerList.setOnItemClickListener(new DrawerItemClickListener());          // enable ActionBar app icon to behave as action to toggle nav drawer          getActionBar().setDisplayHomeAsUpEnabled(true);          getActionBar().setHomeButtonEnabled(true);          // ActionBarDrawerToggle ties together the the proper interactions          // between the sliding drawer and the action bar app icon          mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */          mDrawerLayout, /* DrawerLayout object */          R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */          R.string.drawer_open, /* "open drawer" description for accessibility */          R.string.drawer_close /* "close drawer" description for accessibility */          ) {               public void onDrawerClosed(View view) {                    getActionBar().setTitle(mTitle);                    invalidateOptionsMenu(); // creates call to                                                       // onPrepareOptionsMenu()               }               public void onDrawerOpened(View drawerView) {                    getActionBar().setTitle(mDrawerTitle);                    invalidateOptionsMenu(); // creates call to                                                       // onPrepareOptionsMenu()               }          };          mDrawerLayout.setDrawerListener(mDrawerToggle);          if (savedInstanceState == null) {               selectItem(0);          }     }     @Override     public boolean onCreateOptionsMenu(Menu menu) {          MenuInflater inflater = getMenuInflater();          inflater.inflate(R.menu.main, menu);          return super.onCreateOptionsMenu(menu);     }     /* Called whenever we call invalidateOptionsMenu() */     @Override     public boolean onPrepareOptionsMenu(Menu menu) {          // If the nav drawer is open, hide action items related to the content          // view          boolean drawerLeftOpen = mDrawerLayout.isDrawerOpen(mLeftDrawerList);          boolean drawerRightOpen = mDrawerLayout.isDrawerOpen(mRightDrawerList);          menu.findItem(R.id.action_websearch).setVisible(!(drawerLeftOpen && drawerRightOpen));          return super.onPrepareOptionsMenu(menu);     }     @Override     public boolean onOptionsItemSelected(MenuItem item) {          // The action bar home/up action should open or close the drawer.          // ActionBarDrawerToggle will take care of this.          if (mDrawerToggle.onOptionsItemSelected(item)) {               return true;          }          // Handle action buttons          switch (item.getItemId()) {          case R.id.action_websearch:               // create intent to perform web search for this planet               Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);               intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());               // catch event that there's no activity to handle intent               if (intent.resolveActivity(getPackageManager()) != null) {                    startActivity(intent);               } else {                    Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();               }               return true;          default:               return super.onOptionsItemSelected(item);          }     }     /* The click listner for ListView in the navigation drawer */     private class DrawerItemClickListener implements ListView.OnItemClickListener {          @Override          public void onItemClick(AdapterView<?> parent, View view, int position, long id) {               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          mLeftDrawerList.setItemChecked(position, true);          mRightDrawerList.setItemChecked(position, true);          setTitle(mPlanetTitles[position]);          mDrawerLayout.closeDrawer(mLeftDrawerList);          mDrawerLayout.closeDrawer(mRightDrawerList);     }     @Override     public void setTitle(CharSequence title) {          mTitle = title;          getActionBar().setTitle(mTitle);     }     /**     * When using the ActionBarDrawerToggle, you must call it during     * onPostCreate() and onConfigurationChanged()...     */     @Override     protected void onPostCreate(Bundle savedInstanceState) {          super.onPostCreate(savedInstanceState);          // Sync the toggle state after onRestoreInstanceState has occurred.          mDrawerToggle.syncState();     }     @Override     public void onConfigurationChanged(Configuration newConfig) {          super.onConfigurationChanged(newConfig);          // Pass any configuration change to the drawer toggls          mDrawerToggle.onConfigurationChanged(newConfig);     }     /**     * Fragment that appears in the "content_frame", shows a planet     */     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;          }     }}
Effect:
Figure-1 if you want to change to another one, the menu is displayed below, which cannot be done by using the system's DrawerLayout, but you can modify its source code to achieve the goal.
2. Analyze DrawerLayout as a parent class container, which can contain child views, which can be divided into content areas and menu areas. The content area occupies the entire screen size, and the menu area is inside the screen by default. DrawerLayout inherits from ViewGroup and must overwrite the onLayout () method. When you set the sub-View position, the content area is displayed directly by default, and the menu area is hidden inside the screen by default. The onLayout () Code of DrawerLayout is as follows:
@Override     protected void onLayout(boolean changed, int l, int t, int r, int b) {           mInLayout = true ;           final int width = r - l;           final int childCount = getChildCount();           for (int i = 0; i < childCount; i++) {               final View child = getChildAt(i);               if (child.getVisibility() == GONE) {                    continue;              }               final LayoutParams lp = (LayoutParams) child.getLayoutParams();               if (isContentView(child)) {                   child.layout(lp. leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(),                             lp. topMargin + child.getMeasuredHeight());              } else { // Drawer, if it wasn't onMeasure would have thrown an                              // exception.                    final int childWidth = child.getMeasuredWidth();                    final int childHeight = child.getMeasuredHeight();                    int childLeft;                    final float newOffset;                    if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {                        childLeft = -childWidth + ( int) (childWidth * lp.onScreen);                        newOffset = ( float) (childWidth + childLeft) / childWidth;                   } else { // Right; onMeasure checked for us.                        childLeft = width - ( int) (childWidth * lp.onScreen);                        newOffset = ( float) (width - childLeft) / childWidth;                   }                                   final boolean changeOffset = newOffset != lp.onScreen;                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK ;                    switch (vgrav) {                    default:                    case Gravity.TOP : {                        child.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);                         break;                   }                    case Gravity.BOTTOM : {                         final int height = b - t;                        child.layout(childLeft, height - lp.bottomMargin - child.getMeasuredHeight(), childLeft + childWidth,                                  height - lp.bottomMargin);                         break;                   }                    case Gravity.CENTER_VERTICAL : {                         final int height = b - t;                         int childTop = (height - childHeight) / 2;                         // Offset for margins. If things don't fit right because of                         // bad measurement before, oh well.                         if (childTop < lp.topMargin ) {                             childTop = lp. topMargin;                        } else if (childTop + childHeight > height - lp.bottomMargin ) {                             childTop = height - lp.bottomMargin - childHeight;                        }                        child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);                         break;                   }                   }                    if (changeOffset) {                        setDrawerViewOffset(child, newOffset);                   }                    final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE ;                    if (child.getVisibility() != newVisibility) {                        child.setVisibility(newVisibility);                   }              }          }           mInLayout = false ;           mFirstLayout = false ;     }
In the onLayout method, you can use the parameter to obtain the width and height of the previously measured DrawerLayout, obtain the number of face views in DrawerLayout, and set the position of the subview through a for loop. You can see that the position of the content area is set first, and then the menu area. The latter sets the offset to hide the menu. So this is the key. If you want to bring up a menu below the screen, you need to modify the display position of the menu so that it is placed below the screen. The menu View position is changed, followed by the gesture operation part. In DrawerLayout, the class ViewDragHelper is used to drag a View. First, it declares the help class and callback of the left and right menus,
 private final ViewDragHelper mLeftDragger ;      private final ViewDragHelper mRightDragger ;      private final ViewDragCallback mLeftCallback ;      private final ViewDragCallback mRightCallback ;

And left and right locks and shadows,
 private int mLockModeLeft ;      private int mLockModeRight ;      private Drawable mShadowLeft ;      private Drawable mShadowRight ;
The help class and callback are initialized in the constructor,
  mLeftCallback = new ViewDragCallback(Gravity.LEFT );           mRightCallback = new ViewDragCallback(Gravity.RIGHT );           mLeftDragger = ViewDragHelper.create( this , TOUCH_SLOP_SENSITIVITY , mLeftCallback );           mLeftDragger .setEdgeTrackingEnabled(ViewDragHelper. EDGE_LEFT);           mLeftDragger .setMinVelocity(minVel);           mLeftCallback .setDragger(mLeftDragger );           mRightDragger = ViewDragHelper.create( this , TOUCH_SLOP_SENSITIVITY , mRightCallback );           mRightDragger .setEdgeTrackingEnabled(ViewDragHelper. EDGE_RIGHT);           mRightDragger .setMinVelocity(minVel);           mRightCallback .setDragger(mRightDragger );
When setting the shadow effect, make a left or right judgment.
 public void setDrawerShadow(Drawable shadowDrawable, @EdgeGravity int gravity) {           /*           * TODO Someone someday might want to set more complex drawables here.           * They're probably nuts, but we might want to consider registering           * callbacks, setting states, etc. properly.           */           final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection( this));           if ((absGravity & Gravity. LEFT) == Gravity. LEFT) {               mShadowLeft = shadowDrawable;              invalidate();          }           if ((absGravity & Gravity. RIGHT) == Gravity.RIGHT ) {               mShadowRight = shadowDrawable;              invalidate();          }     }
There are also menu locks,
  public void setDrawerLockMode(@LockMode int lockMode) {          setDrawerLockMode(lockMode, Gravity. LEFT );          setDrawerLockMode(lockMode, Gravity. RIGHT );     }
It is also about making judgments,
public void setDrawerLockMode(@LockMode int lockMode, @EdgeGravity int edgeGravity) {           final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity, ViewCompat.getLayoutDirection( this));           if (absGravity == Gravity. LEFT) {               mLockModeLeft = lockMode;          } else if (absGravity == Gravity. RIGHT) {               mLockModeRight = lockMode;          }           if (lockMode != LOCK_MODE_UNLOCKED) {               // Cancel interaction in progress               final ViewDragHelper helper = absGravity == Gravity.LEFT ? mLeftDragger : mRightDragger ;              helper.cancel();          }           switch (lockMode) {           case LOCK_MODE_LOCKED_OPEN :               final View toOpen = findDrawerWithGravity(absGravity);               if (toOpen != null) {                   openDrawer(toOpen);              }               break ;           case LOCK_MODE_LOCKED_CLOSED :               final View toClose = findDrawerWithGravity(absGravity);               if (toClose != null) {                   closeDrawer(toClose);              }               break ;           // default: do nothing          }     }
This is not only the method above, but also the many methods in DrawerLayout. Gravity is used to determine whether it is the left menu or the right menu. If you want to change it to the pop-up menu from the BOTTOM, replace the corresponding value with Gravity. BOTTOM. Of course, pay attention to some logic problems in the replacement process to avoid leakage. For example, if the offset value is left or right, it must be changed to up or down. Therefore, the value must be recalculated. There are also variable names, such as left or right, and now change to bottom. After modification, the running effect is as follows:







Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.