Android Training学习笔记——Navigation

来源:互联网 发布:聚合数据公司 编辑:程序博客网 时间:2024/04/29 18:55

转载请注明出处:http://blog.csdn.net/xroocky/article/details/50767844
参考源码:
ListView版:http://download.csdn.net/detail/xroocky/9448415
NavigationView版:http://download.csdn.net/detail/xroocky/9448363

Ps:我的源码中存在一个很重要的问题,当使用DrawerLayout进行Fragment切换后,会发现二次进入包含Tab的Fragment时,Fragment的内容没有加载出来,而且Tab切换很不流畅。。。一直不知道问题出在哪里,有朋友知道原因的话劳烦告知一下,谢谢了~


创建滑动View和Tab

一、实现滑动View

  1. 滑动View是需要通过ViewPager来实现的,因此需要在布局文件中插入ViewPager

    <android.support.v4.view.ViewPager    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/pager"    android:layout_width="match_parent"    android:layout_height="match_parent" />
  2. 向ViewPager中添加子view作为page需要为其设置Adapter,有FragmentPagerAdapterFragmentStatePagerAdapter两种Adapter可供选择,FragmentPagerAdapter适用于page个数确定且较少的情况,FragmentStatePagerAdapter则适用于page个数不确定的情况,而且当切换page时可以通过销毁不显示的page来使内存最小化。所以一般FragmentStatePagerAdapter用得比较多。

    public class MainActivity extends AppCompatActivity {    private List<Fragment> fragmentList = new ArrayList<>();    private List<String> titleList = new ArrayList<>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ViewPager viewPager = (ViewPager)findViewById(R.id.viewpager);        viewPager.setAdapter(new VIewPagerAdapter(getSupportFragmentManager(), fragmentList, titleList));    }}
    public class ViewPagerAdapter extends FragmentStatePagerAdapter {    private List<Fragment> fragmentList;    private List<String> titleList;    public VIewPagerAdapter(FragmentManager fm, List fragmentList, List titleList) {        super(fm);        this.fragmentList = fragmentList;        this.titleList = titleList;    }    @Override    public Fragment getItem(int position) {        return fragmentList.get(position);    }    @Override    public int getCount() {        return fragmentList.size();    }    @Override    public CharSequence getPageTitle(int position) {        return titleList.get(position);    }}

    到这里滑动View就已经实现了,还缺少Tabs来标识一下当前是在哪个View,接下来就来学习一下如何添加Tabs。

二、向Action Bar添加Tabs

Training里面给了两种方法添加Tabs,一种是通过actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS)方法来设置ActionBar的导航模式为Tabs导航,然后再调用actionBar.addTab()方法来为ActionBar添加Tabs,但是在AS里面使用这种方法时发现谷歌已经不推荐使用这种方法了,这……是Training还没及时更新么?另外一种方法是通过向布局文件中的ViewPager里面添加PagerTitleStrip来自动生成Tabs,然而这种方法似乎实现的Tabs太简陋了。后来在网上查找了一番得知,目前谷歌最推荐的方式是使用支持库中的TabLayout,具体方法如下。

  1. 添加dependencies

    compile ‘com.android.support:design:23.1.1’

  2. 在布局文件中添加TabLayout

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent">    <android.support.design.widget.TabLayout        android:id="@+id/tab_layout"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@color/colorPrimary"        app:tabIndicatorColor="@android:color/white"        app:tabSelectedTextColor="@android:color/white"        app:tabTextColor="@color/Indigo_100"        android:elevation="4dp"/>    <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="match_parent" /></LinearLayout>
  3. 设置添加并绑定Tab

    TabLayout tabLayout = (TabLayout)view.findViewById(R.id.tab_layout);    tabLayout.setTabMode(TabLayout.MODE_FIXED); //模式有MODE_FIXED或MODE_SCROLLABLE两种    tabLayout.addTab(tabLayout.newTab().setText(titleList.get(0)));    tabLayout.addTab(tabLayout.newTab().setText(titleList.get(1)));    tabLayout.setupWithViewPager(viewPager);

    此时Tabs已经添加并且和ViewPager绑定好了!

创建Navigation Drawer

一、创建Drawer Layout

  1. 向布局文件中添加DrawerLayout

    <android.support.v4.widget.DrawerLayout    android:id="@+id/drawer_layout"    android:layout_width="match_parent"    android:layout_height="match_parent">    <!-- The main content view -->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <android.support.v7.widget.Toolbar            android:id="@+id/my_toolbar"            android:layout_width="match_parent"            android:layout_height="?attr/actionBarSize"            android:background="?attr/colorPrimary"            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"            app:titleTextColor="@android:color/white"/>        <FrameLayout            android:id="@+id/fl_main"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </LinearLayout>    <!-- The navigation drawer -->    <ListView android:id="@+id/left_drawer"        android:layout_width="320dp"        android:layout_height="match_parent"        android:layout_gravity="start"  //如果系统语言是right-to-leftDrawer会出现在屏幕右侧        android:choiceMode="singleChoice"        android:divider="@android:color/transparent"        android:dividerHeight="0dp"        android:background="#fff"/></android.support.v4.widget.DrawerLayout>

    在DrawerLayout中,第一个child必须是界面内容展示区,第二个child必须是Drawer。Drawer的宽度不要超过320dp。

