Android Drawable完全解析(二):Drawable子类用法总结(一)

Android Drawable完全解析(一):Drawable源码分析(上)
Android Drawable完全解析(一):Drawable源码分析(中)
Android Drawable完全解析(一):Drawable源码分析(下)

之前文章对Drawable的源码做了简单分析,下面总结一下Drawable比较常用的子类在xml及Java中的用法!
先上图看一下Drawable及其子类的继承关系:


Drawable及其子类.png

最初看到这么多的子类,内心也是懵的,不过无妨,每种都在xml和java中用一遍就搞定,遇到感兴趣的子类还可以看一下它重写的方法,这也是我们以后自定义Drawable的基础!一个一个来!

1:BitmapDrawable

1.1:在xml中定义
//在Drawable文件夹中创建bitmap
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@mipmap/img_small"
    android:antialias="true"
    android:dither="true" 
    android:filter="true"
    android:tileMode="mirror"
    />
//在布局文件中引用
<View
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="8dp"
    android:background="@drawable/bg_tilemode_repeat"
    />
1.2:在Java代码中创建
View bitmap_java = findViewById(R.id.bitmap_java);
//用Java代码创建BitmapDrawable
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.img9);
BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap);
bitmapDrawable.setFilterBitmap(true);
bitmapDrawable.setAntiAlias(true);
bitmapDrawable.setDither(true);
//通过实验发现,tileMode和gravity属性同时设置,gravity属性失效  
bitmapDrawable.setTileModeXY(Shader.TileMode.MIRROR,Shader.TileMode.MIRROR);
//bitmapDrawable.setGravity(Gravity.CENTER);
bitmap_java.setBackgroundDrawable(bitmapDrawable);
1.3:运行截屏
BitmapDrawable.jpg
1.4:常用属性介绍

1:android:antialias="true"
是否抗锯齿,一般都设置为true;
2:android:dither="true"
是否允许抖动,一般都设置为true,可以使图片在低质量的屏幕上保持较好的显示效果,关于android:dither这篇文章写得很好:关于android布局的两个属性dither和tileMode;
3:android:filter="true"
是否开启滤波,当bitmap被缩放时候,filter可以保证bitmap实例外观的'平滑',一般都建议设置为true;
4:android:gravity
当bitmap实例比它所在的容器尺寸小的时候,gravity属性决定bitmap实例在哪个位置进行绘制
5:android:tileMode
决定bitmap的平铺模式(即bitmap实例尺寸小于其容器怎么填满容器),当设置了tileMode后,gravity属性失效,这一点在上面Java代码里面已经提及.

tileMode有以下四种可选值:
clamp:重复bitmap边缘的颜色;
disabled:不会重复绘制bitmap;
mirror:在水平和垂直方向按照镜像方式重复绘制bitmap;
repeat:保持bitmap原始方向在水平和垂直方向平铺;

2:ColorDrawable

ColorDrawable应该是最简单的Drawable,将指定颜色绘制到ColorDrawable实例所在的Canvas上.

2.1:在xml中定义
//在drawable文件夹下创建color
<?xml version="1.0" encoding="utf-8"?>
<color xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorAccent" />
//在布局文件中引用
<View
    android:layout_width="match_parent"
    android:layout_height="300px"
    android:background="@drawable/bg_color_drawable"
    />
2.2:在Java代码中创建
View v = findViewById(R.id.v);
ColorDrawable colorDrawable = new ColorDrawable(
getResources().getColor(R.color.colorPrimary));
v.setBackgroundDrawable(colorDrawable);
2.3:运行截屏
ColorDrawable.png

3:AnimationDrawable

3.1:在xml中定义,布局文件中引用后依然需要在Java代码中开启动画
//在drawable文件夹下创建animation-list
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/ii1"
        android:duration="@android:integer/config_shortAnimTime" />
    <item
        android:drawable="@mipmap/ii2"
        android:duration="@android:integer/config_shortAnimTime" />
    <item
        android:drawable="@mipmap/ii3"
        android:duration="@android:integer/config_shortAnimTime" />
    <item
        android:drawable="@mipmap/ii4"
        android:duration="@android:integer/config_shortAnimTime" />
    <item
        android:drawable="@mipmap/ii5"
        android:duration="@android:integer/config_shortAnimTime" />
</animation-list>
//在布局文件中引用
<View
    android:id="@+id/v1"
    android:layout_width="500px"
    android:layout_height="500px"
    android:background="@drawable/bg_animation_list"
    />
//在布局文件中引用后,依然需要在java代码中手动开启动画
View v1 = findViewById(R.id.v1);
//1:XML中指定的AnimationDrawable需要手动开启
((AnimationDrawable)v1.getBackground()).start();
3.2:在Java代码中创建
View v2 = findViewById(R.id.v2);
AnimationDrawable anim = new AnimationDrawable();
anim.setOneShot(false);
anim.addFrame(getResources().getDrawable(R.mipmap.ii1),200);
anim.addFrame(getResources().getDrawable(R.mipmap.ii2),200);
anim.addFrame(getResources().getDrawable(R.mipmap.ii3),200);
anim.addFrame(getResources().getDrawable(R.mipmap.ii4),200);
anim.addFrame(getResources().getDrawable(R.mipmap.ii5),200);
v2.setBackgroundDrawable(anim);
anim.start();
3.3:运行截屏
AnimationDrawable录屏.gif
3.4:注意

对于AnimationDrawable,即使是在drawable文件下定义的animation-list在布局文件xml中被引用,也需要在Java代码中手动开启才会执行动画!

4:StateListDrawable

StateListDrawable对应于xml中的selector,是开发中经常使用的Drawable子类,比如一个按钮正常状态和按下时候不同的样式就是用StateListDrawable实现的

4.1:在xml中定义
//在drawable文件夹下创建selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" 
android:visible="true" 
android:dither="true" 
android:constantSize="true" 
android:variablePadding="false" 
android:enterFadeDuration="1000" 
android:exitFadeDuration="1000">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="4dp"/>
            <stroke android:color="@color/colorAccent" android:width="1dp"/>
            <solid android:color="@color/colorPrimary"/>
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <corners android:radius="4dp"/>
            <stroke android:color="@color/colorPrimary" android:width="1dp"/>
            <solid android:color="@color/colorAccent"/>
        </shape>
    </item>
