本篇接續:使用 Android Navigation Drawer 製作側選單(1)
前面我們做到從螢幕左邊往右滑來開啟側選單,但是觸碰 ActionBar 的 App icon 時卻沒有任何反應,現在就來處理 App icon 的動作。很簡單,覆寫 onOptionsItemSelected 方法即可:
前面我們做到從螢幕左邊往右滑來開啟側選單,但是觸碰 ActionBar 的 App icon 時卻沒有任何反應,現在就來處理 App icon 的動作。很簡單,覆寫 onOptionsItemSelected 方法即可:
public boolean onOptionsItemSelected(MenuItem item) {
//home
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
這樣當你觸碰 App icon 時就可以開關 drawer 了。
Action Buttons 事件
onOptionsItemSelected 這個方法同時也可以處理 Action button 的事件。首先,加入 action button 要用到的字串資源 strings.xml:<string name="action_refresh">重新整理</string>
<string name="action_edit">編輯</string>
<string name="action_search">搜尋</string>
<string name="action_info">軟體資訊</string>
接著建立 res/menu/main.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_refresh"
android:orderInCategory="1"
android:showAsAction="ifRoom|withText"
android:icon="@drawable/ic_launcher"
android:title="@string/action_refresh"/>
<item
android:id="@+id/action_edit"
android:orderInCategory="2"
android:showAsAction="ifRoom|withText"
android:title="@string/action_edit"/>
<item
android:id="@+id/action_info"
android:orderInCategory="4"
android:showAsAction="ifRoom|withText"
android:title="@string/action_info"/>
<item
android:id="@+id/action_search"
android:orderInCategory="3"
android:showAsAction="ifRoom|withText"
android:title="@string/action_search"/>
</menu>
參數說明:
- android:id:在 Activity 中可以用來判斷是否被點選。
- android:orderInCategory:選單的順序。若省略,會依 xml 中的順序呈現。
- android:showAsAction:在另一篇中有選項的說明,這裡我們讓它"位置夠用才顯示 | 同時顯示文字"。
- android:icon:圖示。
- android:title:選單的文字。
Portrait (直式)
Landscape (橫式)
空間不夠時,只會顯示 icon (若有),並且擠不下的會在 overflow (3個點)裡出現。空間夠用當然就全部顯示。最後在 Activity 中覆寫 onCreateOptionsMenu:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
就能讓 Action button 出現了。點選後的事件在 onOptionsItemSelected 中處理,在剛才的程式碼中加入 switch 的部份:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//home
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
//action buttons
switch (item.getItemId()) {
case R.id.action_edit:
//....
break;
case R.id.action_search:
//....
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
Drawer item 點選事件
最後,我們要讓側選單的項目被點選時,載入對應的內容,即 Fragment。這邊示範 3 個 Fragment,程式碼大同小異,其中一個示範如何接受傳過來的值。FragmentApple.java
public class FragmentApple extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return initView(inflater, container);
}
private View initView(LayoutInflater inflater, ViewGroup container) {
View view = inflater.inflate(R.layout.fragment_apple, container, false);
return view;
}
}
fragment_apple.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/txtApple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Apple"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
簡單的顯示一個文字欄位。和 FragmentBook.java 一樣,但 FragmentCat.java 多了接值的部份:
public class FragmentCat extends Fragment {
public static final String CAT_COLOR = "cat_color";
private String color = "";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
color = this.getArguments().getString(CAT_COLOR);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return initView(inflater, container);
}
private View initView(LayoutInflater inflater, ViewGroup container) {
View view = inflater.inflate(R.layout.fragment_cat, container, false);
TextView txtCat = (TextView) view.findViewById(R.id.txtCat);
String colorCat = color + " " + txtCat.getText().toString();
txtCat.setText(colorCat);
return view;
}
}
呼叫 getArguments() 來接值參數,然後 getXXX() 來取得該值。
MainActivity.java
回到 MainActivity 中,建立一個當側選單項目被點選時要做什麼的方法 selectItem():private void selectItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentApple();
break;
case 1:
fragment = new FragmentBook();
break;
case 2:
fragment = new FragmentCat();
Bundle args = new Bundle();
args.putString(FragmentCat.CAT_COLOR, "Brown");
fragment.setArguments(args);
break;
default:
//還沒製作的選項,fragment 是 null,直接返回
return;
}
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
// 更新被選擇項目,換標題文字,關閉選單
lstDrawer.setItemChecked(position, true);
setTitle(drawer_menu[position]);
layDrawer.closeDrawer(lstDrawer);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
參數 position 是被點選的那個項目的索引。依不同的索引,建立不同的 Fragment ,其中 FragmentCat 有傳值(一個字串)。最後就是把原本的 Fragment 換成被選擇的,同時更新 ActionBar 的標題並關閉側選單。接著就是建立偵聽器,在接收到事件時呼叫 selectItem() 方法:
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
然後在 initDrawerList() 中指定給 lstDrawer:
//側選單點選偵聽器
lstDrawer.setOnItemClickListener(new DrawerItemClickListener());
大功告成!!範例程式碼:https://github.com/tony915/NavigationDrawerDemo
-- (補充 2014/6/4) --
側選單加入圖示
建立新的 layout 給 ListView 用,drawer_list_item2.xml 如下:<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/imgIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/txtItem"
android:layout_alignTop="@+id/txtItem"
android:padding="3dp"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/txtItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/imgIcon"
android:padding="10dp"
android:text="Item"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#cccccc" />
</RelativeLayout>
只是把原本的 TextView 外面多加一個 Layout,這樣就可以多加一個 ImageView 進來。接著修改程式碼。原本使用的 ArrayAdapter,在這裡無法使用,必須改用 SimpleAdapter,或自己建立 BaseAdapter 來使用。修改後的 initDrawerList() 方法如下:
private void initDrawerList(){
drawer_menu = this.getResources().getStringArray(R.array.drawer_menu);
// ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.drawer_list_item, drawer_menu);
List<HashMap<String,String>> lstData = new ArrayList<HashMap<String,String>>();
for (int i = 0; i < 10; i++) {
HashMap<String, String> mapValue = new HashMap<String, String>();
mapValue.put("icon", Integer.toString(R.drawable.ic_launcher));
mapValue.put("title", drawer_menu[i]);
lstData.add(mapValue);
}
SimpleAdapter adapter = new SimpleAdapter(this, lstData, R.layout.drawer_list_item2, new String[]{"icon", "title"}, new int[]{R.id.imgIcon, R.id.txtItem});
lstDrawer.setAdapter(adapter);
//側選單點選偵聽器
lstDrawer.setOnItemClickListener(new DrawerItemClickListener());
}
這裡的 icon 圖檔,我只示範一個同樣的圖示,你可以換成一個陣列,數量和 drawer_menu 陣列一樣,這樣每個項目就會有自己的圖示,記得要把圖示的資源碼轉成字串。結果如下:
-- (補充 at 2014/8/11) --
如何讓實體按鈕的 "Back" 返回上一頁
將fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
改為
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack("home");
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
這樣就會將前一個 Fragment 保留至堆疊,但是按 Back 鍵到最後一個的時候,因為是 content_frame,會顯示空空的內容,所以必須加寫 Back 事件,讓堆疊的數量等於 0 時,關閉 App。
@Override
public void onBackPressed() {
super.onBackPressed();
FragmentManager fragmentManager = this.getFragmentManager();
int stackCount = fragmentManager.getBackStackEntryCount();
if (stackCount == 0) {
this.finish();
}
}
範例程式放在 GitHub 中的 NavigationDrawerDemo 。
參考資料
- http://developer.android.com/design/patterns/navigation-drawer.html
- http://developer.android.com/training/implementing-navigation/nav-drawer.html
- http://wptrafficanalyzer.in/blog/android-sidebar-navigation-drawer-with-icons/
本文網址:https://blog.tonycube.com/2014/02/android-navigation-drawer-2.html
由 Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀
由 Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀
您好:
回覆刪除不好意思我這邊的程式,我將FragmentApple(Book、Cat)換成我的檔名他卻顯示錯誤 該換成甚麼會比較好?
switch (position) {
case 0:
fragment = new FragmentApple();
break;
case 1:
fragment = new FragmentBook();
break;
case 2:
fragment = new FragmentCat();
Bundle args = new Bundle();
args.putString(FragmentCat.CAT_COLOR, "Brown");
fragment.setArguments(args);
break;
你的檔名是指有一個類別檔案是繼承 Fragment ,像這樣嗎
刪除public class XXX extends Fragment
...
XXX 是你的類別檔案的名稱,且這個類別必須繼承 Fragment。
還有要注意一下 import 的部份,因為 Fragment 有 V4 的支援版本及Android 4.x 之後的版本,所以要看一下是否有 import 對的版本。
刪除你好:
回覆刪除請問Fragment是在MainActivity裡繼承還是在另外幾個裡面繼承?
因為我MainActivity是繼承Activity(我看您上面是這樣打的)
謝謝
每一個繼承 Fragment 的 XXX 類別都是一個獨立的檔案,
刪除MainActivity也是一個獨立的檔案。
文章下方有原始碼連結,你可以直接在上面看(src目錄)比較清楚,
或是下載(在右下方有 Download ZIP)。
很棒的教學喔 節省我很多的研究時間 3Q
回覆刪除不客氣 :)
刪除先謝謝用心的教學文~
回覆刪除我想請教一下,如果我要在選單文字左邊增加icon的話,我該如何操作 謝謝
修改 layout 及 adpater 即可,我寫在文章後面的補充。
刪除再次感謝你的教學!!!
刪除支持!! 這些都是很實用的教學
回覆刪除希望版主下次可以出有關Bitmap network的教學
請教版主 :
回覆刪除在這個側選單 點選任一頁面後
可以 使用 硬鍵Back 回去上一頁嗎?
我有在MainActivity 的 FragmentTransaction 後面加.addToBackStack(null)
但沒有用 , 板主要怎麼解決
我在文章後面多寫了補充,範例程式碼也更新了。
刪除謝謝版主的解決方案
刪除可以back鍵返回了
雖然我把原本的FragmentActivity 換成 Activity
才可以動作。
而且我進去 fragment 好像開啟後有邊框 殘影,
是不是我的app寫得太亂之類的,
有fragment子分頁太大的問題 ?
案返回後
刪除是不是fragment會 重新執行
因為我返回Home後,
在 異步執行緒怪怪的 (好像會衝突)
我2個fragment都有加 異步執行緒
是哪裡有問題嗎?
殘影?沒遇過,如果是用模擬器,可能是模擬器顯示的問題;如果實機也是如此,就要找原因。
刪除異步執行緒在返回時要手動結束掉,否則它會繼續執行到完。
那應該是開啟的背景動畫吧?(猜想)
刪除我用的是黑色背景的layout , 所以才會這樣
謝謝版主的回答
作者已經移除這則留言。
刪除請教Tony
回覆刪除我想要在這 Navigation Drawer上再加上 Swipe Tabs
可是我的Tab點選卻一直顯示不出下面的Fragment
如果把Navigation Drawer的程式碼拿掉才會出現Tab裡的Fragment
是Navigation Drawer覆蓋在Swipe Tab上的關係嗎?
我看了很久程式碼還找不出哪裡需要修改
拜託Tony指點一下
SwipeTab我也沒用過,幫你找到兩篇文章,參考一下
刪除http://stackoverflow.com/questions/22389933/swiping-tabs-inside-a-navigation-drawer-fragment
這篇是教學
http://androidgreeve.blogspot.tw/2014/01/android-actionbar-navigating-with-swipeable-tabs-and-views.html
作者已經移除這則留言。
刪除請問Tony~~是這樣的
刪除我主程式進入到MainActivity中
如你的程式碼會先判斷
if (savedInstanceState == null) {
selectItem(0);
}
然後進到我的BrowserFragment中
而這個BrowserFragment是有用viewPager和Action的結合
第一次進入到這個BrowserFragment中都很正常
但當我利用drawer切換至其他的Fragment再切換回來後
這個BrowserFragment卻只剩下上面的ActionBar Tab
下面原本利用viewpager切換的Fragment卻變成一片空白
但滑動和切換TAB都很正常
一定要重新開啟這個Activity才會恢復正常
請問是哪邊少做了甚麼嗎?
我的做法裡面,是用 replace 把目前的 fragment 置換成被選到的。
刪除也就是說,每次點選drawer切換項目時,fragment 都是新建立的,
而前一個 fragment 就被釋放了。
看看你切換的動作是怎麼做的,問題應該是出在切換上,
fragment 本身應該是沒問題。
請問Tony
回覆刪除這一行 setTitle(drawer_menu[position]);
一直失敗對照很多次,拜託指點一下
要看DDMS告訴你什麼錯誤。
刪除感謝大大,我已經解決問題了,不過現在有一個問題想問Tony大大,就是因為就是我程式碼有加入try catch 去網路抓取資料,可是因為加入try catch的關係,會因為讀取速度的關係畫面經常卡住,所以我想問的是我要怎麼讓畫面先轉入過去,內容資料讀取完之後在呈現出來,例如臉書的切換到動態載入,畫面先轉入過去之後,內容等讀取完之後在呈現出來,請問我該怎麼做,麻煩大大給個方向。謝謝
刪除要用非同步的方式,在資料下載完成後再顯示,
刪除參考 AsyncTask
http://blog.tonycube.com/2011/08/asynctask.html
或 Handler
http://developer.android.com/reference/android/os/Handler.html
Tony大大,上面那個問題我想實作在Fragment ,也是用一樣的辦法嗎?
刪除一樣,只要你不想被載入過久的動作顯響到畫面,兩個動作就必須分開在兩個執行緒上做。
刪除作者已經移除這則留言。
回覆刪除Tony大您好,我想請問一下,如何在最上方加入Search View,我嘗試了許多方法都無法成功
回覆刪除和 Action button 作法一樣,請參考
刪除http://www.coderzheaven.com/2012/10/20/actionbar-search-option-options-android/
Tony大大你好,我把Swipe Tab與Navigation Drawer放在一起,然而,在run的時候點擊Navigation Drawer 的item所顯示的畫面與tab中的viewpage重疊到,請問有方法可以另外再Navigation Drawer的click 事件時建立新的畫面嗎? (因為是初學者有些概念不是很懂)
回覆刪除"Navigation Drawer 的item所顯示的畫面"應該會是一個 fragment,
刪除tab 本身屬於另一個 fragment,
所以當你點選Navigation Drawer 的item時,
應該換掉 tab 這個
找範例裡面這一段,
先建立想要的 fragment 然後把它換掉
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
Tony大大,按造你的說法,我嘗試了把tab放入
刪除Navigation Drawer item的 fragment ,解決了畫面衝突問題,
在此感謝你的解答 : )
不客氣 :)
刪除Hi,Tony
回覆刪除我有一個問題想請教你,就是我的錯誤訊息是
\appcompat_v7\res\values-v21\themes_base.xml:81: error: Error: No resource found that matches the given name: attr 'android:colorAccent'.
在之前我用4.0.3版本開發的時候沒問題,可是顯在4.0.3編譯時不能成功,反而用到5.0.1才能編譯成功,請問這是為什麼?那有辦法降回4.0.3版本嗎?
可能 AndroidMenifest.xml 中, targetSdkVersion 是設為21(Android 5.0),所以4.0.3無法編譯。
刪除這篇(http://code2care.org/pages/appcompat_v7-errors-after-updates-to-api-level-21-material-theme/)
有說到解決方法,但是以 5.0 去編譯。
你可以在"專案"(按右鍵)->Properties->Android->選你要的"Project Build Target"看看能不能編譯。
目前嘗試下來發現 Android 5.0 可以編譯成功,但是進去之後會出現問題
刪除private void initActionBar(){
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
}
這個方法無法使用,我有上網查過google有說Android 5.0 ActionBar 有做變動,請問大大知道這是什麼問題嗎?還有如果我開發平台用5.0.1 那麼我4.0.3版本 可否使用?
我找到這篇(http://blog.csdn.net/xwhnew/article/details/40624715)是說在 5.0 之後,以ToolBar 取代ActionBar,所以可能程式碼要修改。
刪除你的 minSdkVersion 設為 15 (也就是 4.0.3)
targetSdkVersion 設為 21
這樣 4.0.3 之後的裝置都能跑。
你好tony 我有幾個問題然後我算是新手所以問題如果有點怪還請見諒~~
回覆刪除1.開啟程式只會在activity_main停留一會就會切換到第一個選項的頁面
請問要怎麼讓一開始是停在activity_main?
2.要怎麼讓title bar變成白色然後更改文字甚至或讓他消失 等於只有那個選單的圖案而已
1.程式是因為你怎麼寫,它怎麼跑,所以看一下你寫了什麼程式造成了它的目前的行為。
刪除2.要修改 title bar (現在叫Actioin Bar),參考 https://developer.android.com/training/basics/actionbar/styling.html
PS.文字不加標點符號是在告訴閱讀者,不要看!趕快離開!
Tony大您好
回覆刪除我是android新手
參考了您做Navigation Drawer的教學文
請問您有研究過用v7去做嗎?
android.support.v7.app.ActionBarDrawerToggle;
v7版本好像就內建圖檔,比較方便些
網路上找不到v7的教學文,好苦手QQ
作者已經移除這則留言。
刪除作者已經移除這則留言。
刪除沒用過 v7 的版本,這裡有教怎麼從 v4 轉換成 v7,
刪除看起是原本的 ActionBar 不能用了,要改用 Toolbar,
整體的行為是沒多大更動
感謝
回覆刪除為我想寫的軟體提供範本
感恩!!!
作者已經移除這則留言。
回覆刪除作者已經移除這則留言。
刪除Tony大你好,請問可以讓app本身預設開啟時不是顯示側選單的第一個選項而是顯示另一畫面嗎?
回覆刪除把這範例中的這段程式碼
刪除FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment);//<<<<<這裡
fragmentTransaction.addToBackStack("home");
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
我標示"這裡"的那個 fragment 用你想要當主畫面的 Fragment 取代,把這段程式碼放在 Activity 的 onCreate ,這樣它一開啟後,就會立即換成你所指定的 fragment
可以了!謝謝^^ 可是還有個問題想問,就是雖然有顯示我指定的畫面,可是側選單的背景顏色就不見了...直接透明,可以請問是什麼原因嗎?
刪除在側選單的 layout 指定顏色看看
刪除Tony大大,請問R.id.content_frame 這是指什麼呢?
回覆刪除Tony 大大,抱歉我看到了,沒從第一頁開始看起- -
刪除你好,請問setItemChecked沒有反應,應該是哪裡出了問題呢?
回覆刪除我在xml中android:choiceMode不管設定multipleChoice或singleChoise都沒有用...
先確認 check 的值是不是有改變,如果有那就是 view 的問題,有可能 view 有改變但是看不出來,
刪除看看這篇有沒有幫助 http://blog.csdn.net/zhangyingli/article/details/46356895