二、初始化Drawer List

  1. 为ListView设置适配器并绑定点击事件

    listView.setAdapter(new ArrayAdapter<>(this, R.layout.drawer_list_items, drawerItems));listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {    @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        selectItem(position);    }});

    其中可以把字符串数组写进资源文件中,通过R.array.*来引用,具体使用方法在String Array

    String[] drawerItems = getResources().getStringArray(R.array.str_drawer_items);

    ListView的适配器的R.layout.drawer_list_items参数定义的是ListView的一项显示效果,这里的布局文件中只有一个TextView

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/text1"android:layout_width="match_parent"android:layout_height="wrap_content"android:textAppearance="?android:attr/textAppearanceListItemSmall"android:gravity="center_vertical"android:paddingLeft="16dp"android:paddingRight="16dp"android:textColor="#000"android:background="?android:attr/activatedBackgroundIndicator"android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

    TextViewbackground属性为activatedBackgroundIndicator,这个属性值决定了ListView的item被选中的效果,被选中后该item默认背景颜色会变成colorAccent,所以可以通过修改colorAccent来改变ListView的item被选中的效果。另外可以通过改写activatedBackgroundIndicator的值来改变效果,具体方法如下:
    Drawable文件夹下新建xml文件listview_background.xml

    <?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_activated="true" android:drawable="@color/colorListview" /><item android:drawable="@android:color/transparent" /></selector>

    然后在styles.xml文件中添加如何item即可

    <item name="android:activatedBackgroundIndicator">@drawable/listview_background</item>