</selector>
//在布局文件中引用
<Button
    android:id="@+id/v1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="StateListDrawable"
    android:background="@drawable/bg_selector"
    />
4.2:在Java代码中创建
    /**
     * 根据正常状态和按压状态下 填充色,边框颜色及边框宽度,创建StateListDrawable实例
     * @param context
     * @param normalColor
     * @param normalStockColor
     * @param pressedColor
     * @param pressedStockColor
     * @param radius
     * @return
     */
    public static StateListDrawable gainStateListDrawable(Context context, 
        @ColorInt int normalColor, 
        @ColorInt int normalStockColor, 
        @ColorInt int pressedColor, 
        @ColorInt int pressedStockColor, 
        float radius){
        //创建StateListDrawable实例
        StateListDrawable drawable = new StateListDrawable();
        //创建按压 及 正常状态下 对应的状态数组
        int[] pressedState = new int[]{android.R.attr.state_pressed};
        int[] normalState = new int[]{};
        //创建按压 及 正常状态下 对应的Drawable实例
        GradientDrawable pressedDrawable = new GradientDrawable();
        pressedDrawable.setShape(GradientDrawable.RECTANGLE);
        pressedDrawable.setStroke(2,pressedStockColor);
        pressedDrawable.setCornerRadius(radius);
        pressedDrawable.setColor(pressedColor);
        GradientDrawable normalDrawable = new GradientDrawable();
        normalDrawable.setShape(GradientDrawable.RECTANGLE);
        normalDrawable.setStroke(2,normalStockColor);
        normalDrawable.setCornerRadius(radius);
        normalDrawable.setColor(normalColor);
        /**
         * 执行{@link StateListDrawable#addState(int[], Drawable)}
         * 设置不同状态下对应的Drawable实例
         */
        drawable.addState(pressedState,pressedDrawable);
        drawable.addState(normalState,normalDrawable);
        return drawable;
    }
    Button v2 = (Button) findViewById(R.id.v2);
    StateListDrawable stateListDrawable = DrawableUtils.gainStateListDrawable(
      this,
      getResources().getColor(R.color.colorAccent),
      getResources().getColor(R.color.colorPrimary),
      getResources().getColor(R.color.colorPrimary),
      getResources().getColor(R.color.colorAccent),8);
    /**
    * 设置新状态下将要进场的Drawable淡入耗时毫秒值
    * {@link StateListDrawable#setEnterFadeDuration(int)}
    * 设置旧状态下将要离场的Drawable淡出耗时毫秒值
    * {@link StateListDrawable#setExitFadeDuration(int)}
    */
    stateListDrawable.setEnterFadeDuration(500);
    stateListDrawable.setExitFadeDuration(500);
    v2.setBackgroundDrawable(stateListDrawable);
4.3:运行截屏

GIF中的淡入淡出效果并不是卡顿,下面4.4会介绍

StateListDrawable录屏.gif

4.4:常用属性介绍

