Android M新控件知识整理

Posted 奴才快来护驾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android M新控件知识整理相关的知识,希望对你有一定的参考价值。

前言:

Google官方在14年Google I/O上推出了全新的设计语言——Material Design。一并推出了一系列实现Material Design效果的控件库——android Design Support Library。其中有TabLayout, NavigationView,Floating labels for editing text,Floating Action Button,Snackbar, CoordinatorLayout, CollapsingToolbarLayout等等控件。
最近刚好空闲,也就整理了些,当然这里参考了很多大大的博客,在此谢过,文末也会贴出相关的链接,不过有些忘记了,很是惭愧…


Android Studio获得Android Design Support Library库

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

(一): TabLayout

Tablayout的作用一般是配合ViewPager结合Fragment一起使用,实现如下图效果
图1
下面就上代码咯~


activity_xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FF00AEFF"
        app:tabIndicatorColor="#ffffff"
        app:tabMode="fixed"
        app:tabSelectedTextColor="#FFFF5500"
        app:tabTextColor="#ffffff" />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ffffff" />
</LinearLayout>

这里对其中的一些属性进行说明

app:tabIndicatorColor=”@color/white”// 下方滚动的下划线颜色
app:tabSelectedTextColor=”@color/gray”// tab被选中后,文字的颜色
app:tabTextColor=”@color/white” // tab默认的文字颜色
app:tabMode=”fixed”//tab较少时均分整个屏幕
app:tabMode=”scrollable” //tab较多时,支持滑动

当然这些属性也可以在代码中设置:

tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setSelectedTabIndicatorColor();
tabLayout.setTabTextColors(,);注意:这里有两个参数分别是未选中,和选中的时候tab标签的颜色


接着是activity代码:

public class MainActivity extends AppCompatActivity {
    private TabLayout tabLayout;
    private ViewPager viewPager;
    private SimpleFragmentPagerAdapter pagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    private void initView() {
        //setupWithViewPager必须在ViewPager.setAdapter()之后调用
        /**
         也可以通过TabLayout的addTab()方法添加新构建的Tab实例到TabLayout中:
         TabLayout tabLayout = (TabLayout) findViewById(R.id.tabLayout);;
         tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
         tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
         tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
         */
        tabLayout = (TabLayout) findViewById(R.id.tabLayout);
        viewPager = (ViewPager) findViewById(R.id.viewPager);
        pagerAdapter = new SimpleFragmentPagerAdapter(getSupportFragmentManager(), this);
        viewPager.setAdapter(pagerAdapter);
        tabLayout.setupWithViewPager(viewPager);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setNavigationIcon(R.mipmap.ic_launcher);//设置导航栏图标
        toolbar.setLogo(R.mipmap.ic_launcher);//设置app logo
        toolbar.setTitle("Title");//设置主标题
        //toolbar.setSubtitle("Subtitle");//设置子标题
        toolbar.inflateMenu(R.menu.menu_toolbar);//设置右上角的填充菜单
    }
}

说明: setupWithViewPager必须在ViewPager.setAdapter()之后调用
某些时候也可以用下面的代码来实现TabLayout与viewpager的双向交互

tabLayout = (TabLayout) findViewById(R.id.tabLayout);
viewPager = (ViewPager) findViewById(R.id.viewPager);
pagerAdapter = new SimpleFragmentPagerAdapter(getSupportFragmentManager(), this);
//viewPager.setAdapter(pagerAdapter);
//注意一下,setupWithViePager必须在ViewPager.setAdapter()之后调用!
//tabLayout.setupWithViewPager(viewPager);

//1.设置TabLayout的选项卡监听
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        viewPager.setCurrentItem(tab.getPosition());
        colorChange(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {

    }
});
//2.设置Tab的标题来自PagerAdapter.getPageTitle()
tabLayout.setTabsFromPagerAdapter(pagerAdapter);
//3.设置TabLayout.TabLayoutOnPageChangeListener,给ViewPager
TabLayout.TabLayoutOnPageChangeListener listener =
        new TabLayout.TabLayoutOnPageChangeListener(tabLayout);
viewPager.addOnPageChangeListener(listener);
viewPager.setAdapter(pagerAdapter);

最后是adapter和fragment的代码

public class SimpleFragmentPagerAdapter extends FragmentPagerAdapter {
    final int PAGE_COUNT = 3;
    private String tabTitles[] = new String[]{"tab1", "tab2", "tab3"};
    private Context context;

    public SimpleFragmentPagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        this.context = context;
    }

    @Override
    public Fragment getItem(int position) {
        return PageFragment.newInstance(position);
    }

    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return tabTitles[position];
    }
}

