DrawerLayout详解「建议收藏」

DrawerLayout详解「建议收藏」drawerLayout是SupportLibrary包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物。drawerLayout分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(drawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化(这需要使用者自己实现)。

大家好,又见面了,我是你们的朋友全栈君。

drawerLayoutSupport Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物。drawerLayout分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(drawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化(这需要使用者自己实现)。

DrawerLayout详解「建议收藏」

drawerLayout的使用很方便,使用drawerLayout的要点如下:

1.drawerLayout其实是一个布局控件,跟LinearLayout等控件是一种东西,但是drawerLayout带有滑动的功能。只要按照drawerLayout的规定布局方式写完布局,就能有侧滑的效果。

  1. <!-- 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. -->
  2. <android.support.v4.widget.DrawerLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/drawer_layout"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent">
  7. <!-- As the main content view, the view below consumes the entire
  8. space available using match_parent in both dimensions. -->
  9. <FrameLayout
  10. android:id="@+id/content_frame"
  11. android:layout_width="match_parent"
  12. android:layout_height="match_parent" />
  13. <!-- android:layout_gravity="start" tells DrawerLayout to treat
  14. this as a sliding drawer on the left side for left-to-right
  15. languages and on the right side for right-to-left languages.
  16. The drawer is given a fixed width in dp and extends the full height of
  17. the container. A solid background is used for contrast
  18. with the content view. -->
  19. <ListView
  20. android:id="@+id/left_drawer"
  21. android:layout_width="240dp"
  22. android:layout_height="match_parent"
  23. android:layout_gravity="start"
  24. android:choiceMode="singleChoice"
  25. android:divider="@android:color/transparent"
  26. android:dividerHeight="0dp"
  27. android:background="#111"/>
  28. </android.support.v4.widget.DrawerLayout>

有两点要注意:主内容区的布局代码要放在侧滑菜单布局的前面,这可以帮助DrawerLayout判断谁是侧滑菜单,谁是主内容区;侧滑菜单的部分的布局(这里是ListView)可以设置layout_gravity属性,他表示侧滑菜单是在左边还是右边。

2.drawerLayout左侧菜单(或者右侧)的展开与隐藏可以被DrawerLayout.DrawerListener的实现监听到,这样你就可以在菜单展开与隐藏反生的时刻做一些希望做的事情,比如更新actionbar菜单等。如果你的activityactionbar的话,还是建议你用ActionBarDrawerToggle来监听,ActionBarDrawerToggle实现了DrawerListener,所以他能做DrawerListener可以做的任何事情,同时他还能将drawerLayout的展开和隐藏与actionbarapp 图标关联起来,当展开与隐藏的时候图标有一定的平移效果,点击图标的时候还能展开或者隐藏菜单。

  1. mDrawerToggle = new ActionBarDrawerToggle(
  2. this, /* host Activity */
  3. mDrawerLayout, /* DrawerLayout object */
  4. R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
  5. R.string.drawer_open, /* "open drawer" description for accessibility */
  6. R.string.drawer_close /* "close drawer" description for accessibility */
  7. ) {
  8. public void onDrawerClosed(View view) {
  9. getActionBar().setTitle(mTitle);
  10. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  11. }
  12. public void onDrawerOpened(View drawerView) {
  13. getActionBar().setTitle(mDrawerTitle);
  14. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  15. }
  16. };
  17. mDrawerLayout.setDrawerListener(mDrawerToggle);


3.何为侧边菜单。

侧边菜单其实只是一个普通的View,一般里面装的是ListView,看起来就像菜单,他完全可以是一个buttontextView等等。虽然称为菜单,但跟Activity的菜单形式是两码事,Activity的菜单只需要在资源文件中定义好,就能按照固定的形式显示出来。而drawerLayout的侧边菜单显示成什么样完全是取决于你自己,同样点击事件也完全由你自己去写。如下代码所示我们的侧边菜单是一个ListView显示的:

  1. mDrawerList.setAdapter(new ArrayAdapter<String>(this,
  2. R.layout.drawer_list_item, mPlanetTitles));
  3. mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
  4. /* The click listner for ListView in the navigation drawer */
  5. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  6. @Override
  7. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  8. selectItem(position);
  9. }
  10. }
  11. private void selectItem(int position) {
  12. // update the main content by replacing fragments
  13. Fragment fragment = new PlanetFragment();
  14. Bundle args = new Bundle();
  15. args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
  16. fragment.setArguments(args);
  17. FragmentManager fragmentManager = getFragmentManager();
  18. fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
  19. // update selected item and title, then close the drawer
  20. mDrawerList.setItemChecked(position, true);
  21. setTitle(mPlanetTitles[position]);
  22. mDrawerLayout.closeDrawer(mDrawerList);
  23. }