1:android:constantSize="true"
设置StateListDrawable实例不同状态下展示的Drawable尺寸是否保存一致,如果为true,则所有状态下Drawable尺寸都一致,是所有Drawable实例中最大尺寸;如果为false,不同状态下Drawable的尺寸由其自身决定!
2:android:variablePadding="false"
设置StateListDrawable实例的padding值是否根据状态改变发生变化,默认是false;
3:android:enterFadeDuration="2000"
对应{@link StateListDrawable#setEnterFadeDuration(int)}方法,设置新状态下将要进场的Drawable淡入耗时毫秒值
4:android:exitFadeDuration="2000"
对应{@link StateListDrawable#setExitFadeDuration(int)}方法,设置旧状态下将要离场的Drawable淡出耗时毫秒值
3,4两个属性的设置就实现了GIF中状态改变时的淡入淡出效果

5:AnimatedStateListDrawable

AnimatedStateListDrawable是StateListDrawable 的子类,除了可以指定不同状态下对应的Drawable,还可以为状态的改变设置过渡效果!

5.1:在xml中定义
//在drawable文件夹下创建animated-selector
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--设置不同状态下展示的Drawable-->
    <item
        android:id="@+id/pressed"
        android:drawable="@drawable/bg_shape_accent"
        android:state_pressed="true" />
    <item
        android:id="@+id/normal"
        android:drawable="@drawable/bg_shape_primary" />
    <!--为状态变化导致的Drawable实例切换添加过渡效果/一个帧动画-->
    <transition
        android:fromId="@id/normal"
        android:reversible="true"
        android:toId="@id/pressed">
        <animation-list>
            <item
                android:drawable="@drawable/bg_shape_primary"
                android:duration="400"
                />
            <item
                android:drawable="@mipmap/ii1"
                android:duration="400" />
            <item
                android:drawable="@mipmap/ii2"
                android:duration="400" />
            <item
                android:drawable="@mipmap/ii3"
                android:duration="400" />
            <item
                android:drawable="@mipmap/ii4"
                android:duration="400" />
            <item
                android:drawable="@mipmap/ii5"
                android:duration="400" />
            <item
                android:drawable="@drawable/bg_shape_accent"
                android:duration="400"
                />
        </animation-list>
    </transition>
</animated-selector>
//在布局文件中引用
<View
    android:id="@+id/v1"
    android:layout_width="match_parent"
    android:layout_height="800px"
    android:background="@drawable/bg_animated_selector"
    android:clickable="true"
    />
5.2:在Java代码中创建暂未成功,哪位同学知道可以指教一下!

在Java代码中创建AnimatedStateListDrawable后,执行{@link AnimatedStateListDrawable#addState(int[], Drawable, int)}方法,int不知道如何赋值!

    /**
     * Add a new drawable to the set of keyframes.
     *
     * @param stateSet An array of resource IDs to associate with the keyframe
     * @param drawable The drawable to show when in the specified state, may not be null
     * @param id The unique identifier for the keyframe
     */
    public void addState(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) {
5.3:运行截屏
AnimatedStateListDrawable录屏.gif
5.4:transition中属性介绍

1:android:fromId="@id/normal"
指定当前过渡效果起始状态所对应的item的ID值.
如:<item android:id="@+id/normal"
2:android:toId="@id/pressed"
指定当前过渡效果终点状态所对应的item的ID值.
如:<item android:id="@+id/pressed"
3:android:reversible="true"
指定当前过渡效果是否可以反向执行.
如上面的例子中,按钮状态从普通变为按压,再从按压变为普通,会发现帧动画执行了2遍,执行顺序的相反的,从UI状态的逻辑上也更合理.

6:LevelListDrawable

LevelListDrawable对应level-list,也是一个Drawable实例的集合,其中每一个Drawable实例都有android:minLevel,android:maxLevel两个属性,当设置LevelListDrawable实例的level值,则会显示level在android:minLevel和android:maxLevel两个属性之间的Drawable实例.

6.1:在xml中定义
//在drawable文件夹下创建level-list 
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@mipmap/battery0"
        android:maxLevel="0"
        android:minLevel="0" />
    <item
        android:drawable="@mipmap/battery1"
        android:maxLevel="1"
        android:minLevel="1" />
    <item
        android:drawable="@mipmap/battery2"
        android:maxLevel="2"
        android:minLevel="2" />
    <item
        android:drawable="@mipmap/battery3"
        android:maxLevel="3"
        android:minLevel="3" />
</level-list>
//在布局文件中引用
<View
    android:id="@+id/v1"
    android:layout_width="match_parent"
    android:layout_height="400px"
    android:background="@drawable/bg_level_list"
    />
6.2:在Java代码中创建

对于LevelListDrawable实例,无论是不是在XML中定义并引用到布局文件中,要切换其展示Drawable实例,都需要在Java代码中调用
{@link LevelListDrawable#setLevel(int)}方法

public class LevelListDrawableActivity extends AppCompatActivity {
    private View v1;
    private View v2;
    LevelListDrawable drawable1;
    LevelListDrawable drawable2;
    private int currLevel = 0;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            drawable1.setLevel(currLevel);
            drawable2.setLevel(currLevel);
            currLevel = ++currLevel%3;
            handler.sendMessageDelayed(handler.obtainMessage(),200);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_level_list_drawable);
        v1 = findViewById(R.id.v1);
        v2 = findViewById(R.id.v2);
        drawable1 = (LevelListDrawable) v1.getBackground();
        drawable2 = new LevelListDrawable();
        drawable2.addLevel(0,0,getResources().getDrawable(R.mipmap.battery3));
        drawable2.addLevel(1,1,getResources().getDrawable(R.mipmap.battery2));
        drawable2.addLevel(2,2,getResources().getDrawable(R.mipmap.battery1));
        drawable2.addLevel(3,3,getResources().getDrawable(R.mipmap.battery0));
        v2.setBackgroundDrawable(drawable2);
        handler.obtainMessage().sendToTarget();
    }
}
6.3:运行截屏
LevelListDrawable录屏.gif
6.4:常用属性介绍

1:android:maxLevel="0"
该Drawable实例显示所允许的最大level值.
2:android:minLevel="0"
该Drawable实例显示所允许的最小level值.

7:ClipDrawable

准确来说,ClipDrawable是一个Drawable容器,其中只有一个Drawable实例,我们通过setLevel()方法设置其中的Drawable实例展示比例,level值范围为[0,10000],越大显示比例越高.level为0则完全不显示,level为10000则完全显示不裁剪.

7.1:在xml中定义
//在drawable文件夹下创建clip 
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="horizontal"
    android:drawable="@mipmap/img400267"
    android:gravity="left"/>

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="vertical"
    android:drawable="@mipmap/img400267"
    android:gravity="bottom"/>
//在布局文件中引用
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
    tools:context="com.jet.mydrawable.ClipDrawableActivity"
    android:fillViewport="true"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="8dp"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="xml:"
            android:layout_marginBottom="8dp"
            />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="310px"
            android:orientation="horizontal"
            >
            <View
                android:id="@+id/iv1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1.0"
                android:background="@drawable/bg_clip_horizontal"
                />
            <android.support.v4.widget.Space
                android:layout_width="8dp"
                android:layout_height="match_parent" />
            <View
                android:id="@+id/iv2"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1.0"
                android:background="@drawable/bg_clip_vertical"
                />
        </LinearLayout>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Java: android:clipOrientation=horizontal"
            android:layout_marginBottom="8dp"
            android:layout_marginTop="8dp"
            />
        <View
            android:id="@+id/v_j1"
            android:layout_width="800px"
            android:layout_height="530px"
            />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Java: android:clipOrientation=vertical"
            android:layout_marginBottom="8dp"
            android:layout_marginTop="8dp"
            />
        <View
            android:id="@+id/v_j2"
            android:layout_width="800px"
            android:layout_height="530px"
            />
    </LinearLayout>
</ScrollView>

7.2:在Java代码中创建

对于ClipDrawable,无论是在xml中声明后布局文件中引用,还是Java代码中创建,如果要调整其展示的比例,都需要在Java代码中调用{@link ClipDrawable#setLevel(int)}方法

public class ClipDrawableActivity extends AppCompatActivity {
    private int currLevel = 0;
    private View iv1;
    private View iv2;
    private ClipDrawable cp1;
    private ClipDrawable cp2;
    private Handler handlerXml = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            cp1.setLevel(currLevel);
            cp2.setLevel(currLevel);
            if (currLevel >= 10000) {
                currLevel = 0;
            } else {
                currLevel += 100;
            }
            handlerXml.sendEmptyMessageDelayed(1, 20);
        }
    };
    private View v_j1;
    private View v_j2;
    private ClipDrawable cp_horizontal;
    private ClipDrawable cp_vertical;
    private int[] gravities = new int[]{
            Gravity.LEFT,
            Gravity.TOP,
            Gravity.RIGHT,
            Gravity.BOTTOM,
            Gravity.CENTER,
            Gravity.CENTER_HORIZONTAL,
            Gravity.CENTER_VERTICAL,
            Gravity.CLIP_HORIZONTAL,
            Gravity.CLIP_VERTICAL,
            Gravity.FILL,
            Gravity.FILL_HORIZONTAL,
            Gravity.FILL_VERTICAL,
            Gravity.START,
            Gravity.END
    };
    private int index = 0;
    private int currLevelJava = 0;
    private Handler handlerJava = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(index == 0&&currLevelJava==0){
                Toast.makeText(ClipDrawableActivity.this,"开始试验不同的Gravity值!",Toast.LENGTH_LONG).show();
            }
            cp_horizontal.setLevel(currLevelJava);
            cp_vertical.setLevel(currLevelJava);
            if(index == gravities.length-1&&currLevelJava==10000){
                Toast.makeText(ClipDrawableActivity.this,"不同的Gravity值实验完毕!",Toast.LENGTH_LONG).show();
            }
            if (currLevelJava >= 10000) {
                currLevelJava = 0;
            } else {
                currLevelJava += 100;
            }
            if (currLevelJava == 0) {
                //说明一种Gravity属性已经展示完毕,应该换下一种Gravity属性了
                index = (++index) % gravities.length;
                cp_horizontal = new ClipDrawable(getResources().getDrawable(R.mipmap.img400267), gravities[index], ClipDrawable.HORIZONTAL);
                cp_vertical = new ClipDrawable(getResources().getDrawable(R.mipmap.img400267), gravities[index], ClipDrawable.VERTICAL);
                v_j1.setBackgroundDrawable(cp_horizontal);
                v_j2.setBackgroundDrawable(cp_vertical);
            }
            handlerJava.sendEmptyMessageDelayed(1, 20);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clip_drawable);
        //1:在布局文件中直接引用clip
        iv1 = findViewById(R.id.iv1);
        iv2 = findViewById(R.id.iv2);
        cp1 = (ClipDrawable) iv1.getBackground();
        cp2 = (ClipDrawable) iv2.getBackground();
        handlerXml.sendEmptyMessage(0);
        //2:在Java代码中创建ClipDrawable
        v_j1 = findViewById(R.id.v_j1);
        v_j2 = findViewById(R.id.v_j2);
        cp_horizontal = new ClipDrawable(getResources().getDrawable(R.mipmap.img400267), gravities[index], ClipDrawable.HORIZONTAL);
        cp_vertical = new ClipDrawable(getResources().getDrawable(R.mipmap.img400267), gravities[index], ClipDrawable.VERTICAL);
        v_j1.setBackgroundDrawable(cp_horizontal);
        v_j2.setBackgroundDrawable(cp_vertical);
        handlerJava.sendEmptyMessage(0);
    }
}
7.3:运行截屏
ClipDrawable在XML及Java中创建及试验不同Gravity属性的效果.gif
7.4:常用属性介绍