public class PageFragment extends Fragment {
    public static final String ARGS_PAGE = "args_page";
    private int mPage;

    public static PageFragment newInstance(int page) {
        Bundle args = new Bundle();
        args.putInt(ARGS_PAGE, page);
        PageFragment fragment = new PageFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPage = getArguments().getInt(ARGS_PAGE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_page, container, false);
        TextView textView = (TextView) view.findViewById(R.id.textView);
        textView.setText("第" + mPage + "页");
        return view;
    }

}

fargment的xml文件我只是放了个textview,这里就不帖代码了
至此就是TabLayout的简单用法,但是我们也可以自己定义TabLayout的样式
关于如何自定义TabLayout的样式有兴趣的朋友可以移步:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0731/3247.html参考泡哥的博文。

碰到的坑:

在Android Studio 1.4 ,buildToolsVersion “23.0.3”,小米2s测试机 中创建demo使用TabLayout:最外层不使用CoordinatorLayout而使用LinearLayout做为最外层根布局则界面如下图所示,
通知栏透明变成白色,很是难看,原因是此Activity默认设置了android:theme=”@style/AppTheme.NoActionBar”这个主题
这里写图片描述
我的解决办法是:修改次Activity的主题

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

其中最后两个item的作用是去除ActionBar,顺便配图一张,意会下前面3个item所代表的含义;
这里写图片描述

(二): AppBarLayout、CoordinatorLayout

  • AppBarLayout 是继承LinerLayout实现的一个ViewGroup容器组件,它是为了Material Design设计的App Bar,支持手势滑动操作。默认的AppBarLayout是垂直方向的,它的作用是把AppBarLayout包裹的内容都作为AppBar,很多时候AppBarLayout都是作为Toolbar的父布局容器
  • CoordinatorLayout:是一个增强型的FrameLayout:它的作用有两个
    a. 作为一个布局的根布局
    b. 最为一个为子视图之间相互协调手势效果的一个协调布局
    它是组织它众多子view之间互相协作的一个ViewGroup。CoordinatorLayout使得子view之间知道了彼此的存在,一个子view的变化可以通知到另一个子view,CoordinatorLayout 所做的事情就是当成一个通信的桥梁,连接不同的view,使用 Behavior 对象进行通信。
  • CoordinatorLayout + AppBarLayout(向上滚动隐藏指定的View)
    1). 首先需要用CoordinatorLayout包住AppBarLayout;
    2). 顶部区域的View都放在AppBarLayout里面;
    3). AppBarLayout外面,CoordinatorLayout里面,放一个带有可滚动的View.如下的例子,放的是ViewPager,而ViewPager里面是放了RecylerView的,即是可以滚动的View.;
    4). 在可以滚动的View上设置属性 app:layout_behavior=”@string/appbar_scrolling_view_behavior”这个Behavior的class是真正控制滚动时候View的滚动行为
    比如:在CoordinatorLayout中使用AppBarLayout,如果AppBarLayout的子View(如ToolBar、TabLayout)标记了app:layout_scrollFlags滚动事件,那么在CoordinatorLayout布局里其它标记了app:layout_behavior的子View(LinearLayout、RecyclerView、NestedScrollView等)就能够响应(如ToolBar、TabLayout)控件被标记的滚动事件。
    4). 在AppBarLayout里面的View,通过app:layout_scrollFlags属性来控制,滚动时候的表现.其中有4种Flag的类型;
    • scroll: 所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。
    • enterAlways:这个flag让任意向下的滚动都会导致该view变为可见,启用快速“返回模式”。
    • enterAlwaysCollapsed:当你的视图已经设置minHeight属性又使用此标志时,你的视图只能已最小高度进入,只有
      当滚动视图到达顶部时才扩大到完整高度。
    • exitUntilCollapsed:滚动退出屏幕,最后折叠在顶端。

总结:这两个Layout暂时只是做些记录,感觉使用的频率不是很高,有兴趣的朋友可以移步我参考的博文

http://blog.csdn.net/u010687392/article/details/46852565


(三): Toolbar

Toolbar是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 ,Google 非常推荐大家使用 Toolbar 来作为Android客户端的导航栏,以此来取代之前的 Actionbar 。与 Actionbar 相比,Toolbar 明显要灵活的多。它不像 Actionbar 一样,一定要固定在Activity的顶部,而是可以放到界面的任意位置。其支持:

  • 设置导航栏图标;
  • 设置App的logo;
  • 支持设置标题和子标题;
  • 支持添加一个或多个的自定义控件;
  • 支持Action Menu;

Toolbar 是在 Android 5.0 才开始加上的,如果想向下兼容的话,我们需要在工程中引入 appcompat-v7 的兼容包,使用 android.support.v7.widget.Toolbar 进行开发。

