这一次本人分享的是模仿知乎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 { @Override public 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; @Override public 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; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems)); } @Override public 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); } @Override public 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;// toolbar private 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;//第三个 @Override protected 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对应的Fragment mLeftMenuFragment .setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener() { @Override public void menuItemSelected(String title, int position) { showSelectedFragment(position); mTitle = title; mToolbar.setTitle(mTitle);// 设置toolbar的文字 mDrawerLayout.closeDrawer(Gravity.LEFT); // 关闭菜单 } }); // 设置默认其中的Fragment showSelectedFragment(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的重叠) */ @Override protected 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() { @Override public 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(); } /* * 创建菜单 */ @Override public 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的展开状态进行监听,改变他的flag mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close) { @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); flag = false; } @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); flag = true; } }; mActionBarDrawerToggle.syncState(); mDrawerLayout.setDrawerListener(mActionBarDrawerToggle); } /* * 他的一个作用是监听当前的左侧菜单是否展开,假如展开则点击时候关闭不退出应用,否则关闭应用 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (flag) { mDrawerLayout.closeDrawers(); return true; } return super.onKeyDown(keyCode, event); } }
结果截图:
参考bolg,hyman老师的:Android 5.x Theme 与 ToolBar 实战