zoukankan      html  css  js  c++  java
  • Material适配1

    版权声明:

    欢迎转载,但请保留文章原始出处

    作者:GavinCT

    出处:http://www.cnblogs.com/ct2011/p/4493384.html

    随着Material Design的普及,很多开发人员都会面临App的Material适配。如果你的App不只是针对5.0以上设备的话(多数情况也必须做兼容), 那么下面的经验总结将会对你有所帮助。
    当然,有些公司的App不会改成Material Design,但如果你以前使用AppCompatV7的话,升级到21后,你必然面临和以前不一样的使用方式,了解新的方式也是必须的。

    言归正传,官方给我们的适配方案是AppCompatV7,新发布的22.1.1适配包相对于22又进行了较大的改动,同时对Material适配更加强大,因此本文主要介绍基于22.1.1版本的适配流程。

    开始使用

    compile 'com.android.support:appcompat-v7:22.1.1'
    

    这里需要说明的是使用19、20及其以下版本仍然是Holo风格,
    使用21和22版本都会有Material的效果。

    Theme介绍

    引用完库之后,首先要面对的是配置主题。否则如果你以前使用AppCompat的话,运行之后会发现App惨不忍睹。

    分类

    Theme主要有以下几种分类:

    • Theme.AppCompat (dark version)
    • Theme.AppCompat.Light (light version)
    • Theme.AppCompat.Light.DarkActionBar

    如果以前使用ActionBar Holo风格时使用的就是AppCompat,那么这些地方是不需要更改的。

    注: Material下的ActionBar会比之前更大,这点可在之后的ActionMode讨论中看到。

    配置

    Theme配置和原先有些不一样,配置示例如下:

    <style name="Theme.App" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Main theme colors -->
        <!--   your app branding color for the app bar -->
        <item name="colorPrimary">@color/theme_primary</item>
        <!--   darker variant for the status bar and contextual app bars -->
        <item name="colorPrimaryDark">@color/theme_primary_dark</item>
        <!--   theme UI controls like checkboxes and text fields -->
        <item name="colorAccent">@color/theme_color_accent</item>
    </style>
    

    先上官方解释图:

    ThemeColor

    图上的各参数都可以直接配置到主题中生效。其中colorPrimaryDark仅在Lollipop以上机器生效。

    colorAccent解析

    colorAccent会改变光标颜色、checkbox背景色等。
    基本上可以理解为控件的主色调。

    以Checkbox为例:
    官方默认是绿色的

    colorAccent_green

    改变colorAccent为蓝色后

    colorAccent_blue

    自定义Status Bar (Lollipop以上设备)

    Material可以让你轻松订制Staus Bar。

    • 可以使Theme中的android:statusBarColor属性来改变,默认从android:colorPrimaryDark中获取。
    • 代码设置: Window.setStatusBarColor()

    常见错误

    现在AppCompat对窗口主题的flag要求更严格。
    主要原因是为了支持Dialog,大量使用了 AppCompat 之前并没有重视的 windowNoTitle 标志。

    升级到v22.1.0以后(包括本文讲述的22.1.1),你可能遇到下面的异常:

    Caused by: java.lang.IllegalArgumentException: AppCompat does not support the current theme features
            at android.support.v7.app.AppCompatDelegateImplV7.ensureSubDecor(AppCompatDelegateImplV7.java:360)
            at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:246)
            at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:106)
    

    解决办法有两种:

    • 最简单的是使用 Theme.AppCompat.NoActionBar 作为 parent theme,这样就会一直正常。

    • 如果不能这样做(或许你需要同时支持ActionBar和NoActionBar,其实也可以通过第一种方式来解决,可能colorPrimary之类的需要多配置一遍),
      你可以采用:

      <style name="MyTheme" parent="Theme.AppCompat">
          ...
      </style>
      
      <style name="MyTheme.NoActionBar" parent="MyTheme">
          <!-- Both of these are needed -->
          <item name="windowActionBar">false</item>
          <item name="windowNoTitle">true</item>
      </style>
      

    AppCompatActivity使用

    最新的22.1.1版本,ActionBarActivity已经废弃。开始采用AppCompatActivity。
    如果你以前使用的是ActionBarActivity,建议替换掉,不需要更改其他代码。

    (特别重要的AppCompatDelegate登场,具体代码可以查看AppCompatActivity实现,这里可以简单替换下快速适配,其实一般情况下也不需要自己来写AppCompatDelegate)

    OK,到这里,其实你的App差不多就能正常运行了,只是有些细节方面还需要继续完善。
    你可能已经注意到你的Dialog还不是Material风格,那么我们继续来看Dialog。

    AppCompatDialog

    AppCompat之前的21、22版本都没有实现Material Dialog。 在22.1.x发布时,这个问题终于解决了。

    AppCompatDialog是AppCompat themed Dialog的 Base class.
    目前他的子类只有AlertDialog,但已经足够使用。

    使用方式也很简单,直接将AlertDialog改为android.support.v7.app包下的AlertDialog即可。
    其他使用方式一样,不需要做任何改动。

    Preference

    官方至今没有做到完全的适配。
    对比图:
    4.x设备上
    preference_4.x

    5.x设备上
    preference_5.x

    可以看到PreferenceCategoryPreference在4.x设备上底部都有横线,5.x设备上都没有。
    也可以看到CheckBoxPreference是已经适配了的。

    为了能让Preference适配的更加彻底,推荐下常用的第三方适配库: Android-MaterialPreference

    但是作者并没有去写DialogPreference一类的,比如常见的ListPreference。
    其实这里是有解决办法的。上面已经写到了新版的AlertDialog,配合How can I change the appearance of ListPreference Dialog 这篇帖子,就不难实现。
    但也可以看到有人讨论了Material规范中提到的实现方式,当然也有人根据Google规范进行了实现,这里可以根据需求来自行选择实现方式。

    关于Preference需要说明的是:

    • 如果app是针对11以上的,推荐使用AppCompatActivity和PreferenceFragment来实现。
    • 如果兼容更早的版本,需要借助AppCompatDelegate来实现,Google的示例代码:AppCompatPreferenceActivity.java
    /*
     * Copyright (C) 2014 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.example.android.supportv7.app;
    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.preference.PreferenceActivity;
    import android.support.annotation.LayoutRes;
    import android.support.annotation.Nullable;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.AppCompatDelegate;
    import android.support.v7.widget.Toolbar;
    import android.view.MenuInflater;
    import android.view.View;
    import android.view.ViewGroup;
    /**
     * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
     * to be used with AppCompat.
     *
     * This technique can be used with an {@link android.app.Activity} class, not just
     * {@link android.preference.PreferenceActivity}.
     */
    public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
        private AppCompatDelegate mDelegate;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            getDelegate().installViewFactory();
            getDelegate().onCreate(savedInstanceState);
            super.onCreate(savedInstanceState);
        }
        @Override
        protected void onPostCreate(Bundle savedInstanceState) {
            super.onPostCreate(savedInstanceState);
            getDelegate().onPostCreate(savedInstanceState);
        }
        public ActionBar getSupportActionBar() {
            return getDelegate().getSupportActionBar();
        }
        public void setSupportActionBar(@Nullable Toolbar toolbar) {
            getDelegate().setSupportActionBar(toolbar);
        }
        @Override
        public MenuInflater getMenuInflater() {
            return getDelegate().getMenuInflater();
        }
        @Override
        public void setContentView(@LayoutRes int layoutResID) {
            getDelegate().setContentView(layoutResID);
        }
        @Override
        public void setContentView(View view) {
            getDelegate().setContentView(view);
        }
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            getDelegate().setContentView(view, params);
        }
        @Override
        public void addContentView(View view, ViewGroup.LayoutParams params) {
            getDelegate().addContentView(view, params);
        }
        @Override
        protected void onPostResume() {
            super.onPostResume();
            getDelegate().onPostResume();
        }
        @Override
        protected void onTitleChanged(CharSequence title, int color) {
            super.onTitleChanged(title, color);
            getDelegate().setTitle(title);
        }
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            getDelegate().onConfigurationChanged(newConfig);
        }
        @Override
        protected void onStop() {
            super.onStop();
            getDelegate().onStop();
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            getDelegate().onDestroy();
        }
        public void invalidateOptionsMenu() {
            getDelegate().invalidateOptionsMenu();
        }
        private AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                mDelegate = AppCompatDelegate.create(this, null);
            }
            return mDelegate;
        }
    }
    

    Material中还有一个重要的特性是阴影的设置和波纹效果的实现,这里来粗略说明一下:

    Elevation - 设置阴影

    v21以后在View的xml中使用android:elevation属性,或者在代码中使用View的setElevation()方法。

    兼容老版的阴影策略

    还是需要使用.9图片的阴影来做。

    注: ViewCompat.setElevation() sadly doesn't apply shadows in pre-Lollipop.

    RippleDrawable - 波纹效果

    使用已经提供好的

    • ?android:attr/selectableItemBackground
      扩散到View边界
    • ?android:attr/selectableItemBackgroundBorderless
      设置后,会从孩子往父亲找一个依附的色。如果View往上找的时候,亲生父亲没背景色,会继续向上查找直到最顶端。找到了最顶端的爷爷,这个时候才绘制。
      然而,如果父亲的兄弟又绘制了颜色,且盖住了最顶端的绘制,会导致看不到效果。如果有一定的透明度,结果就显而易见了。
      特别注意:
      • 当把硬件加速给关闭时,这个效果是没有的。
      • 这是API 21的新属性,老版本无法使用.

    改变默认响应色

    改变Theme中的android:colorControlHighlight属性。

    自定义

    <!-- A green ripple drawn atop a black rectangle. -->
    <ripple android:color="#ff00ff00">
    	<item android:drawable="@android:color/black" />
    </ripple>
    
    <!-- A blue ripple drawn atop a drawable resource. -->
    <ripple android:color="#ff0000ff">
    	<item android:drawable="@drawable/my_drawable" />
    </ripple>
    

    android:color中是点击响应色,也是波纹扩散色。
    item中是正常状态下的显示。

    一般使用时会和原有的selector配合,原有的selector负责5.0以下显示效果,
    新的selector内部含有ripple标签放在drawable-v21中,保证点击效果。

    selector

    写到这里,我觉得对一个中国开发者的Material入门篇来说,还需要说明下魅族适配的问题

    关于魅族SmartBar适配问题

    和魅族官方技术人员沟通过,不(pu)幸(tian)被(tong)告(qing)知(a):使用AppCompatV7 21以上,暂时无法进行SmartBar适配。

    原因大概解释如下:

    • v19的时候,ActionBar的处理是:如果系统有,系统处理;系统没有,自己画。
    • v21以后都是Compat库自己画了,不会调用系统的,因此魅族无法获取合并到SmartBar中。

    so,坐等魅族找到新的适配策略或者放弃SmartBar~~

    最后

    下一篇Toolbar与ActionMode,继续看请点击Material适配2 - 高级篇

    参考资料

  • 相关阅读:
    在TreeView控件节点中显示图片
    PAT 甲级 1146 Topological Order (25 分)
    PAT 甲级 1146 Topological Order (25 分)
    PAT 甲级 1145 Hashing
    PAT 甲级 1145 Hashing
    PAT 甲级 1144 The Missing Number (20 分)
    PAT 甲级 1144 The Missing Number (20 分)
    PAT 甲级 1151 LCA in a Binary Tree (30 分)
    PAT 甲级 1151 LCA in a Binary Tree (30 分)
    PAT 甲级 1149 Dangerous Goods Packaging
  • 原文地址:https://www.cnblogs.com/ct2011/p/4493384.html
Copyright © 2011-2022 走看看