4.在代码中主动展开与隐藏侧边菜单。

在点击侧边菜单选项的时候我们往往需要隐藏菜单来显示整个菜单对应的内容。DrawerLayout.closeDrawer方法用于隐藏侧边菜单,DrawerLayout.openDrawer方法用于展开侧边菜单(参见第3点中的代码部分)

5.如何在菜单展开或者隐藏的时候更新activitymenu

上面的的第2点讲到DrawerLayout.DrawerListener监听展开与隐藏事件,在监听的回调方法中我们用invalidateOptionsMenu通知activity重绘menu,然后activity就有机会在onPrepareOptionsMenu方法中更新menu元素的显示与隐藏

代码:

  1. /* Called whenever we call invalidateOptionsMenu() */
  2. @Override
  3. public boolean onPrepareOptionsMenu(Menu menu) {
  4. // If the nav drawer is open, hide action items related to the content view
  5. boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
  6. menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
  7. return super.onPrepareOptionsMenu(menu);
  8. }


6.如何让app图标点击的时候能够展开或者隐藏侧边菜单。

一般的想法是在activityonOptionsItemSelected方法中判断点击事件是否来自于app图标,然后用DrawerLayout.closeDrawerDrawerLayout.openDrawer来隐藏与展开(参见第4点:在代码中主动展开与隐藏侧边菜单)。但是drawerLayout提供了更优雅的方式:使用ActionBarDrawerToggleonOptionsItemSelected方法。该方法activityonOptionsItemSelected方法中根据传递进来的menu item做了上面我们在“一般想法中提到的事情。用官方的说法是”ActionBarDrawerTogglewill take care of this”。我们只需这样做就ok了:

Activity中:

  1. @Override
  2. public booleanonOptionsItemSelected(MenuItem item) {
  3. // The action bar home/up actionshould open or close the drawer.
  4. // ActionBarDrawerToggle will takecare of this.
  5. if(mDrawerToggle.onOptionsItemSelected(item)) {
  6. return true;
  7. }
  8. ……….//处理其他菜单点击事件
  9. returnsuper.onOptionsItemSelected(item);
  10. }