1:android:clipOrientation="vertical"
定义裁剪的方向,水平或者垂直,对应Java代码中创建ClipDrawable实例方法:public ClipDrawable(Drawable drawable, int gravity, int orientation) { 中的gravity;
2:android:gravity="bottom"
定义裁剪后保留区域位于ClipDrawable容器的方位,在上面Activity的实验GIF录屏中可见不同类型的Gravity的作用!
Google官方介绍截图如下,感觉还是用到时候实验一下比较方便!

ClipDrawable的Gravity属性介绍.png

8:InsetDrawable

InsetDrawable也是一个Drawable容器,其中仅包含一个Drawble实例,InsetDrawable通过设置inset属性值来定义插入的Drawable实例与自身四边的距离.当我们需要设置一个视觉尺寸小于View尺寸的背景,InsetDrawable就能派上用场了!

8.1:在xml中定义
//在drawable文件夹下创建inset
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@mipmap/img2"
    android:insetBottom="40dp"
    android:insetLeft="10dp"
    android:insetRight="30dp"
    android:insetTop="20dp"
    android:visible="true">
</inset>
//在布局文件中引用
<View
    android:layout_width="match_parent"
    android:layout_height="400px"
    android:background="@drawable/bg_inset"
    />
8.2:在Java代码中创建
//用Java代码创建InsetDrawable
v = findViewById(R.id.v);
float density = getResources().getDisplayMetrics().density;
insetDrawable = new InsetDrawable(
    getResources().getDrawable(R.mipmap.img2),
    (int)(density*10),(int)(density*20),
    (int)(density*30),(int)(density*40));
v.setBackgroundDrawable(insetDrawable);
8.3:运行截屏

InsetDrawable截屏.jpg

通过xml或Java创建InsetDrawable实例时指定的间距:
InsetDrawable属性效果.png

8.4:常用属性介绍

1:android:insetLeft="20dp"
InsetDrawable内部插入的Drawable实例与其左边缘的距离
2:android:insetTop="20dp"
InsetDrawable内部插入的Drawable实例与其顶部的距离
3:android:insetRight="20dp"
InsetDrawable内部插入的Drawable实例与其右边缘的距离
4:android:insetBottom="20dp"
InsetDrawable内部插入的Drawable实例与其底部的距离
5:android:inset="20dp"
InsetDrawable内部插入的Drawable实例与其四边的距离

9:RotateDrawable

9.1:在xml中定义
//在drawable文件夹下创建
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@mipmap/rotate_round"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360"
    android:visible="true">
</rotate>
//在布局文件中引用
<View
    android:id="@+id/v1"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@drawable/bg_rotate"
    />
9.2:在Java代码中创建
public class RotateDrawableActivity extends AppCompatActivity {
    ****
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(run){
                r1.setLevel(currLevel);
                r2.setLevel(currLevel);
                if(currLevel>=10000){
                    currLevel = 0;
                }else{
                    currLevel += 10;
                }
                handler.sendEmptyMessageDelayed(0,10);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ****
        v1 = findViewById(R.id.v1);
        r1 = (RotateDrawable) v1.getBackground();
        //通过Java代码创建RotateDrawable
        v2 = findViewById(R.id.v2);
        r2 = new RotateDrawable();
        //相当于xml中:android:drawable="@mipmap/rotate_round"        
        r2.setDrawable(getResources().getDrawable(R.mipmap.rotate_round));
        //相当于xml中:android:pivotX="50%" android:pivotY="50%"        
        r2.setPivotX(0.5f);
        r2.setPivotY(0.5f);
        //相当于xml中:android:fromDegrees="0" android:toDegrees="360"
        r2.setFromDegrees(0.0f);
        r2.setToDegrees(-360.0f);
        r2.setLevel(0);
        v2.setBackgroundDrawable(r2);
        handler.obtainMessage().sendToTarget();
    }
    ****
}
9.3:运行截屏
RotateDrawable录屏.gif
9.4:常用属性介绍

1:android:fromDegrees="0"
RotateDrawable实例起始角度,大于0是顺时针旋转,小于0是逆时针旋转;
2:android:toDegrees="360"
RotateDrawable实例最终角度,大于0是顺时针旋转,小于0是逆时针旋转;
3:android:pivotX="50%"
RotateDrawable实例旋转中心点X轴坐标相对自身位置;
4:android:pivotY="50%"
RotateDrawable实例旋转中心点Y轴坐标相对自身位置;

10:ScaleDrawable

是一个Drawable容器,通过设置level值来改变它包含的Drawable实例的宽高缩放值.level的值是[0,10000],0代表完全不可见,10000代表不缩放保持原始尺寸.

10.1:在xml中定义
//在drawable文件夹下创建scale
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@mipmap/img800534"
    android:level="1"
    android:scaleGravity="center"
    android:scaleHeight="30%"
    android:scaleWidth="30%">
</scale>
//在布局文件中引用
<View
    android:layout_width="400px"
    android:layout_height="400px"
    android:layout_marginTop="8dp"
    android:background="@drawable/bg_scale" />
10.2:在Java代码中创建
public class ScaleDrawableActivity extends AppCompatActivity {
    private View v1;
    private View v2;
    private ScaleDrawable scaleDrawable1;
    private ScaleDrawable scaleDrawable2;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int level = scaleDrawable2.getLevel();
            if (level + 100 >= 10000) {
                scaleDrawable2.setLevel(0);
            } else {
                scaleDrawable2.setLevel(level + 100);
            }
            handler.sendEmptyMessageDelayed(0, 20);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scale_drawable);
        v1 = findViewById(R.id.v1);
        v2 = findViewById(R.id.v2);
        scaleDrawable1 = new ScaleDrawable(
            getResources().getDrawable(R.mipmap.img800534), 
            Gravity.CENTER, 0.4f, 0.4f);
        scaleDrawable2 = new ScaleDrawable(
            getResources().getDrawable(R.mipmap.img800534), 
            Gravity.CENTER, 0.4f, 0.4f);
        scaleDrawable1.setLevel(1);
        scaleDrawable2.setLevel(1);
        v1.setBackgroundDrawable(scaleDrawable1);
        v2.setBackgroundDrawable(scaleDrawable2);
        handler.obtainMessage().sendToTarget();
    }
}
10.3:运行截屏
ScaleDrawable录屏.gif
10.4:常用属性介绍

