寫Android的時候,可能有多個界面。在風格統一的軟件中,寫Activity時會有很多重復。例如我所在軟工課程小組的項目:Github鏈接 ,里面的TaskListActivity和TeacherListActivity就在Navigation的處理上有重復。還有一個雙擊退出APP的方法onBackPressed()
也重復實現了。之前讓負責界面的同學把這些代碼放到一個BaseActivity里面,讓其他Activity繼承它。他說不好做,他嘗試過,但失敗了。
于是這次我獨自做 英語詞典APP 的時候 ,經過在Google上的一番搜索和實踐探索,寫出一個還可以的BaseActivity。現在做個記錄,以后還會用得到。
BaseActivity項目的Github鏈接
這個BaseActivity包括側滑菜單(Navigation Drawer)和工具欄(Tool Bar)。
先看最終效果:
一、Navigation Drawer
由于Navigation Drawer涉及到BaseActivity的主要布局,所以先說明。
Activity的布局文件
先看Android官方Navigation Drawer說明:Creating a Navigation Drawer
根據說明,將layout/activity_base.xml
設置為以下內容:<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 主內容布局 --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent"/> <!-- 側滑菜單 --> <ListView android:id="@+id/left_drawer" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:background="#111" android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp"/> </android.support.v4.widget.DrawerLayout>
Android Studio不會提示android:layout_gravity這一項,但是整行敲完之后,可以正常運行。
側滑菜單列表布局
這里只做簡單的布局,所以側滑菜單項都是TextView。
創建layout/list_item_drawer.xml
:<?xml version="1.0" encoding="utf-8"?> <TextView android:id="@+id/tv_na_draw" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" android:textColor="#fff"/>
因為等會兒要用ArrayAdapter,所以這里最外層一定要是TextView。
側滑菜單項的文本
StringArray
創建values/string_array_test.xml
:<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="planets_array"> <item>主界面</item> <item>關于</item> <item>設置</item> <item>退出</item> </string-array> </resources>
Activity初始化
Initialize the Drawer List
這里由于要將Activity做成BaseActivity,所以和官方文檔上的代碼不太一樣。public class BaseActivity extends AppCompatActivity { protected String[] planetTitles; protected DrawerLayout drawerLayout; protected ListView drawerList; protected FrameLayout frameLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } /** * 重寫setContentView,以便于在保留側滑菜單的同時,讓子Activity根據需要加載不同的界面布局 */ @Override public void setContentView(@LayoutRes int layoutResID) { drawerLayout = (DrawerLayout) getLayoutInflater().inflate(R.layout.activity_base, null); frameLayout = (FrameLayout) drawerLayout.findViewById(R.id.content_frame); // 將傳入的layout加載到activity_base的content_frame里面 getLayoutInflater().inflate(layoutResID, frameLayout, true); super.setContentView(drawerLayout); setUpNavigation(); } private void setUpNavigation() { planetTitles = getResources().getStringArray(R.array.planets_array); drawerList = (ListView) findViewById(R.id.left_drawer); drawerList.setAdapter(new ArrayAdapter<>(BaseActivity.this, R.layout.list_item_drawer, planetTitles)); } }
MainActivity
先創建個Activity查看效果。
創建MainActivity繼承BaseActivity :public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
layout_main用的是默認布局。
在啟動前要更改AndroidManifest.xml
,將MainActivity設置成Launcher:<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
啟動后,手指從最左往右劃,打開側滑菜單。
添加側滑菜單點擊事件
Handle Navigation Click Events
先創建ClickListener,這里簡單地設置點擊后顯示所點擊的文本。
在BaseActivity中添加: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) { Toast.makeText(BaseActivity.this, planetTitles[position], Toast.LENGTH_SHORT).show(); }
在BaseActivity的
setUpNavigation()
中添加:drawerList.setOnItemClickListener(new DrawerItemClickListener());
二、ToolBar
ToolBar 布局
ToolBar的各個成分:
Setting Up the App Bar
打開AndroidManifest.xml,將里面的android:theme="@style/AppTheme"
替換成android:theme="@style/Theme.AppCompat.Light.NoActionBar"
,如下:<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> ...(三點表示省略,下同)
創建
layout/toolbar.xml
:<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="@style/ThemeOverlay.AppCompat.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> </merge>
ToolBar 初始化
在BaseActivity里添加:public class BaseActivity extends AppCompatActivity { ... private Toolbar toolbar; ... private void setUpToolBar() { toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); } }
在BaseActivity的
setContentView()
里的setUpNavigation();
下面添加setUpToolBar();
,如下:@Override public void setContentView(@LayoutRes int layoutResID) { ... setUpNavigation(); setUpToolBar(); }
如果想要在MainActivity里顯示ToolBar,還需要在
activity_main.xml
里include一個ToolBar的布局:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout ...> <include layout="@layout/toolbar"/> </RelativeLayout>
顯示的效果如下:
加入側滑菜單打開鍵
到 Material Design Icons 下載圖標。搜索menu,下載PNG版本。
解壓后,將ic_menu_black_24dp/android
文件夾下面的所有文件夾復制到res/
里面。
在BaseActivity的setUpToolBar()
中添加:toolbar.setNavigationIcon(R.drawable.ic_menu_black_24dp);
雖然有圖標,但是還沒設置點擊事件,所以點擊的時候沒有任何反應。點擊ToolBar的home按鈕打開Navigation Drawer
在BaseActivity里添加:@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: drawerLayout.openDrawer(GravityCompat.START); return true; default: break; } return super.onOptionsItemSelected(item); }
這里就省略ActionBarDrawerToggle的部分了,影響不是特別大。實際應用中再去添加。
添加Option Menu
在values/strings.xml
里添加等會兒會用到的字符串:<resources> ... <string name="edit">編輯</string> <string name="search">搜索</string> <string name="change_history">變更記錄</string> </resources>
去 Material Design Icons 下載相關圖標。
創建res/menu/menu_main.xml
:<?xml version="1.0" encoding="utf-8"?> <menu android:id="@+id/expanded_menu" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_edit" android:icon="@drawable/ic_mode_edit_black_24dp" android:title="@string/edit" app:showAsAction="ifRoom"/> <item android:id="@+id/menu_search" android:icon="@drawable/ic_search_black_24dp" android:title="@string/search" app:showAsAction="ifRoom"/> <item android:id="@+id/menu_change_history" android:icon="@drawable/ic_change_history_black_24dp" android:title="@string/change_history" app:showAsAction="never"/> </menu>
其中ifRoom表示如果ToolBar空間足夠,則將其圖標顯示在ToolBar上面。never表示永不顯示在ToolBar上面,只有當點擊option menu按鈕的時候,才會出現。
在BaseActivity中添加:@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); super.onCreateOptionsMenu(menu); return true; }
啟動APP:
點擊menu按鈕后:
Option Menu的點擊事件
在BaseActivity里的onOptionsItemSelected()
增加case即可。隱藏ActionBar的Option Menu
有時候不想在某個Activity中顯示Option Menu,于是修改BaseActivity:public class BaseActivity extends AppCompatActivity { ... private boolean showOptionMenu = true; ... protected void hideOptionMenu() { showOptionMenu = false; } @Override public boolean onCreateOptionsMenu(Menu menu) { if (showOptionMenu) { getMenuInflater().inflate(R.menu.menu_main, menu); } super.onCreateOptionsMenu(menu); return true; } }
三、參考鏈接
ToolBar
https://developer.android.com/training/appbar/setting-up.html
https://developer.android.com/training/appbar/actions.html
http://stackoverflow.com/questions/32367041/calling-toolbar-on-each-activity
http://stackoverflow.com/questions/30824324/clicking-hamburger-icon-on-toolbar-does-not-open-navigation-drawer
http://stackoverflow.com/questions/19724567/how-to-add-menu-indicator-next-to-action-bars-app-icon
http://stackoverflow.com/questions/26582075/cannot-catch-toolbar-home-button-click-eventNavigation Drawer
https://developer.android.com/training/implementing-navigation/nav-drawer.html
http://stackoverflow.com/questions/33009469/baseactivity-for-navigation
http://stackoverflow.com/questions/22652556/creating-base-activity-with-navigation-drawer-in-androidArrayAdapter
http://stackoverflow.com/questions/9280965/arrayadapter-requires-the-resource-id-to-be-a-textview-xml-problems
http://stackoverflow.com/questions/29591546/unable-to-use-layout-gravity-for-listview-inside-drawerlayoutString Array
https://developer.android.com/guide/topics/resources/string-resource.html#StringArrayMaterial icons
https://design.google.com/icons/
文章列表