如果不仔细阅读官方文档,估计我们很难看出(mDrawerToggle.onOptionsItemSelected(item)在这里的作用。这也是我刚开始最疑惑的地方。

7. drawerLayoutFragment是什么关系?

我们看到很多使用drawerLayout的代码中都同时使用了Fragment,这会造成误解,以为使用drawerLayout必须用到Fragment,其实这是错误的,使用Fragment是因为在侧滑菜单被点击的时候,主内容区如果内容比较复杂,用Fragment去填充会更容易,如果你的主内容区只是一个简单的字符串,只想在不同菜单点击的时候更新一下字符串的内容,我觉得没必要用Fragment。不过官方的例子其实中,Fragment所做的就是更新字符串内容这么简单。


最后我们来看看一个完整的drawerLayout的例子,来源于官方网站的demo,代码中反映了上述我们提到的所有要点:

Activity


  1. /*
  2. * Copyright 2013 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.example.android.navigationdrawerexample;
  17. import java.util.Locale;
  18. import android.app.Activity;
  19. import android.app.Fragment;
  20. import android.app.FragmentManager;
  21. import android.app.SearchManager;
  22. import android.content.Intent;
  23. import android.content.res.Configuration;
  24. import android.os.Bundle;
  25. import android.support.v4.app.ActionBarDrawerToggle;
  26. import android.support.v4.view.GravityCompat;
  27. import android.support.v4.widget.DrawerLayout;
  28. import android.view.LayoutInflater;
  29. import android.view.Menu;
  30. import android.view.MenuInflater;
  31. import android.view.MenuItem;
  32. import android.view.View;
  33. import android.view.ViewGroup;
  34. import android.widget.AdapterView;
  35. import android.widget.ArrayAdapter;
  36. import android.widget.ImageView;
  37. import android.widget.ListView;
  38. import android.widget.Toast;
  39. /**
  40. * This example illustrates a common usage of the DrawerLayout widget
  41. * in the Android support library.
  42. * <p/>
  43. * <p>When a navigation (left) drawer is present, the host activity should detect presses of
  44. * the action bar's Up affordance as a signal to open and close the navigation drawer. The
  45. * ActionBarDrawerToggle facilitates this behavior.
  46. * Items within the drawer should fall into one of two categories:</p>
  47. * <p/>
  48. * <ul>
  49. * <li><strong>View switches</strong>. A view switch follows the same basic policies as
  50. * list or tab navigation in that a view switch does not create navigation history.
  51. * This pattern should only be used at the root activity of a task, leaving some form
  52. * of Up navigation active for activities further down the navigation hierarchy.</li>
  53. * <li><strong>Selective Up</strong>. The drawer allows the user to choose an alternate
  54. * parent for Up navigation. This allows a user to jump across an app's navigation
  55. * hierarchy at will. The application should treat this as it treats Up navigation from
  56. * a different task, replacing the current task stack using TaskStackBuilder or similar.
  57. * This is the only form of navigation drawer that should be used outside of the root
  58. * activity of a task.</li>
  59. * </ul>
  60. * <p/>
  61. * <p>Right side drawers should be used for actions, not navigation. This follows the pattern
  62. * established by the Action Bar that navigation should be to the left and actions to the right.
  63. * An action should be an operation performed on the current contents of the window,
  64. * for example enabling or disabling a data overlay on top of the current content.</p>
  65. */
  66. public class MainActivity extends Activity {
  67. private DrawerLayout mDrawerLayout;
  68. private ListView mDrawerList;
  69. private ActionBarDrawerToggle mDrawerToggle;
  70. private CharSequence mDrawerTitle;
  71. private CharSequence mTitle;
  72. private String[] mPlanetTitles;
  73. @Override
  74. protected void onCreate(Bundle savedInstanceState) {
  75. super.onCreate(savedInstanceState);
  76. setContentView(R.layout.activity_main);
  77. mTitle = mDrawerTitle = getTitle();
  78. mPlanetTitles = getResources().getStringArray(R.array.planets_array);
  79. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  80. mDrawerList = (ListView) findViewById(R.id.left_drawer);
  81. // set a custom shadow that overlays the main content when the drawer opens
  82. mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
  83. // set up the drawer's list view with items and click listener
  84. mDrawerList.setAdapter(new ArrayAdapter<String>(this,
  85. R.layout.drawer_list_item, mPlanetTitles));
  86. mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
  87. // enable ActionBar app icon to behave as action to toggle nav drawer
  88. getActionBar().setDisplayHomeAsUpEnabled(true);
  89. getActionBar().setHomeButtonEnabled(true);
  90. // ActionBarDrawerToggle ties together the the proper interactions
  91. // between the sliding drawer and the action bar app icon
  92. mDrawerToggle = new ActionBarDrawerToggle(
  93. this, /* host Activity */
  94. mDrawerLayout, /* DrawerLayout object */
  95. R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
  96. R.string.drawer_open, /* "open drawer" description for accessibility */
  97. R.string.drawer_close /* "close drawer" description for accessibility */
  98. ) {
  99. public void onDrawerClosed(View view) {
  100. getActionBar().setTitle(mTitle);
  101. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  102. }
  103. public void onDrawerOpened(View drawerView) {
  104. getActionBar().setTitle(mDrawerTitle);
  105. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  106. }
  107. };
  108. mDrawerLayout.setDrawerListener(mDrawerToggle);
  109. if (savedInstanceState == null) {
  110. selectItem(0);
  111. }
  112. }
  113. @Override
  114. public boolean onCreateOptionsMenu(Menu menu) {
  115. MenuInflater inflater = getMenuInflater();
  116. inflater.inflate(R.menu.main, menu);
  117. return super.onCreateOptionsMenu(menu);
  118. }
  119. /* Called whenever we call invalidateOptionsMenu() */
  120. @Override
  121. public boolean onPrepareOptionsMenu(Menu menu) {
  122. // If the nav drawer is open, hide action items related to the content view
  123. boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
  124. menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
  125. return super.onPrepareOptionsMenu(menu);
  126. }
  127. @Override
  128. public boolean onOptionsItemSelected(MenuItem item) {
  129. // The action bar home/up action should open or close the drawer.
  130. // ActionBarDrawerToggle will take care of this.
  131. if (mDrawerToggle.onOptionsItemSelected(item)) {
  132. return true;
  133. }
  134. // Handle action buttons
  135. switch(item.getItemId()) {
  136. case R.id.action_websearch:
  137. // create intent to perform web search for this planet
  138. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
  139. intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
  140. // catch event that there's no activity to handle intent
  141. if (intent.resolveActivity(getPackageManager()) != null) {
  142. startActivity(intent);
  143. } else {
  144. Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
  145. }
  146. return true;
  147. default:
  148. return super.onOptionsItemSelected(item);
  149. }
  150. }
  151. /* The click listner for ListView in the navigation drawer */
  152. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  153. @Override
  154. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  155. selectItem(position);
  156. }
  157. }
  158. private void selectItem(int position) {
  159. // update the main content by replacing fragments
  160. Fragment fragment = new PlanetFragment();
  161. Bundle args = new Bundle();
  162. args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
  163. fragment.setArguments(args);
  164. FragmentManager fragmentManager = getFragmentManager();
  165. fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
  166. // update selected item and title, then close the drawer
  167. mDrawerList.setItemChecked(position, true);
  168. setTitle(mPlanetTitles[position]);
  169. mDrawerLayout.closeDrawer(mDrawerList);
  170. }
  171. @Override
  172. public void setTitle(CharSequence title) {
  173. mTitle = title;
  174. getActionBar().setTitle(mTitle);
  175. }
  176. /**
  177. * When using the ActionBarDrawerToggle, you must call it during
  178. * onPostCreate() and onConfigurationChanged()...
  179. */
  180. @Override
  181. protected void onPostCreate(Bundle savedInstanceState) {
  182. super.onPostCreate(savedInstanceState);
  183. // Sync the toggle state after onRestoreInstanceState has occurred.
  184. mDrawerToggle.syncState();
  185. }
  186. @Override
  187. public void onConfigurationChanged(Configuration newConfig) {
  188. super.onConfigurationChanged(newConfig);
  189. // Pass any configuration change to the drawer toggls
  190. mDrawerToggle.onConfigurationChanged(newConfig);
  191. }
  192. /**
  193. * Fragment that appears in the "content_frame", shows a planet
  194. */
  195. public static class PlanetFragment extends Fragment {
  196. public static final String ARG_PLANET_NUMBER = "planet_number";
  197. public PlanetFragment() {
  198. // Empty constructor required for fragment subclasses
  199. }
  200. @Override
  201. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  202. Bundle savedInstanceState) {
  203. View rootView = inflater.inflate(R.layout.fragment_planet, container, false);
  204. int i = getArguments().getInt(ARG_PLANET_NUMBER);
  205. String planet = getResources().getStringArray(R.array.planets_array)[i];
  206. int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
  207. "drawable", getActivity().getPackageName());
  208. ((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
  209. getActivity().setTitle(planet);
  210. return rootView;
  211. }
  212. }
  213. }