通过上面Java代码及GIF可见,即使设置了宽高缩放比例,继续不断调整level值,图片也会从不可见逐渐变为原始尺寸

11:DrawerArrowDrawable

A drawable that can draw a "Drawer hamburger" menu or an arrow and animate between them.The progress between the two states is controlled via[setProgress(float)]
这是Google官方介绍,通俗点讲,DrawerArrowDrawable是一个可以绘制三条横线或者箭头,并且在两者之间添加过渡动画的Drawable实例.通过调用{@link DrawerArrowDrawable#setProgress(float)}方法来设置横线到箭头的进度值.

11.1:在xml中无法创建,只能通过Java代码创建
11.2:在Java代码中创建
public class DrawerArrowDrawableActivity extends AppCompatActivity {
    View v;
    DrawerArrowDrawable drawerArrowDrawable;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            float progress = drawerArrowDrawable.getProgress();
            if (progress + 0.01f <= 1.00f) {
                progress += 0.01f;
            } else {
                progress = 0.00f;
            }
            drawerArrowDrawable.setProgress(progress);
            if (progress > 0.99f || progress <= 0.00f) {
                //动画已结完整展示了一遍,停一会儿再重复
                handler.sendEmptyMessageDelayed(0, 1000);
                Toast.makeText(DrawerArrowDrawableActivity.this, 
                ""+progress, Toast.LENGTH_SHORT).show();
            } else {
                handler.sendEmptyMessageDelayed(0, 40);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drawer_arrow_drawable);
        v = findViewById(R.id.v);
        //在Java中创建DrawerArrowDrawable实例
        drawerArrowDrawable = new DrawerArrowDrawable(this);
        //设置DrawerArrowDrawable实例的箭头指向
        drawerArrowDrawable.setDirection(
            DrawerArrowDrawable.ARROW_DIRECTION_LEFT);
        //设置DrawerArrowDrawable实例绘制的横线和箭头的颜色
        drawerArrowDrawable.setColor(Color.RED);
        //设置DrawerArrowDrawable实例的箭头两边斜线长度
        drawerArrowDrawable.setArrowHeadLength(160);
        //设置DrawerArrowDrawable实例的箭头中轴长度
        drawerArrowDrawable.setArrowShaftLength(320);
        //设置DrawerArrowDrawable实例的三条横线的长度
        drawerArrowDrawable.setBarLength(320);
        //设置DrawerArrowDrawable实例的三条横线的宽度
        drawerArrowDrawable.setBarThickness(64);
        //设置DrawerArrowDrawable实例的三条横线间的间隔
        drawerArrowDrawable.setGapSize(48);
        //设置DrawerArrowDrawable实例状态发生变化过程中(由横线到箭头的过程),
        //绘制的线条是否发生旋转
        drawerArrowDrawable.setSpinEnabled(true);
        //设置DrawerArrowDrawable实例状态改变的进度(0:横线 1:箭头)
        drawerArrowDrawable.setProgress(0.00f);
        v.setBackgroundDrawable(drawerArrowDrawable);
        handler.obtainMessage().sendToTarget();
    }
}
11.3:运行截屏
DrawerArrowDrawable录屏.gif
11.4:常用方法