Toolbar在xml文件中的代码

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <!--自定义控件-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Clock" />
</android.support.v7.widget.Toolbar>

其中:background的值的作用是:使其背景色跟随Activity主题指定的颜色(我是这么理解的)
android toolbar作为actionbar 在低分辨率手机上高度太高如何解决?(在知乎看到的,并没有测试)

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="@color/white"
    android:minHeight="48dp"
    android:theme="@style/ToolBarStyle"
    app:maxButtonHeight="48dp"
    app:paddingEnd="0dp"
    app:paddingStart="0dp"
    app:layout_scrollFlags="scroll|enterAlways"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:titleMarginStart="0dp" />

Activity中的代码:

toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.mipmap.ic_launcher);//设置导航栏图标
//toolbar.setLogo(R.mipmap.ic_launcher);//设置app logo
toolbar.setTitle("Title");//设置主标题
//toolbar.setTitleTextColor(getResources().getColor(R.color.colorAccent));
//setSupportActionBar(toolbar);
//toolbar.setSubtitle("Subtitle");//设置子标题
toolbar.inflateMenu(R.menu.menu_toolbar);//设置右上角的填充菜单
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        int menuItemId = item.getItemId();
        if (menuItemId == R.id.action_search) {
            Toast.makeText(MainActivity.this, "searh", Toast.LENGTH_SHORT).show();
        } else if (menuItemId == R.id.action_share) {
            Toast.makeText(MainActivity.this, "action_share", Toast.LENGTH_SHORT).show();
        } else if (menuItemId == R.id.action_item1) {
            Toast.makeText(MainActivity.this, "action_item1", Toast.LENGTH_SHORT).show();
        } else if (menuItemId == R.id.action_item2) {
            Toast.makeText(MainActivity.this, "action_item2", Toast.LENGTH_SHORT).show();
        }
        return true;
    }
});

(四): NavigationView

在Material Design中,NavigationView导航抽屉,被设计用于应用导航,提供了一种通用的导航方式,体现了设计的一致性。而NavigationView的典型用途就是配合之前v4包的DrawerLayout,作为其中的Drawer部分,即导航菜单的本体部分。NavigationView是一个导航菜单框架,使用menu资源填充数据,使我们可以更简单高效的实现导航菜单。它提供了不错的默认样式、选中项高亮、分组单选、分组子标题、以及可选的Header。
参考博文:http://www.jianshu.com/p/76e30f87a4ed;
Tip:在使用Android Studio 开发的时候创建Activity可以选择带NavigationView的Activity,一分钟就搞定了这个效果,你还瞎折腾啥;

碰到的坑:java.lang.IllegalArgumentException: DrawerLayout must be measured with MeasureSpec.EXACTLY.
这个错误是的解决办法是把DrawerLayout 的父容器宽高设置成match_paren

如何使用:典型的布局文件如下,外层是DrawerLayout,它的第一个child将作为content,第二个child作为Drawer

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        <android.support.v4.widget.DrawerLayout
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:openDrawer="start">
            <!-- Content -->
            <FrameLayout
                android:id="@+id/content_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/content_bg" />
            <!-- Drawer -->
            <android.support.design.widget.NavigationView
                android:id="@+id/navigation"
                android:layout_width="240dp"
                android:layout_height="match_parent"
                android:layout_gravity="start"
                android:background="@color/content_bg"
                android:fitsSystemWindows="true"
                app:headerLayout="@layout/drawer_header"
                app:menu="@menu/drawer" />
        </android.support.v4.widget.DrawerLayout>
    </android.support.design.widget.AppBarLayout>
</LinearLayout>

其中:在DrawerLayout节点添加tools:openDrawer=”start”这个属性目前发现的作用是在布局的时候能查看到效果,去除的话会使节点下的drawer布局在preview观察模式下不可见,删除亦没有影响
但是NavigationView节点下的android:layout_gravity=”start”这条属性不可删除,删除的后果是drawer布局充满屏幕,点击程序异常崩溃 ,且可在此节点下设置drawer布局的背景颜色
NavigationView节点下:这两条属性是引用抽屉的头布局和布局用的
app:headerLayout=”@layout/drawer_header”
app:menu=”@menu/drawer”
这里写图片描述
效果图如上:这里我们会看到toolbar的title是默认对应的Activity名称,如果想更换的话手动设置toolbar.setTitle(“噜噜噜”),并且你也可以看到一个text文本显示clock,这个就是toolbar自定义的按钮
抽屉头布局xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/heard_bg" />
</LinearLayout>

