• 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 - 高级篇

    参考资料

  • 相关阅读:
    Setting Text to Image On Android and Adjudt the text font size based on the android resolution
    BlackBerry Localization sample (2): Localizing a Blackberry Java application
    How to get the android resolution
    How to set location in BlackBerry simulator
    BlackBerry JDE (Java development Environment)
    Android Localization
    BlackBerry Localization sample (1)
    Image
    Android应用添加(创建)和删除及判断是否存在桌面快捷方式
    Android开发笔记——圆角和边框们
  • 原文地址:https://www.cnblogs.com/ct2011/p/4493384.html
Copyright © 2020-2023  润新知