见上面Java代码:
可以设置箭头方法,箭头的斜线和中轴长度,横线的长度,宽度和间距,通过不断设置progress不断调整状态改变的进度!

12:GradientDrawable

GradientDrawable代表颜色的渐变区域.在XML中是定义在shape内部的gradient,可以设置其颜色渐变的三种类型:
线性 android:type="linear"
放射 android:type="radial"
平铺 android:type="sweep"

12.1:在xml中定义
//在drawable文件夹下创建
//线性渐变
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle">
    <gradient
        android:angle="0"
        android:centerColor="@android:color/holo_green_light"
        android:centerX="0.5"
        android:centerY="0.5"
        android:endColor="@color/colorPrimaryDark"
        android:startColor="@color/colorAccent"
        android:type="linear"
        android:gradientRadius="40dp"
        />
</shape>
//放射渐变
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle">
    <gradient
        android:angle="0"
        android:centerColor="@android:color/holo_green_light"
        android:centerX="0.5"
        android:centerY="0.5"
        android:endColor="@color/colorPrimaryDark"
        android:startColor="@color/colorAccent"
        android:type="radial"
        android:gradientRadius="70dp"
        />
</shape>
//平铺渐变
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle">
    <gradient
        android:angle="0"
        android:centerColor="@android:color/holo_green_light"
        android:centerX="0.5"
        android:centerY="0.5"
        android:endColor="@color/colorPrimaryDark"
        android:startColor="@color/colorAccent"
        android:type="sweep"
        />
</shape>
//在布局文件中引用
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:fillViewport="true"
    tools:context="com.jet.mydrawable.GradientDrawableActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="8dp">

        <View
            android:layout_width="match_parent"
            android:layout_height="160dp"
            android:layout_marginTop="8dp"
            android:background="@drawable/bg_shape_gradient_linear" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="160dp"
            android:layout_marginTop="8dp"
            android:orientation="horizontal">

            <View
                android:layout_width="0dp"
                android:layout_height="160dp"
                android:layout_weight="1.0"
                android:background="@drawable/bg_shape_gradient_radial" />

            <android.support.v4.widget.Space
                android:layout_width="8dp"
                android:layout_height="match_parent" />

            <View
                android:layout_width="0dp"
                android:layout_height="160dp"
                android:layout_weight="1.0"
                android:background="@drawable/bg_shape_gradient_radial_noendcolor" />
        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="160dp"
            android:layout_marginTop="8dp"
            android:background="@drawable/bg_shape_gradient_sweep" />
    </LinearLayout>
</ScrollView>
12.2:在Java代码中创建
v = findViewById(R.id.v);
gradientDrawable = new GradientDrawable(
    GradientDrawable.Orientation.TR_BL,
    new int[]{Color.RED,Color.YELLOW,Color.GREEN}
);
gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
v.setBackgroundDrawable(gradientDrawable);
12.3:运行截屏
GradientDrawable截屏.jpg

13:LayerDrawable

LayerDrawable是一个管理Drawable列表的Drawable子类,列表中每一个Drawable实例都按照其在列表中的顺序进行绘制,最后一个Drawable实例绘制于顶部.LayerDrawable对应着xml中的layer-list,其中每一个Drawable对应着一个item.

13.1:在xml中定义
//在drawable文件夹下创建layer-list
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
            android:tint="@android:color/holo_red_light" />
    </item>
    <item
        android:left="10dp"
        android:top="10dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
            android:tint="@android:color/holo_green_light" />
    </item>
    <item
        android:left="20dp"
        android:top="20dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
            android:tint="@android:color/holo_blue_light" />
    </item>
</layer-list>
//在布局文件中引用
<View
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_marginBottom="8dp"
    android:background="@drawable/bg_layer_list"
    />
13.2:在Java代码中创建
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layer_drawable);
        //经过实验发现:调用LayerDrawable中Drawable子实例的setBounds,并不能改变LayerDrawable中绘制的Drawable实例的尺寸
        v = findViewById(R.id.v);
        Drawable drawable0 = DrawableCompat.wrap(getResources().getDrawable(R.mipmap.ic_launcher)).mutate();
//        drawable0.setBounds(0,0,200,200);
        drawable0.setTint(Color.RED);
        Drawable drawable1 = DrawableCompat.wrap(getResources().getDrawable(R.mipmap.ic_launcher)).mutate();
//        drawable1.setBounds(0,0,200,200);
        drawable1.setTint(Color.GREEN);
        Drawable drawable2 = DrawableCompat.wrap(getResources().getDrawable(R.mipmap.ic_launcher)).mutate();
//        drawable2.setBounds(0,0,200,200);
        drawable2.setTint(Color.BLUE);
        Drawable[] drawables = new Drawable[3];
        drawables[0] = drawable0;
        drawables[1] = drawable1;
        drawables[2] = drawable2;
        layerDrawable = new LayerDrawable(drawables);
        layerDrawable.setLayerInset(0,0,0,0,0);
        layerDrawable.setLayerInset(1,80,80,0,0);
        layerDrawable.setLayerInset(2,160,160,0,0);
        layerDrawable.setLayerGravity(0,Gravity.TOP);
        layerDrawable.setLayerGravity(1,Gravity.TOP);
        layerDrawable.setLayerGravity(2,Gravity.TOP);
        //需要直接调用LayerDrawable实例的setLayerSize来改变其中Drawable子实例的尺寸
        layerDrawable.setLayerSize(0,100,100);
        layerDrawable.setLayerSize(1,200,200);
        layerDrawable.setLayerSize(2,300,300);
        v.setBackgroundDrawable(layerDrawable);
    }
13.3:运行截屏
LayerDrable截屏.png
13.4:LayerDrawable注意事项

