美文网首页Android 开发艺术探索读书笔记程序员Android知识
Android 开发艺术探索读书笔记 6 -- Android

Android 开发艺术探索读书笔记 6 -- Android

作者: 开心wonderful | 来源:发表于2017-08-18 14:14 被阅读73次

    本篇文章主要介绍以下几个知识点:

    • Drawable 简介
    • Drawable 的分类
    • 自定义 Drawable
    hello,夏天 (图片来源于网络)

    Drawable,使用简单,比自定义 View 成本低,非图片类型的 Drawable 占用空间小。

    6.1 Drawable 简介

    Drawable,一种图像的概念(不一定是图片,如通过颜色也可构造出图像)。

    在 Android 设计中,Drawable 是一个抽象类,是所有 Drawable 对象的基类,其层次关系如下:

    Drawable 的层次关系

    通过 getIntrinsicWidthgetIntrinsicHeight 可获取 Drawable 的内部宽高。一张图片的宽高是其内部宽高,但一个颜色形成的 Drawable 没有内部宽高概念。(注:Drawable 的内部宽高不等同于其大小,一般是无大小概念,如作 View 的背景时会被拉伸至 View 的同等大小)

    6.2 Drawable 的分类

    Drawable 的种类繁多,如 BitmapDawableShapDrawableLayerDrawableStateListDrawable等。

    6.2.1 BitmapDrawable

    表示一张图片。实际开发中直接引用原始图片即可,也可通过 XML 的方式来描述,如下:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- src  图片的资源id
         antialias  是否开启图片抗锯齿功能
         dither  是否开启抖动效果
         filter  是否开启过滤效果
         gravity  对图片进行定位
         mipMap  纹理映射(默认为 false)
         tileMode  平铺模式(默认 disabled),开启后 gravity 属性会被忽略-->
    <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
        android:src="@mipmap/ic_header"
        android:antialias="true"
        android:dither="true"
        android:filter="true"
        android:gravity="center"
        android:mipMap="false"
        android:tileMode="disabled"/>
    

    其中 gravity 属性的可选项如下:

    gravity 属性的可选项

    其中 tileMode 属性开启后各效果如下:

    平铺模式下的图片显示效果

    NinePatchDrawable,.9格式的图片,可自动根据所需的宽高进行相应的缩放而不失真,和 BitmapDrawable 一样,在实际开发中直接引用图片即可,也可通过 XML 来描述如下:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- src  图片的资源id
         dither  是否开启抖动效果 -->
    <nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
        android:src="@drawable/message_left"
        android:dither="true" />
    

    实际使用中在 bitmap 标签中也可用 .9 图,即 BitmapDrawable 也可代表一个 .9 格式的图片。

    6.2.2 ShapeDrawable

    通过颜色来构造的图形,可以是纯色的图形,也可以是具有渐变效果的图形。其语法如下:

    <?xml version="1.0" encoding="utf-8"?>
    
    <!-- shape  图形的形状:rectangle 矩形、 oval 椭圆、
                line 横线、 ring 圆环
         注:ling 和 ring 需要<stroke>标签来指定线的宽度和颜色等信息-->
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    
        <!-- 表示 shape 的四个角的角度,只适用于矩形 shape
             radius 为四个角设定相同的角度,优先级低
             其他分别指设定左上角、右上角、左下角、右下角的角度-->
        <corners
            android:radius="32px"
            android:topLeftRadius="32px"
            android:topRightRadius="32dp"
            android:bottomLeftRadius="32px"
            android:bottomRightRadius="32px"/>
    
        <!-- 表渐变效果,与 <solid> 标签是互相排斥的
             angle  渐变的角度,默认0,其值必须为45的倍数(0:从左到右 90:从上到下)
             centerX、centerY  渐变的中心点的横坐标、纵坐标
             startColor、centerColor、endColor  渐变的起始、中间、结束色
             gradientRadius  渐变的半径,仅当 android:type="radial" 时有效
             type  渐变的类别,1.linear:线性  2.radial:径向  3.sweep:扫描线
             useLevel  一般为false,当Drawable作为StateListDrawable使用时为true -->
        <gradient
            android:angle="0"
            android:centerX="32"
            android:centerY="32"
            android:startColor="@color/colorPrimary"
            android:centerColor="@color/colorPrimaryDark"
            android:endColor="@color/colorAccent"
            android:gradientRadius="32dp"
            android:type="linear"
            android:useLevel="false"/>
    
        <!-- 表示包含它的 View 的空白-->
        <padding
            android:left="32dp"
            android:right="32dp"
            android:top="32dp"
            android:bottom="32dp"/>
    
        <!-- shape 的大小
             width、height  宽、高
            (注:ShapeDrawable 的固有宽高,但作为View背景时无效)-->
        <size
            android:width="32dp"
            android:height="32dp" />
    
        <!-- 表示纯色填充
             color  指定 shape 中填充的颜色-->
        <solid
            android:color="@color/colorAccent" />
    
        <!-- 描边
             width  描边的宽度
             color  描边的颜色
             dashWidth  组成虚线的线段的宽度
             dashGap  组成虚线的线段间的间隔
            (注:dashWidth 和 dashGap 有任何一个为 0,则虚线效果无效)-->
        <stroke
            android:width="32dp"
            android:color="@color/colorAccent"
            android:dashWidth="32dp"
            android:dashGap="32dp"/>
    
    </shape>
    

    其中 shape 属性中的 ring 这个形状,有 5 个特殊属性如下:

    ring 的属性值

    其中 type 属性各类别的区别如下:

    渐变的类别,从左到右依次为 linear、radial、sweep

    6.2.3 LayerDrawable

    其对应的 XML 标签是<layer-list>,表示一种层次化的 Drawable 集合,一种叠加的效果,其语法如下:

    <?xml version="1.0" encoding="utf-8"?>
    
    <!-- 一个 layer-list 中可以包含多个 item,每个 item 表示一个 Drawable 
         layer-list 有层次的概念,下面的 item 会覆盖 上面的 item -->
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <!-- drawable  可以直接引用已有的 Drawable 资源
             top、bottom、left、right:Drawable相对于View的上下左右的偏移量,单位为像素-->
        <item
            android:drawable="@drawable/drawable_test"
            android:id="@id/btn_chapter06"
            android:top="@dimen/drawable_dimen"
            android:right="@dimen/drawable_dimen"
            android:bottom="@dimen/drawable_dimen"
            android:left="@dimen/drawable_dimen"/>
        
    </layer-list>
    

    下面是一个 layer-list 具体使用例子,它实现了微信中的文本输入框效果,代码如下:

    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item>
            <shape android:shape="rectangle">
                <solid android:color="#0ac39e"/>
            </shape>
        </item>
    
        <item android:bottom="6dp">
            <shape android:shape="rectangle">
                <solid android:color="#ffffff"/>
            </shape>
        </item>
    
        <item
            android:bottom="1dp"
            android:left="1dp"
            android:right="1dp">
            <shape android:shape="rectangle">
                <solid android:color="#ffffff"/>
            </shape>
        </item>
    
    </layer-list>
    

    运行效果如下:

    layer-list 的应用

    6.2.4 StateListDrawable

    其对应的 XML 标签是<selector>,也是表示 Drawable 集合,每个 Drawable 对应着 View 的一种状态,其语法如下:

    <?xml version="1.0" encoding="utf-8"?>
    
    <!-- constantSize  StateListDrawable 的固有大小是否随着其状态的改变而改变
                       默认 false,即随着状态的改变而改变
         dither  是否开启抖动效果 默认 true
         layer-variablePadding  StateListDrawable 的 padding是否随着其状态的
                                改变而改变,默认 false
                                -->
    <selector xmlns:android="http://schemas.android.com/apk/res/android"
        android:constantSize="true"
        android:dither="true"
        android:variablePadding="false">
    
        <!-- drawable  已有 Drawable 的资源id
             其他表示 View 的各种状态 -->
        <item
            android:drawable="@drawable/drawable_test"
            android:state_pressed="true"
            android:state_focused="true"
            android:state_hovered="true"
            android:state_selected="true"
            android:state_checkable="true"
            android:state_checked="true"
            android:state_enabled="true"
            android:state_activated="true"
            android:state_window_focused="true" />
    
    </selector>
    

    其中 <item> 标签中的 View 的常见状态如下:

    View 的常见状态

    StateListDrawable 主要用于设置可单击的 View 的背景,如 Button,常见的例子如下:

    <selector xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item
            android:drawable="@drawable/drawable_pressed"
            android:state_pressed="true" />
    
        <item
            android:drawable="@drawable/drawable_pressed"
            android:state_focused="true" />
    
        <!-- default -->
        <item android:drawable="@drawable/drawable_normal" />
    
    </selector>
    

    6.2.5 LevelListDrawable

    其对应的 XML 标签是<level-list>,也是表示 Drawable 集合,每个 Drawable 都有一个等级的概念。LevelListDrawable 根据不同的等级切换对应的 Drawable,其语法如下:

    <level-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <!-- maxLevel、minLevel  指定等级范围,在最小值和最大值之间
                                 的等级会对应此 item 中的 Drawable-->
        <item
            android:drawable="@drawable/drawable_test"
            android:maxLevel="100"
            android:minLevel="32"/>
    
    </level-list>
    

    LevelListDrawable 作为 View 的背景时可通过 Drawable 的 setLevel 来设置不同的等级,作为 ImageView 的前景时可通过 ImageView 的 setImageLevel 来切换 Drawable。

    Drawable 等级范围为 0 ~ 10000,例子如下:

    <level-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item
            android:drawable="@drawable/drawable_off"
            android:maxLevel="0" />
    
        <item
            android:drawable="@drawable/drawable_on"
            android:maxLevel="1" />
    
    </level-list>
    

    6.2.6 TransitionDrawable

    其对应的 XML 标签是<transition>,用于实现两个 Drawable 之间的淡入淡出效果,其语法如下:

    <transition xmlns:android="http://schemas.android.com/apk/res/android">
    
        <!-- top、bottom、left、right 指 Drawable 四周的偏移量-->
        <item
            android:drawable="@drawable/drawable_test"
            android:top="@dimen/drawable_dimen"
            android:bottom="@dimen/drawable_dimen"
            android:right="@dimen/drawable_dimen"
            android:left="@dimen/drawable_dimen"/>
    
    </transition>
    

    使用步骤如下:

    1. 定义 TransitionDrawable

    <transition xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:drawable="@drawable/drawable_test"/>
    
        <item android:drawable="@drawable/drawable_test_02" />
    
    </transition>
    

    2. 设置为 View 的背景

    <!-- 注:在 ImageView 中也可直接作为 Drawable 来用 -->
    <TextView
        android:id="@+id/tv_transition"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@drawable/chapter_06_transition_drawable"/>
    

    3. 通过startTransitionreverseTransition 来开启效果及其逆过程

    TextView tvTransition = (TextView) findViewById(R.id.tv_transition);
    TransitionDrawable drawable = (TransitionDrawable) tvTransition.getBackground();
    drawable.startTransition(2000);
    

    运行效果如下:

    transition 效果

    6.2.7 InsetDrawable

    其对应的 XML 标签是<inset>,可以将其他 Drawable 内嵌到自己当中,并可在四周留出一定的间距,其语法如下:

    <!-- insetTop、insetBottom、insetRight、insetLeft  
         表示 顶部、底部、右边、左边 内凹的大小 -->
    <inset xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:insetTop="@dimen/drawable_dimen"
        android:insetBottom="@dimen/drawable_dimen"
        android:insetRight="@dimen/drawable_dimen"
        android:insetLeft="@dimen/drawable_dimen"/>
    

    当一个 View 的背景比自己的实际区域小时可采用 InsetDrawable 实现(也可采用 LayerDrawable 来实现),如下:

    <inset xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:insetTop="15dp"
        android:insetBottom="15dp"
        android:insetRight="15dp"
        android:insetLeft="15dp">
        <shape android:shape="rectangle">
            <solid android:color="#ff0000"/>
        </shape>
    </inset>
    

    运行效果:

    InsetDrawable 效果

    6.2.8 ScaleDrawable

    其对应的 XML 标签是<scale>,它可以根据自己的等级(level)将指定的 Drawable 缩放到一定比例,其语法如下:

    <!-- scaleGravity  等同于 shape 中的 android:gravity
         scaleWidth、scaleHeight  指定对 Drawable 宽高的缩放比例,百分比表示 -->
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:scaleGravity="center"
        android:scaleHeight="32%"
        android:scaleWidth="32%" />
    

    ScaleDrawable 的默认等级为 0,即不可见,要 ScaleDrawable 可见则等级不为 0。如将一张图片缩小为原来的30%,如下:

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:scaleGravity="center"
        android:scaleHeight="70%"
        android:scaleWidth="70%" />
    

    然后设置 ScaleDrawable 的等级大于 0且小等于10000的值,如下:

    TextView tvScale = (TextView) findViewById(R.id.tv_scale);
    ScaleDrawable scaleDrawable = (ScaleDrawable) tvScale.getBackground();
    scaleDrawable.setLevel(1);
    

    6.2.9 ClipDrawable

    其对应的 XML 标签是<clip>,它可以根据自己的等级(level)来剪裁另一个Drawable,其语法如下:

    <!-- clipOrientation  剪裁方向:水平或竖直
         gravity  需要和 clipOrientation 一起才能发挥作用 -->
    <clip xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:clipOrientation="horizontal"
        android:gravity="center" />
    

    其中 gravity 的属性如下:

    ClipDrawable 的 gravity 属性

    下面实现将一张图片从上往下进行裁剪的效果:

    1. 定义 ClipDrawable

    <!-- 实现顶部的裁剪效果,方向设为竖直,gravity 设为 bottom -->
    <clip xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/drawable_test"
        android:clipOrientation="vertical"
        android:gravity="bottom" />   
    

    2. 设置给 ImageView (也可作为 View 的背景)

     <ImageView
         android:id="@+id/iv_clip"
         android:layout_width="100dp"
         android:layout_height="100dp"
         android:src="@drawable/chapter_06_clip_drawable"
         android:layout_gravity="center_horizontal" />
    

    3. 在代码中设置 ClipDrawable 的等级

    ImageView ivClip = (ImageView) findViewById(R.id.iv_clip);
    ClipDrawable clipDrawable = (ClipDrawable) ivClip.getDrawable();
    // 0-10000,其中0表示完全裁剪,10000表示不裁剪
    clipDrawable.setLevel(5000);
    

    运行效果如下:

    ClipDrawable 的裁剪效果

    6.3 自定义 Drawable

    Drawable 使用范围单一,作为 ImageView 的图像显示或者作为 View 的背景。

    Drawable 的工作原理很简单,其核心是 draw 方法,可通过重写其 draw 方法来自定义 Drawable。(注:自定义的 Drawable 无法在 XML 中使用)

    下面自定义一个圆形的 Drawable,它的半径会随着 View 的变化而变化,可作为 View 的通用背景,如下:

    public class CustomDrawable extends Drawable {
    
        private Paint mPaint;
    
        public CustomDrawable(int color) {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(color);
        }
    
        @Override
        public void draw(@NonNull Canvas canvas) {
            final Rect r = getBounds();
            float cx = r.exactCenterX();
            float cy = r.exactCenterY();
            canvas.drawCircle(cx, cy ,Math.min(cx, cy), mPaint);
        }
    
        @Override
        public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
            mPaint.setAlpha(alpha);
            invalidateSelf();
        }
    
        @Override
        public void setColorFilter(@Nullable ColorFilter colorFilter) {
            mPaint.setColorFilter(colorFilter);
            invalidateSelf();
        }
    
        @Override
        public int getOpacity() {
            // not sure, so be safe
            return PixelFormat.TRANSLUCENT;
        }
    }
    

    在代码中设置自定义的 Drawale:

    ImageView ivCustom = (ImageView) findViewById(R.id.iv_custom_drawable);
    CustomDrawable customDrawable = new CustomDrawable(getResources().getColor(R.color.colorAccent));
    ivCustom.setImageDrawable(customDrawable);
    

    运行效果如下:

    自定义圆形的 Drawable

    以上便是完整的自定义 Drawable 的流程。值得注意的是,自定义的 Drawable 有固定大小时最好重写 getIntrinsicWidthgetIntrinsicHeight 这两方法。

    本篇文章就介绍到这。

    相关文章

      网友评论

        本文标题:Android 开发艺术探索读书笔记 6 -- Android

        本文链接:https://www.haomeiwen.com/subject/orfnrxtx.html