你的位置:首页 > 操作系统

[操作系统]Android Material Design NavigationView 及 Palette 颜色提取器


DrawerLayout + NavigationView

DrawerLayout布局,通常在里面添加两个子控件,程序主界面添加到NavitagionView前面。

<android.support.v4.widget.DrawerLayout ="http://schemas.android.com/apk/res/android"  ="http://schemas.android.com/apk/res-auto"  ="http://schemas.android.com/tools"  android:id="@+id/drawer_layout"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:fitsSystemWindows="true"  tools:openDrawer="start">
  <include    layout="@layout/app_main"    android:layout_width="match_parent"    android:layout_height="match_parent" />
<android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" /></android.support.v4.widget.DrawerLayout>

这里有两个重要的属性headerLayout和menu,分别表示header部分布局和menu菜单资源。

程序主界面布局app_main,包含AppBarLayout和content_main,以及一个FloatingActionButton。

<android.support.design.widget.CoordinatorLayout ="http://schemas.android.com/apk/res/android"  ="http://schemas.android.com/apk/res-auto"  ="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
  <android.support.design.widget.FloatingActionButton    android:id="@+id/fab"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="bottom|end"    android:layout_margin="@dimen/fab_margin"    android:src="@android:drawable/ic_dialog_email" /></android.support.design.widget.CoordinatorLayout>

上面的主界面布局也以随意切换成其它布局。

注意事项:

1.DrawerLayout指定openDrawer为start,NavigationView指定layout_gravity为start。

2.指定NavigationView的headerLayout属性和menu属性,分别为layout布局文件和menu文件。

headerLayout

<??><LinearLayout ="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="@dimen/nav_header_height"  android:background="@drawable/side_nav_bar"  android:gravity="bottom"  android:orientation="vertical"  android:theme="@style/ThemeOverlay.AppCompat.Dark">  <ImageView    android:id="@+id/imageView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:paddingTop="@dimen/nav_header_vertical_spacing"    android:src="@android:drawable/sym_def_app_icon" />  <TextView    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:paddingTop="@dimen/nav_header_vertical_spacing"    android:text="Android Studio"    android:textAppearance="@style/TextAppearance.AppCompat.Body1" />  <TextView    android:id="@+id/textView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="android.studio@android.com" /></LinearLayout>

menu菜单资源

调用 setNavigationItemSelectedListener() 后,在菜单项被选中的时候,你会通过OnNavigationItemSelectedListener 得到回调。在处理回调时,你会知道是哪个菜单项被点击,此时你可以处理选择事件,修改选中状态,加载新的内容,以及通过代码来关闭 drawer,或者其他任何你想执行的操作。

<menu ="http://schemas.android.com/apk/res/android">​  <group android:checkableBehavior="single">    <item      android:id="@+id/nav_camera"      android:icon="@drawable/ic_menu_camera"      android:title="Import" />    <item      android:id="@+id/nav_gallery"      android:icon="@drawable/ic_menu_gallery"      android:title="Gallery" />    <item      android:id="@+id/nav_slideshow"      android:icon="@drawable/ic_menu_slideshow"      android:title="Slideshow" />    <item      android:id="@+id/nav_manage"      android:icon="@drawable/ic_menu_manage"      android:title="Tools" />  </group>  <item android:title="Communicate">    <menu>      <item        android:id="@+id/nav_share"        android:icon="@drawable/ic_menu_share"        android:title="Share" />      <item        android:id="@+id/nav_send"        android:icon="@drawable/ic_menu_send"        android:title="Send" />    </menu>  </item></menu>

下面是涉及到的styles文件

<resources>  <!-- 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>  </style>  <style name="AppTheme.NoActionBar">    <item name="windowActionBar">false</item>    <item name="windowNoTitle">true</item>  </style>  <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />  <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /></resources>

Activity

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);    setSupportActionBar(toolbar);    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);    fab.setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View view) {        Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)            .setAction("Action", null).show();      }    });    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(        this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);    drawer.setDrawerListener(toggle);    toggle.syncState();    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);    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();    }  }  @Override  public boolean onCreateOptionsMenu(Menu menu) {    getMenuInflater().inflate(R.menu.main, menu);    return true;  }  @Override  public boolean onOptionsItemSelected(MenuItem item) {    int id = item.getItemId();    if (id == R.id.action_settings) {      return true;    }    return super.onOptionsItemSelected(item);  }  @SuppressWarnings("StatementWithEmptyBody")  @Override  public boolean onNavigationItemSelected(MenuItem item) {    int id = item.getItemId();    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);    drawer.closeDrawer(GravityCompat.START);    return true;  }}

Toolbar设置

    mToolbar = (Toolbar) findViewById(R.id.toolbar);    mToolbar.setTitle("Rocko");    // 标题的文字需在setSupportActionBar之前,不然会无效    // toolbar.setSubtitle("副标题");    setSupportActionBar(mToolbar);    /* 这些通过ActionBar来设置也是一样的,注意要在setSupportActionBar(toolbar);之后,不然就报错了 */    // getSupportActionBar().setTitle("标题");    // getSupportActionBar().setSubtitle("副标题");    // getSupportActionBar().setLogo(R.drawable.ic_launcher);    /* 菜单的监听可以在toolbar里设置,也可以像ActionBar那样,通过下面的两个回调方法来处理 */    mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {      @Override      public boolean onMenuItemClick(MenuItem item) {        switch (item.getItemId()) {          case R.id.action_settings:            Toast.makeText(MainActivity.this, "action_settings", Toast.LENGTH_SHORT).show();            break;          case R.id.action_share:            Toast.makeText(MainActivity.this, "action_share", Toast.LENGTH_SHORT).show();            break;          default:            Log.e("tag", item.toString());            break;        }        return true;      }    });