直接在Java代码中创建LayerDrawable,想要改变其中Drawable子实例的尺寸,需要调用LayerDrawable.setLayerSize,直接调用其中Drawable子实例的setBounds方法不会起作用!!

其他的几个方法:setLayerInset,setLayerGravity,setLayerSize则设置了LayerDrawable中Drawable子实例与容器的间距,方位及尺寸!

14:NinePatchDrawable

NinePatchDrawable是一种 PNG 图像,在其中可定义当视图中的内容超出正常图像边界时 Android 缩放的可拉伸区域。此类图像通常指定为至少有一个尺寸设置为 "wrap_content" 的视图的背景,而且当视图扩展以适应内容时,九宫格图像也会扩展以匹配视图的大小.
最常用的地方就是聊天气泡!

14.1:在xml中定义
//在drawable文件夹下创建nine-patch
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:src="@drawable/bubble" />
//在布局文件中引用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    ****
    android:padding="8dp"
    android:orientation="vertical"
    >
    <EditText
        android:padding="32dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_nine_patch"
        />
    <EditText
        android:padding="32dp"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_nine_patch"
        />
</LinearLayout>
14.2:注意
  • 不建议在Java代码中创建NinePatchDrawable
  • xml中nine-patch下面的src属性设计的.9图,必须放在drawable文件夹下面,放在mipmap文件夹下识别不了
14.3:运行截屏
9图.png
14.4:刚刚搜到一个在线制作.9.png的网站

AndroidAssetStudio

15:RoundedBitmapDrawable

RoundedBitmapDrawable是一个可以绘制圆角或者直接绘制为圆形的Drawable子类.

15.1:无法在xml中定义
15.2:在Java代码中创建
public class RoundedBitmapDrawableActivity extends AppCompatActivity {
    private View v1;
    private RoundedBitmapDrawable roundedBitmapDrawable;
    private View v2;
    private RoundedBitmapDrawable roundedBitmapDrawable2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rounded_bitmap_drawable);
        //通过Java代码创建RoundedBitmapDrawable
        v1 = findViewById(R.id.v1);
        roundedBitmapDrawable = 
                RoundedBitmapDrawableFactory.create(
                        getResources(), 
                        BitmapFactory.decodeResource(getResources(), R.mipmap.ii2)
                );
        roundedBitmapDrawable.setAntiAlias(true);
        //1:设置Bitmap实例是否是圆形
        roundedBitmapDrawable.setCircular(true);
        v1.setBackgroundDrawable(roundedBitmapDrawable);
        v2 = findViewById(R.id.v2);
        //通过Java代码创建RoundedBitmapDrawable
        roundedBitmapDrawable2 = 
                RoundedBitmapDrawableFactory.create(
                        getResources(), 
                        BitmapFactory.decodeResource(getResources(), R.mipmap.ii3));
        roundedBitmapDrawable2.setAntiAlias(true);
        roundedBitmapDrawable2.setCornerRadius(64.0f);
        v2.setBackgroundDrawable(roundedBitmapDrawable2);
    }
}
15.3:运行截屏
RoundedBitmapDrawable.png
15.4:注意事项
  • RoundedBitmapDrawable只能在Java代码中创建使用;
  • 通过调用setCornerRadius或者setCircular来制定RoundedBitmapDrawable实例的圆角或者设置为圆形.
15.5:常用工具方法
    /**
     * 创建RoundedBitmapDrawable实例
     *
     * @param bitmapOri    原始Bitmap实例
     * @param circular     是否是圆形
     * @param strokeWidth  边框宽度
     * @param cornerRadius 圆角半径值
     * @return
     */
    public static RoundedBitmapDrawable gainRoundedBitmapDrawableWithStroke(
            Resources resources,
            Bitmap bitmapOri,
            boolean circular,
            @ColorInt int strokeColor,
            int strokeWidth,
            float cornerRadius) {
        //注意,要先执行Bitmap.copy(Config config, boolean isMutable),
        //因为下面代码 Canvas(bitmap)需要保证传入的Bitmap实例必须是mutable
        Bitmap bitmap = bitmapOri.copy(Bitmap.Config.ARGB_8888, true);
        //Construct a canvas with the specified bitmap to draw into. 
        //The bitmap must be mutable.
        Canvas canvas = new Canvas(bitmap);
        Paint borderPaint = new Paint();
        borderPaint.setAntiAlias(true);
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setStrokeWidth(strokeWidth);
        borderPaint.setColor(strokeColor);
        //
        RoundedBitmapDrawable roundedBitmapDrawable = 
                RoundedBitmapDrawableFactory.create(resources, bitmap);
        if (circular) {
            canvas.drawCircle(
                    roundedBitmapDrawable.getIntrinsicWidth() / 2, 
                    roundedBitmapDrawable.getIntrinsicWidth() / 2, 
                    roundedBitmapDrawable.getIntrinsicWidth() / 2, 
                    borderPaint);
            //设置RoundedBitmapDrawable实例绘制为圆形
            roundedBitmapDrawable.setCircular(true);
        } else {
            //设置RoundedBitmapDrawable实例的圆角值
            RectF rectF = new RectF();
            rectF.set(0, 0, 
                    roundedBitmapDrawable.getIntrinsicWidth(), 
                    roundedBitmapDrawable.getIntrinsicHeight());
            canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, borderPaint);
            roundedBitmapDrawable.setCornerRadius(cornerRadius);
        }
        return roundedBitmapDrawable;
    }

在Java代码中调用及截屏:

        //使用工具类创建带边框的圆形RoundedBitmapDrawable实例
        v3 = findViewById(R.id.v3);
        roundedBitmapDrawable3 = DrawableUtils.gainRoundedBitmapDrawableWithStroke(
            getResources(),
            BitmapFactory.decodeResource(getResources(), R.mipmap.ii4),
            true, 
            Color.RED,
            8,
            32.0f);
        v3.setBackgroundDrawable(roundedBitmapDrawable3);
        //使用工具类创建带边框的RoundedBitmapDrawable实例
        v4 = findViewById(R.id.v4);
        roundedBitmapDrawable4 = DrawableUtils.gainRoundedBitmapDrawableWithStroke(
            getResources(),
            BitmapFactory.decodeResource(getResources(), R.mipmap.ii1),
            false, 
            Color.GREEN,
            32,
            32.0f);
        v4.setBackgroundDrawable(roundedBitmapDrawable4);