三、处理ListView点击事件

  1. 因为在打开APP后没有点击Drawer中ListView时需要将ListView设定为第一个item被选中并在内容展示区显示默认内容,所以把点击响应事件单独写成一个selectItem(position)方法,在初始化时调用selectItem(0)

    private void selectItem(int position) {    // update the main content by replacing fragments    fragmentManager.beginTransaction().replace(R.id.fl_main, fragmentList.get(position)).commit();    // update selected item and title, then close the drawer    listView.setItemChecked(position, true);    setTitle(drawerItems[position]);    drawerLayout.closeDrawer(listView);}

四、监听Drawer打开关闭事件

  1. 可以通过实现DrawerLayout.DrawerListener接口来重写回调函数onDrawerOpened()onDrawerOpened(),从而监听Drawer。然而为了更好地实现Action Bar和Drawer的交互,还可以通过ActionBarDrawerToggle来监听Drawer。

    actionBarDrawerToggle = new ActionBarDrawerToggle(this,            drawerLayout,            (Toolbar)findViewById(R.id.my_toolbar),            R.string.drawer_open,            R.string.drawer_close) {        public void onDrawerClosed(View drawerView) {            super.onDrawerClosed(drawerView);            getSupportActionBar().setTitle(mTitle); //mTitle为Drawer每个item的名称,会变            invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()        }        public void onDrawerOpened(View drawerView) {            super.onDrawerOpened(drawerView);            getSupportActionBar().setTitle(mDrawerTitle);   //mDrawerTitle为App名称,不会变            invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()        }    };    //下面这句同样也是使Drawer图标动态变化的关键    drawerLayout.setDrawerListener(actionBarDrawerToggle);

    通过onPrepareOptionsMenu方法来实现Action Bar的菜单隐藏

    /* Called whenever we call invalidateOptionsMenu() */@Overridepublic boolean onPrepareOptionsMenu(Menu menu) {    // If the nav drawer is open, hide action items related to the content view    boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);    menu.findItem(R.id.action_favorite).setVisible(!drawerOpen);    return super.onPrepareOptionsMenu(menu);}

五、通过App图标打开关闭Drawer

  1. 通过以下代码打开Drawer在Action Bar的图标:

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);getSupportActionBar().setHomeButtonEnabled(true);

    还需添加以下代码,onPostCreate方法中的syncState是用来设置Drawer图标的(设置成默认图标),onConfigurationChanged方法是在用户配置改变后用来改变actionBarDrawerToggle配置的(具体不是很清楚……),因为Drawer的图标也是在Action Bar,所以需要在onOptionsItemSelected处理它的点击

    @Overrideprotected void onPostCreate(Bundle savedInstanceState) {    super.onPostCreate(savedInstanceState);    // Sync the toggle state after onRestoreInstanceState has occurred.    actionBarDrawerToggle.syncState();}@Overridepublic void onConfigurationChanged(Configuration newConfig) {    super.onConfigurationChanged(newConfig);    //This method should always be called by your Activity's onConfigurationChanged method.    actionBarDrawerToggle.onConfigurationChanged(newConfig);}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {    // Pass the event to ActionBarDrawerToggle, if it returns    // true, then it has handled the app icon touch event    if (actionBarDrawerToggle.onOptionsItemSelected(item)) {        return true;    }    // Handle your other action bar items...    switch (item.getItemId()) {        case R.id.action_favorite:            break;        default:            break;    }    return super.onOptionsItemSelected(item);}

    到这里,Navigation Drawer基本就创建完成了。Training里面的Drawer是使用ListView布局的,然而在目前的支持库中有个名为NavigationView的控件,是谷歌让开发者直接拿来用的Drawer,接下来就来试试NavigationView

六、使用NavigationView创建Drawer

NavigationView是谷歌提供的一个便于布局Drawer的控件,只需为其设置好app:headerLayoutapp:menu属性,就可以得到很美观的Drawer

  1. 将原来的ListView换成NavigationView

    <android.support.design.widget.NavigationView    android:id="@+id/navigation"    android:layout_width="wrap_content"    android:layout_height="match_parent"    android:layout_gravity="start"    app:headerLayout="@layout/drawer_header"    app:menu="@menu/drawer_items" />

    headerLayout属性定义的是Drawer的头部布局,menu定义的是Drawer中的菜单

    //drawer_header.xml<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="150dp"><ImageView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:src="@drawable/google"    android:scaleType="centerCrop"/><RelativeLayout    android:layout_width="match_parent"    android:layout_height="match_parent"></RelativeLayout></FrameLayout>

    Drawer的头部布局我原本是打算直接给FrameLayout设置一个background属性,属性值就是一张图片,可是由于长宽比例不合适导致图片拉伸变形。因此只好采用现在这种添加一个ImageView,并为其设置scaleType属性的方法来避免背景拉伸,大家如果有其他的方法在留言板说一下呀~

    <?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android"><group android:id="@+id/gp_first"    android:checkableBehavior="single">    <item android:id="@+id/item_1"        android:title="首页"        android:checked="true"/>    <item android:id="@+id/item_2"        android:title="发现" />    <item android:id="@+id/item_3"        android:title="关注" /></group><item android:id="@+id/gp_second"    android:title="应用设置">    <menu>        <item android:id="@+id/item_4"            android:title="切换主题"            android:checkable="true"/>        <item android:id="@+id/item_5"            android:title="设置"            android:checkable="true"/>    </menu></item></menu>

    当使用子菜单时,需为每个条目设置android:checkable="true"属性来实现单选。如果仅仅是为菜单分组,而非子菜单的话,把两组菜单分别放入一个group中即可,需注意的是要为group设置id,否则app会分不清两个group,把两个groupitem显示到一个列表中。

  2. 处理NavigationView点击事件

    navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {        @Override        public boolean onNavigationItemSelected(MenuItem item) {            if (item.getItemId() == R.id.item_1 ||                    item.getItemId() == R.id.item_2 ||                    item.getItemId() == R.id.item_3 ||                    item.getItemId() == R.id.item_4 ||                    item.getItemId() == R.id.item_5) {                item.setChecked(true);                selectItem(item.getItemId(), item.getTitle().toString());            }            return true;        }    });
    private void selectItem(Integer id, String title) {    // update the main content by replacing fragments    fragmentManager.beginTransaction().replace(R.id.fl_main, fragmentMap.get(id)).commit();    // update selected item and title, then close the drawer    getSupportActionBar().setTitle(title);    drawerLayout.closeDrawers();}
  3. Drawer图标颜色问题
    默认的颜色是黑色,与白色的标题很不搭,于是想办法把它改成白色。直接将android:textColorPrimary改成白色可以实现效果,但是其他不想变成白色的地方也成白色了……后来在网上看到了修改styles.xml文件的方法
    问题的关键不是Drawer图标的默认颜色是黑色,而是Toolbartheme设置不合理,把Toolbartheme设置为android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"popupTheme设置为app:popupTheme="@style/ThemeOverlay.AppCompat.Light"即可

  4. status bar透明处理
    为API>=21的系统建立一个styles资源文件如下,并且需要为DrawerLayout添加属性android:fitsSystemWindows="true"

    <?xml version="1.0" encoding="utf-8"?><resources><style name="AppTheme" parent="AppTheme.Base">    <item name="android:windowDrawsSystemBarBackgrounds">true</item>    <item name="android:statusBarColor">@android:color/transparent</item></style></resources>
0 0