设置ActionBar及StatusBar背景颜色,在Api21及以上才有。

    if (null != vibrant) {        /* 界面颜色UI统一性处理,看起来更Material一些 */      mPagerSlidingTabStrip.setBackgroundColor(vibrant.getRgb());      mPagerSlidingTabStrip.setTextColor(vibrant.getTitleTextColor());      // 其中状态栏、游标、底部导航栏的颜色需要加深一下,也可以不加,具体情况在代码之后说明      mPagerSlidingTabStrip.setIndicatorColor(colorBurn(vibrant.getRgb()));      mToolbar.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()));      }    }

colorBurn

  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);  }

Palette

根据图片来决定标题的颜色和标题栏的背景色,这样视觉上更具有冲击力和新鲜感,而不像统一色调那样呆板。 

Palette是什么?它能让你从图像中提取突出的颜色。这个类能提取以下突出的颜色:

  1. Vibrant(充满活力的)

  2. Vibrant dark(充满活力的黑)

  3. Vibrant light(充满活力的亮)

  4. Muted(柔和的)

  5. Muted dark(柔和的黑)

  6. Muted lighr(柔和的亮)

如何使用?

要提取这些颜色,在你加载图片的后台线程中传递一个位图对象给Palette.generate()静态方法。如果不使用线程,则调用Palette.generateAsync()方法并且提供一个监听器去替代。

你可以在Palette类中使用getter方法来从检索突出的颜色,比如Palette.getVibrantColor。

Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {@Overridepublic void onGenerated(Palette palette) {   xx.setBackgroundColor(palette.getVibrantColor(mContext       .getResources().getColor(R.color.black_translucent_60))); }});

2.

Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {@Overridepublic void onGenerated(Palette palette) {   xx.setBackgroundColor(palette.getVibrantColor(mContext       .getResources().getColor(R.color.black_translucent_60))); }});Palette.generateAsync(bitmap,new Palette.PaletteAsyncListener() {   @Override   public void onGenerated(Palette palette) {     Palette.Swatch vibrant =palette.getVibrantSwatch();      if (swatch != null) {        // If we have a vibrant color        // update the title TextView        titleView.setBackgroundColor(vibrant.getRgb());        titleView.setTextColor(vibrant.getTitleTextColor());      }   } });

上面的代码展示了如何从一张图片中提取颜色将textView的背景色设置成图片的主色调,然后再使用getTitleTextColor()来设置一个匹配的文字颜色。

比如如果你的TextView 有个背景图片,要想让字体颜色能够和背景图片匹配,则使用getBodyTextColor()比较合适,getTitleTextColor()其实应该和getBodyTextColor()差不多。

size的问题

你还可以使用如下方法一次性获得所有的swatch:

List<Palette.Swatch> swatches = palette.getSwatches();

在上面的代码中,你可能注意到了可以设置palette的size。size越大,花费的时间越长,而越小,可以选择的色彩也越小。最佳的选择是根据image的用途:

  • 头像之类的,size最好在24-32之间;
  • 风景大图之类的 size差不多在8-16;
  • 默认是16. 

有四种创建Palette实例的方法:

// Synchronous methods.// --------------------------------// These should be used when you have access to the underlying image loading thread.// Picasso allows this through a Transformation. For other libraries, YMMV.// Uses the default palette size (16).Palette p = Palette.generate(bitmap);// Allows you to specify the maximum palette size, in this case 24.Palette p = Palette.generate(bitmap, 24);// Asynchronous methods// --------------------------------// This is the quick and easy integration path. Internally uses an AsyncTask so// this may not be optimal (since you're dipping in and out of threads)// Uses the default palette size (16).Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {  @Override  public void onGenerated(Palette palette) {    // Here's your generated palette  }});// Allows you to specify the maximum palette size, in this case 24.Palette.generateAsync(bitmap, 24, new Palette.PaletteAsyncListener() {  @Override  public void onGenerated(Palette palette) {    // Here's your generated palette  }});

 

创建完一个实例之后,我们还需要得到一种采集的样本(swatch),有6中样本(swatch):

Vibrant. Palette.getVibrantSwatch()Vibrant dark. Palette.getDarkVibrantSwatch()Vibrant light. Palette.getLightVibrantSwatch()Muted. Palette.getMutedSwatch()Muted dark. Palette.getDarkMutedSwatch()Muted light. Palette.getLightMutedSwatch()

具体选择哪一种取决于你自己,大多数情况下我们都使用Vibrant and Dark Vibrant。

使用样本(swatch)

swatch有以下方法:

getPopulation(): the amount of pixels which this swatch represents.getRgb(): the RGB value of this color.getHsl(): the HSL value of this color.getBodyTextColor(): the RGB value of a text color which can be displayed on top of this color.getTitleTextColor(): the RGB value of a text color which can be displayed on top of this color.