使用工具类创建带边框的RoundedBitmapDrawable实例 截屏

RoundedBitmapDrawable带边框.jpg

16:TransitionDrawable

TransitionDrawable是可在两种可绘制对象资源之间交错淡出的可绘制对象.每个可绘制对象由单一<item>元素表示。不支持超过两个项目。要向前转换,请调用startTransition(),要向后转换,则调用reverseTransition()

16.1:在xml中定义
//在drawable文件夹下创建transition
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/ii3"/>
    <item android:drawable="@mipmap/ii4"/>
</transition>
//在布局文件中引用
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:text="xml:"
    />
<View
    android:id="@+id/v1"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_marginBottom="8dp"
    android:background="@drawable/bg_transition"
    />
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:text="Java:"
    />
<View
    android:id="@+id/v2"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_marginBottom="8dp"
    />
16.2:在Java代码中创建

对于TransitionDrawable,都必须在Java代码中手动调用startTransition才能展现出淡入淡出动画效果

    ****
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            transitionDrawable1.resetTransition();
            transitionDrawable2.resetTransition();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition_drawable);
        v1 = findViewById(R.id.v1);
        v2 = findViewById(R.id.v2);
        transitionDrawable1 = (TransitionDrawable) v1.getBackground();
        transitionDrawable2 = new TransitionDrawable(
            new Drawable[]{
                getResources().getDrawable(R.mipmap.ii1),
                getResources().getDrawable(R.mipmap.ii2)
            });
        v2.setBackgroundDrawable(transitionDrawable2);
        //
        transitionDrawable1.startTransition(4000);
        transitionDrawable2.startTransition(4000);
        handler.sendEmptyMessageDelayed(0,6000);
    }
    ****
16.3:运行截屏
TransitionDrawable录屏.gif
16.4:TransitionDrawable要点
  • TransitionDrawable是LayerDrawable的子类,所以同样支持在XML或者在Java代码中设置间距
  • TransitionDrawable必须在Java代码中手动调用startTransition(int durationMillis)来开启动画

17:RippleDrawable

17.1:在xml中定义
//在drawable文件夹下创建ripple
//1:在ripple下添加id为@android:id/mask的Drawable子项,作为涟漪动画的遮罩(展示范围)
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorAccent">
    <item
        android:id="@android:id/mask"
        android:drawable="@android:color/holo_green_light" />
</ripple>
//2:在ripple下并未设置@android:id/mask的Drawable实例作为涟漪动画的遮罩(展示范围),则涟漪动画的展示范围是其所在控件尺寸
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorAccent">
    <item
        android:drawable="@android:color/holo_green_light" />
</ripple>
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorAccent">
    <item
        android:drawable="@mipmap/ii1" />
</ripple>
//3:在ripple下并无其他Drawable子项,则涟漪动画的展示范围可能会超出RippleDrawable实例的范围
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorAccent"/>
//在布局文件中引用
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
    tools:context="com.jet.mydrawable.LevelListDrawableActivity"
    android:fillViewport="true"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="8dp"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:text="xml:"
            />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Xml 1"
            android:background="@drawable/bg_ripple_child_mask"
            />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Xml 2"
            android:background="@drawable/bg_ripple_child_color"
            />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Xml 3"
            android:background="@drawable/bg_ripple_child_drawable"
            />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Xml 4"
            android:background="@drawable/bg_ripple_nochild"
            />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:text="Java:"
            />
        <Button
            android:id="@+id/v1"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Java 1"
            />
        <Button
            android:id="@+id/v2"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_marginBottom="8dp"
            android:text="RippleDrawable Java 2"
            />
    </LinearLayout>
</ScrollView>
17.2:在Java代码中创建
//Java代码创建RippleDrawable
v1 = (Button) findViewById(R.id.v1);
v2 = (Button) findViewById(R.id.v2);
ColorStateList colorStateList = ColorStateList.valueOf(getResources().getColor(R.color.colorAccent));
Drawable content = getResources().getDrawable(R.mipmap.battery0);
ShapeDrawable mask1 = new ShapeDrawable();
mask1.setShape(new OvalShape());
ShapeDrawable mask2 = new ShapeDrawable();
mask2.setShape(new RectShape());
InsetDrawable insetDrawable = new InsetDrawable(mask2,48);
RippleDrawable rippleDrawable1 = new RippleDrawable(colorStateList,content,mask1);
RippleDrawable rippleDrawable2 = new RippleDrawable(colorStateList,content,insetDrawable);
v1.setBackgroundDrawable(rippleDrawable1);
v2.setBackgroundDrawable(rippleDrawable2);
17.3:运行截屏
RippleDrawable录屏.gif
17.4:属性介绍

以Java中RippleDrawable构造函数为例:

    /**
     * Creates a new ripple drawable with the specified ripple color and
     * optional content and mask drawables.
     * //涟漪动画的颜色
     * @param color The ripple color 
     * //涟漪动画所在背景Drawable实例,在mask缺失情况下可以限定涟漪动画的展示范围
     * @param content The content drawable, may be {@code null}
     * //涟漪动画展示范围与mask(遮罩)相同
     * @param mask The mask drawable, may be {@code null}
     */
    public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content,
            @Nullable Drawable mask) {
        ****
    }

关于RippleDrawable涟漪动画展示范围的详情情况,下面一篇文章写的很好:
Android L Ripple的使用

17.5:注

现在的手机系统大部分都5.0以上了,会发现很多手机原生Button点击效果比RippleDrawable还要丰富,单纯靠RippleDrawable还无法做到这样的效果,后续会介绍实现方法!

关于文章涉及到的代码,在用法总结完毕后,会在后续文章上传!

推荐阅读更多精彩内容