標籤:模仿知乎android app二 toolbar drawerlayout fragment的使用
這一次本人分享的是模仿知乎Android APP這個類型的,還有網易新聞等,他們都菜單介面等挺相似的。
我使用的是Material Design中提倡的app bar。使用ToolBar+DrawerLayout+Fragment實現,大家也可以在Fragment裡面嵌套ViewPager這樣使得Fragment包含一個ViewPager,ViewPager包含更多的Faragment去顯示內容。
這個Demo的大概功能敘述:
使用ToolBar,然後會有一個左側菜單,使用的是Fragment來實現,然後點擊菜單的時候會顯示不同的Fragment ,而且該fragment不會掩掉toolbar,不會像上一個案例一樣每點擊一次都去使用一個新的Activity然後把toolbar掩蓋,大家可以去看看那篇blog:
模仿知乎APP一 。、
主要的思路:
使用ToolBar的一些細節就是需要繼承AppCompatActivity(V7包下),使用的ToolBar也是V7包下的,那麼環境之類這裡也不詳細說了,然後需要去重新寫一個style Apptheme,去掉預設的ActionBar。再然後在布局檔案中頭部使用ToolBar,然後在使用一個DrawerLayout,他的第一個View是內容地區,我們使用一個FrameLayout,第二個view是左側菜單,我們也是用一個FrameLayout,這樣,我們就可以做到重用內容地區,可以使用不同的Fragment放在第二個內容地區中。
具體還是開代碼實現吧:
項目架構:
它包含3個Fragment去顯示內容地區,一個MainActivity,然後還有一個LeftFragment以及對應的Adapter
先看布局檔案:
主布局檔案,就像上面,沒什麼好講的
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res/com.example.mytoolbar_04" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical" tools:context="com.example.mytoolbar_04.MainActivity" > <android.support.v7.widget.Toolbar android:id="@+id/id_toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:minHeight="?attr/actionBarSize" app:navigationIcon="@drawable/ic_toc_white_24dp" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ThemeOverlay.AppCompat.ActionBar" app:title="APP Title" /> <android.support.v4.widget.DrawerLayout android:id="@+id/id_drawerlayout" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:id="@+id/id_content_container" android:layout_width="match_parent" android:layout_height="match_parent" > </FrameLayout> <FrameLayout android:id="@+id/id_left_menu_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="left" android:background="#ffffff" > </FrameLayout> </android.support.v4.widget.DrawerLayout></LinearLayout>
然後是左側菜單的ListView的布局檔案,一個圖片+文字作為一個item,也就這樣
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="48dp" android:gravity="center_vertical" android:paddingRight="16dp" > <ImageView android:layout_marginLeft="16dp" android:id="@+id/id_item_icon" android:src="@drawable/music_36px" android:layout_marginRight="8dp" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"/><TextView android:id="@+id/id_item_title" android:layout_marginLeft="72dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#64000000" android:textSize="16sp" android:text="@string/hello_world" android:layout_gravity="center_vertical"/></FrameLayout>
然後是style檔案:
他是被資訊清單檔中的application引用,他的主題需要是android:Theme.Light,然後加入有values-v14等包,它裡面的style檔案也需要更改,否則可能出錯
<resources> <style name="AppBaseTheme" parent="android:Theme.Light"> </style> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style></resources>
ToolBar上的菜單:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context="com.example.mytoolbar_04.MainActivity" > <!-- 總是顯示 --> <item android:id="@+id/id_action_refreash" android:icon="@drawable/ic_cached_white_24dp" android:orderInCategory="100" android:title="@string/action_refreash" app:showAsAction="always"/> <!-- 總是顯示 --> <item android:id="@+id/id_action_delete" android:icon="@drawable/ic_delete_white_24dp" android:orderInCategory="100" android:title="@string/action_delete" app:showAsAction="always"/> <!-- 總是顯示 --> <item android:id="@+id/id_action_favorite" android:icon="@drawable/ic_favorite_outline_white_24dp" android:orderInCategory="100" android:title="@string/action_favorite" app:showAsAction="always"/> <!-- 不顯示在ActionBar上 --> <item android:id="@+id/action_settings" android:orderInCategory="100" android:showAsAction="never" android:title="@string/action_settings"/></menu>
關於顏色那些自己去配置就好,喜歡的顏色就行,下面是重頭戲:
首先是,內容地區,我的三個Fragment都是類似的,所以只貼出一個,他顯示一個TextView
import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.LinearLayout.LayoutParams;import android.widget.TextView;public class FirstFragment extends Fragment{@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){TextView tv = new TextView(getActivity());LinearLayout.LayoutParams lp = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);tv.setLayoutParams(lp);tv.setTextSize(50);tv.setText("第一個Fragment");return tv;}}然後是MenuItem.java,他domain類,是對右側菜單的item的一些屬性的集合
public MenuItem(String text, boolean isSelected, int icon, int iconSelected){this.text = text; //顯示的文字this.isSelected = isSelected; //是否被選中this.icon = icon; //他的表徵圖this.iconSelected = iconSelected; //選中的表徵圖}boolean isSelected;String text;int icon;int iconSelected;}
然後是LeftMenuFragment.java類,他是左側的菜單,用於填充DrawerLayout中的第二個View。他繼承ListFragment,不需要重寫onCreateView方法,監聽他的item的點擊事件,使用一個介面OnMenuItemSelectedListener去負責與Activity互動,然後,裡麵包含一個方法menuItemSelecte(String title ,int position),分別是item的標題和他在ListView的位置position
import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.ListFragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ListView;public class LeftMenuFragment extends ListFragment{private static final int SIZE_MENU_ITEM = 3;//菜單總數private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM];private LeftMenuAdapter mAdapter;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);MenuItem menuItem = null;for (int i = 0; i < SIZE_MENU_ITEM; i++){menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light);mItems[i] = menuItem;}}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){return super.onCreateView(inflater, container, savedInstanceState);}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState){super.onViewCreated(view, savedInstanceState);setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems));}@Overridepublic void onListItemClick(ListView l, View v, int position, long id){super.onListItemClick(l, v, position, id);if (mMenuItemSelectedListener != null){mMenuItemSelectedListener.menuItemSelected( ((MenuItem) getListAdapter().getItem(position)).text, position);// 發生點擊事件,傳遞參數給Activity處理}mAdapter.setSelected(position);}// 點擊監聽器public interface OnMenuItemSelectedListener{/** * @param title 被點擊listview的標題 * @param position 被點擊ListView的position */void menuItemSelected(String title, int position);}private OnMenuItemSelectedListener mMenuItemSelectedListener; // 監聽ListView點擊之後發生的事情,用於與Activity互動public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener){this.mMenuItemSelectedListener = menuItemSelectedListener;}}他的適配器:適配器就不多說,主要是有一個setSelected方法,他的作用是去設定被單擊的item的一些背景,icon
import android.content.Context;import android.graphics.Color;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.ImageView;import android.widget.TextView;public class LeftMenuAdapter extends ArrayAdapter<MenuItem>{private LayoutInflater mInflater;private int mSelected;public LeftMenuAdapter(Context context, MenuItem[] objects){super(context, -1, objects);mInflater = LayoutInflater.from(context);}@Overridepublic View getView(int position, View convertView, ViewGroup parent){if (convertView == null){convertView = mInflater.inflate(R.layout.item_left_menu, parent, false);}ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon);TextView title = (TextView) convertView.findViewById(R.id.id_item_title);title.setText(getItem(position).text);iv.setImageResource(getItem(position).icon);convertView.setBackgroundColor(Color.TRANSPARENT);// 假如是當前的item,則是去設定他對對應的backgroundColor和新的drawable,以區分其他的if (position == mSelected){iv.setImageResource(getItem(position).iconSelected);convertView.setBackgroundColor(getContext().getResources().getColor( R.color.state_menu_item_selected));}return convertView;}/** * @param position點擊的時候item的位置 */public void setSelected(int position){this.mSelected = position;notifyDataSetChanged();}}然後,是我們最重要的MainActivity了,裡面都有注釋,應該都能看懂的了~~,他的一些功能,初始化UI,ToolBar,然後需要在OnCreate方法中選中首個需要顯示的title和Fragment,然後需要注意的是顯示在toolbar上的標題應該是跟ListVIew被單擊的item的title是一樣的。然後單擊事件的前提是當前LeftMenuFragment是展開的,單擊之後需要先把所有的Fragment先隱藏,再去判斷那個Fragment需要顯示再去顯示。再處理一下ToolBar的菜單的點擊事件,這裡點擊之後都會顯示一份Toast。再處理按下物理返回鍵的時候應該做出的行為,當是菜單展開的時候關閉菜單而不是關閉應用,然後沒有菜單展開才是關閉應用。
import android.os.Bundle;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.support.v4.widget.DrawerLayout;import android.support.v7.app.ActionBarDrawerToggle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.text.TextUtils;import android.view.Gravity;import android.view.KeyEvent;import android.view.Menu;import android.view.View;import android.widget.Toast;import com.example.mytoolbar_04.fragment.FirstFragment;import com.example.mytoolbar_04.fragment.SecondFragment;import com.example.mytoolbar_04.fragment.ThirdFragment;public class MainActivity extends AppCompatActivity{private ActionBarDrawerToggle mActionBarDrawerToggle;private DrawerLayout mDrawerLayout;// 包括左側菜單和內容地區private Toolbar mToolbar;// toolbarprivate LeftMenuFragment mLeftMenuFragment;// 左側菜單private String mTitle;private boolean flag = false;// 左側菜單是否展開的標誌private static final String KEY_TITLLE = "key_title";private FirstFragment firstFragment; //對應三個不同的內容地區,第一個private SecondFragment secondFragment;//第二個private ThirdFragment thirdFragment;//第三個@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initToolBar();initViews();// 重設頁面的也就是ToolBar的標題restoreTitle(savedInstanceState);FragmentManager fm = getSupportFragmentManager();// v4包mLeftMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_left_menu_container);if (mLeftMenuFragment == null){mLeftMenuFragment = new LeftMenuFragment();fm.beginTransaction().add(R.id.id_left_menu_container, mLeftMenuFragment).commit();}// 進行對leftFragment的點擊事件監聽,需要的是此時leftFragment這時候是展開的,// 然後點擊完成需要關閉該LeftMenuFragment,顯示被點擊的item對應的FragmentmLeftMenuFragment .setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener() { @Override public void menuItemSelected(String title, int position) { showSelectedFragment(position);mTitle = title;mToolbar.setTitle(mTitle);// 設定toolbar的文字mDrawerLayout.closeDrawer(Gravity.LEFT); // 關閉菜單} });// 設定預設其中的FragmentshowSelectedFragment(0);}/** * 當點擊LeftFragment時候選擇需要顯示的內容Fragment * @param position */protected void showSelectedFragment(int position){FragmentManager fm = getSupportFragmentManager();FragmentTransaction fragmentTransaction = fm.beginTransaction();hideFragment(fragmentTransaction);switch (position){case 0:if (firstFragment == null){firstFragment = new FirstFragment();fragmentTransaction.add(R.id.id_content_container, firstFragment);} else{fragmentTransaction.show(firstFragment);}break;case 1:if (secondFragment == null){secondFragment = new SecondFragment();fragmentTransaction.add(R.id.id_content_container, secondFragment);} else{fragmentTransaction.show(secondFragment);}break;case 2:if (thirdFragment == null){thirdFragment = new ThirdFragment();fragmentTransaction.add(R.id.id_content_container, thirdFragment);} else{fragmentTransaction.show(thirdFragment);}break;}fragmentTransaction.commit();}/** * 每一次顯示Fragment之前都先去隱藏Fragment * @param fragmentTransaction */protected void hideFragment(FragmentTransaction fragmentTransaction){if (firstFragment != null){fragmentTransaction.hide(firstFragment);}if (secondFragment != null){fragmentTransaction.hide(secondFragment);}if (thirdFragment != null){fragmentTransaction.hide(thirdFragment);}}/** * 恢複標題,使得toolbar中的文字與LeftMenuFragment上的ListVIew文字一樣 * @param savedInstanceState */private void restoreTitle(Bundle savedInstanceState){if (savedInstanceState != null)// 存在的時候,把標題讀出了,顯示mTitle = savedInstanceState.getString(KEY_TITLLE);if (TextUtils.isEmpty(mTitle))// 如何不存在,就像是第一次點擊來應用的時候,顯示首頁面{mTitle = getResources().getStringArray(R.array.array_left_menu)[0];}mToolbar.setTitle(mTitle);}/* * 該方法執行時期為使用者點擊了home鍵長時間沒有返回主介面或者是切換豎橫屏的時候, * 這時候會儲存對於的fragment的title,下載被onCreate方法中用到 一定需要重寫,或者可能出現文字的重疊(由於Fragment的重疊) */@Overrideprotected void onSaveInstanceState(Bundle outState){super.onSaveInstanceState(outState);outState.putString(KEY_TITLLE, mTitle);}/** * 初始化ToolBar */private void initToolBar(){Toolbar toolbar = mToolbar = (Toolbar) findViewById(R.id.id_toolbar);toolbar.setTitle(getResources().getStringArray(R.array.array_left_menu)[0]);setSupportActionBar(toolbar);// 需要在設定title之後才執行toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener(){@Overridepublic boolean onMenuItemClick(android.view.MenuItem arg0){int event = arg0.getItemId();switch (event){case R.id.id_action_refreash:showToast("您點擊了重新整理按鈕");return true;case R.id.id_action_delete:showToast("您點擊了刪除按鈕");return true;case R.id.id_action_favorite:showToast("您點擊了收藏按鈕");return true;}return false;}});}/** * 列印toast * @param text需要顯示的文字 */protected void showToast(String text){Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();}/* * 建立菜單 */@Overridepublic boolean onCreateOptionsMenu(Menu menu){getMenuInflater().inflate(R.menu.main, menu);return super.onCreateOptionsMenu(menu);}/** * 初始化 */private void initViews(){mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawerlayout);// 這個drawerListener需要對drawer的展開狀態進行監聽,改變他的flagmActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close){@Overridepublic void onDrawerClosed(View drawerView){super.onDrawerClosed(drawerView);flag = false;}@Overridepublic void onDrawerOpened(View drawerView){super.onDrawerOpened(drawerView);flag = true;}};mActionBarDrawerToggle.syncState();mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);}/* * 他的一個作用是監聽當前的左側菜單是否展開,假如展開則點擊時候關閉不退出應用,否則關閉應用 */@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event){if (flag){mDrawerLayout.closeDrawers();return true;}return super.onKeyDown(keyCode, event);}}結果:
參考bolg,hyman老師的:Android 5.x Theme 與 ToolBar 實戰
模仿知乎Android APP二