drawer布局xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_camara"
            android:icon="@android:drawable/ic_menu_camera"
            android:title="Import" />
        <item
            android:id="@+id/nav_manage"
            android:icon="@android:drawable/ic_menu_manage"
            android:title="Tools" />
    </group>
    <item android:title="Communicate">
        <menu>
            <item
                android:id="@+id/nav_share"
                android:icon="@android:drawable/ic_menu_share"
                android:title="Share" />
            <item
                android:id="@+id/nav_send"
                android:icon="@android:drawable/ic_menu_send"
                android:title="Send" />
        </menu>
    </item>
</menu>

说明:其中节点代表分组,android:checkableBehavior=”single”这个属性表述这个group子条目只能单选,我们还可以通过为item添加子菜单来实现带有头部的分组效果,就像上图group小组下面item那样设置,并可以为这个小组设置title;分组是可以选中的,在子 menu item 设置 android:checkable=”true” 就行了
在Activity中的代码:
Activity中必须实现:implements NavigationView.OnNavigationItemSelectedListener

//获取控件,设置监听
NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
navigationView.setNavigationItemSelectedListener(this);
//设置点击返回键抽屉缩进
@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}
//抽屉里item的点击事件
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();
    if (id == R.id.navigation_original) {

    } else if (id == R.id.navigation_library) {

    } else if (id == R.id.navigation_component) {

    } else if (id == R.id.navigation_ui) {

    } else if (id == R.id.navigation_text) {

    } else if (id == R.id.nav_send) {

    }
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

(五): Palette

compile ‘com.android.support:palette-v7:21.0.+’

Eclipse首先我们找到sdk/extras/android/support/v7/palette/libs/android-support-v7-palette.jar导入我们的工程。

 /**
     * 界面颜色的更改
     */
    private void colorChange(int position) {
        // 用来提取颜色的Bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),drawables[position] );
        // Palette的部分
        Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
            /**
             * 提取完之后的回调方法
             */
            @Override
            public void onGenerated(Palette palette) {
                Palette.Swatch vibrant = palette.getVibrantSwatch();
                //设置tabLayout的背景颜色,为palette取出来的颜色
                tabLayout.setBackgroundColor(vibrant.getRgb());
                //设置选中的TabLayout标签底部游标的颜色为palette取出来的颜色,并且加深颜色
                tabLayout.setSelectedTabIndicatorColor(colorBurn(vibrant.getRgb()));
                //设置tab标签颜色,第一个参数是未被选中tab标签颜色,第二个是选中的tab标签颜色
                tabLayout.setTabTextColors(getResources().getColor(R.color.gray),getResources().getColor(R.color.withe));
                //设置toolbar背景色为palette取出来的颜色
                toolbar.setBackgroundColor(vibrant.getRgb());
                if (android.os.Build.VERSION.SDK_INT >= 21) {
                    Window window = getWindow();
                    // 很明显,这两货是新API才有的。
                    window.setStatusBarColor(colorBurn(vibrant.getRgb()));
                    window.setNavigationBarColor(colorBurn(vibrant.getRgb()));
                }
            }
        });
    }
 /**
     * 颜色加深处理
     *
     * @param RGBValues
     *            RGB的值,由alpha(透明度)、red(红)、green(绿)、blue(蓝)构成,
     *            Android中我们一般使用它的16进制,
     *            例如:"#FFAABBCC",最左边到最右每两个字母就是代表alpha(透明度)、
     *            red(红)、green(绿)、blue(蓝)。每种颜色值占一个字节(8位),值域0~255
     *            所以下面使用移位的方法可以得到每种颜色的值,然后每种颜色值减小一下,在合成RGB颜色,颜色就会看起来深一些了
     * @return
     */
    private int colorBurn(int RGBValues) {
        int alpha = RGBValues >> 24;
        int red = RGBValues >> 16 & 0xFF;
        int green = RGBValues >> 8 & 0xFF;
        int blue = RGBValues & 0xFF;
        red = (int) Math.floor(red * (1 - 0.1));
        green = (int) Math.floor(green * (1 - 0.1));
        blue = (int) Math.floor(blue * (1 - 0.1));
        return Color.rgb(red, green, blue);
    }

最后附上demo:http://download.csdn.net/detail/laogong3783/9551694

结束语:写这篇博文只是想对最近的学习的知识做个整理,如有错误,欢迎指正,新人勿喷。

以上是关于Android M新控件知识整理的主要内容,如果未能解决你的问题,请参考以下文章

IOS开发-OC学习-常用功能代码片段整理

开源整理:Android App新手指引开源控件

Android 片段与复合控件

Android自定义控件系列—Button七十二变

android如何跨片段分离/附加保留视图状态

小程序各种功能代码片段整理---持续更新