Xml

activity_main.xml

  1. <!--
  2. Copyright 2013 The Android Open Source Project
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <!-- 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. -->
  14. <android.support.v4.widget.DrawerLayout
  15. xmlns:android="http://schemas.android.com/apk/res/android"
  16. android:id="@+id/drawer_layout"
  17. android:layout_width="match_parent"
  18. android:layout_height="match_parent">
  19. <!-- As the main content view, the view below consumes the entire
  20. space available using match_parent in both dimensions. -->
  21. <FrameLayout
  22. android:id="@+id/content_frame"
  23. android:layout_width="match_parent"
  24. android:layout_height="match_parent" />
  25. <!-- android:layout_gravity="start" tells DrawerLayout to treat
  26. this as a sliding drawer on the left side for left-to-right
  27. languages and on the right side for right-to-left languages.
  28. The drawer is given a fixed width in dp and extends the full height of
  29. the container. A solid background is used for contrast
  30. with the content view. -->
  31. <ListView
  32. android:id="@+id/left_drawer"
  33. android:layout_width="240dp"
  34. android:layout_height="match_parent"
  35. android:layout_gravity="start"
  36. android:choiceMode="singleChoice"
  37. android:divider="@android:color/transparent"
  38. android:dividerHeight="0dp"
  39. android:background="#111"/>
  40. </android.support.v4.widget.DrawerLayout>

fragment_planet.xml

  1. <!--
  2. Copyright 2013 The Android Open Source Project
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
  14. android:id="@+id/image"
  15. android:layout_width="match_parent"
  16. android:layout_height="match_parent"
  17. android:background="#000000"
  18. android:gravity="center"
  19. android:padding="32dp" />

drawer_list_item.xml

  1. <!--
  2. Copyright 2013 The Android Open Source Project
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  14. android:id="@android:id/text1"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:textAppearance="?android:attr/textAppearanceListItemSmall"
  18. android:gravity="center_vertical"
  19. android:paddingLeft="16dp"
  20. android:paddingRight="16dp"
  21. android:textColor="#fff"
  22. android:background="?android:attr/activatedBackgroundIndicator"
  23. android:minHeight="?android:attr/listPreferredItemHeightSmall"/>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/149404.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号