ShapeDrawable和GradientDrawable的简单使用

本篇博客是介绍Drawable的第十篇博客,跟以往的几篇不同的是,本篇博客一次介绍两个Drawable,之所以这么做,是因为这两个Drawable在xml中使用的标签都是shape。有那么一点点共通的地方。(形状都是通过根标签shape来指定的)

我们还是先看一下shapeDrawable吧!
还是老规矩,先看一下shapedrawable的效果图吧!
椭圆形
圆角方形

主布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.shapeandgradientdrawable.GradientActivity">

    <TextView
        android:id="@+id/tv"
        android:gravity="center"
        android:background="@drawable/shape_drawable"
        android:layout_centerInParent="true"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:text="Hello World!" />
    <Button
        android:visibility="gone"
        android:layout_below="@id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/start_animation"
        android:onClick="startDrawableAnimation"/>
</RelativeLayout>

shape_drawable文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle"
>

<size
        android:width="1dp"
        android:height="1dp" />
    <solid android:color="@color/colorAccent" />

    <corners android:radius="5dp" />

    <stroke
        android:width="2dp"
        android:color="@color/colorPrimaryDark"
        android:dashGap="3dp"
        android:dashWidth="3dp" />

</shape>

java文件:

package com.example.shapeandgradientdrawable;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;
import android.os.Bundle;
import android.widget.TextView;

/**
 * Created by Administrator on 2017/3/18 0018.
 */

public class ShapeActivity extends Activity {

    private TextView tv;
    ShapeDrawable drawable;
    private int index = 0;
    private boolean isTimerRunning = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.shape_layout);
        tv = (TextView)findViewById(R.id.tv);
    }

}

这里有一个很有意思的现象,通过tv获取背景的drawable是一个gradientdrawable对象而不是一个shapedrawable对象,我不知道为什么会出现这种情况,源码中也没有看出什么东西!!而且在shape的跟标签中有一个useLevel的属性,而在shapeDrawable中根本就没有代码解析这个useLevel这个属性,相反GradientDrawable对这个属性进行的解析,具体的等到了说GradientDrawable的时候在详细说明。

在shape的跟标签下可以使用size 、padding、solid、corners、stroke和gradient几个子标签,其中size用来指定这个drawable的大小的,如果这个drawable是用来当做背景的,那么宽和高设置为1即可,它会和layout中的UI宽高进行对比,取其中的最大值,如果是作为ImageView中src属性的值,那么显示出来的宽高将与size中指定的宽高一致,有兴趣的可以自己试一下。

solid标签中只有一个属性,那就是设置形状内部的填充颜色。

corners这个标签不是在任何情况下都会起作用的,它收到根标签shape中形状属性值的影响,目前我测试的是只有在shape的为rectangle的时候才起作用。

stroke标签中的属性用来设置边界的宽度,颜色,是否是虚线等。

padding的作用与View中padding中作用一样,这里就不做太多的介绍了。

至于Gradient我们还是在GradientDrawable中说吧!!!

现在开始介绍GradientDrawable,主要就是介绍gradient标签。

还是先看一下效果吧!

静态:
无Level影响
无Level影响
有Level影响
有Level影响
在来一个gif图:

Orientation改变

主布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.shapeandgradientdrawable.GradientActivity">

    <TextView
        android:id="@+id/tv"
        android:gravity="center"
        android:background="@drawable/gradient_drawable"
        android:layout_centerInParent="true"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:text="Hello World!" />
    <Button
        android:layout_below="@id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/start_animation"
        android:onClick="startDrawableAnimation"/>
</RelativeLayout>

drawable文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:innerRadius="5dp"
    android:shape="rectangle"
    android:useLevel="true">
    <size android:height="1dp"
        android:width="1dp"
        />
    <solid android:color="@color/colorAccent"/>
    <corners android:radius="5dp"/>
    <stroke android:color="@color/colorPrimaryDark"
        android:dashGap="3dp"
        android:dashWidth="3dp"
        android:width="2dp"/>
    <gradient android:angle="45"
        android:centerColor="@color/colorAccent"
        android:useLevel="true"
        android:type="linear"/>
</shape>

Java文件:

package com.example.shapeandgradientdrawable;

import android.graphics.drawable.GradientDrawable;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class GradientActivity extends AppCompatActivity {

    private TextView tv;
    GradientDrawable drawable;
    private int index = 0;
    GradientDrawable.Orientation[] orientations;
    private boolean isTimerRunning = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gradient_layout);
        tv = (TextView)findViewById(R.id.tv);
        drawable = (GradientDrawable) tv.getBackground();
        drawable.setLevel(10000);
        orientations = GradientDrawable.Orientation.values();
        Log.i("zyq","orientation = "+orientations.length);
    }

    public void startDrawableAnimation(View view){
        if(isTimerRunning){
            timer.cancel();
            timer.onFinish();
        }else{
            if (drawable instanceof GradientDrawable){
                timer.start();
            }
        }
    }

    private CountDownTimer timer = new CountDownTimer(Integer.MAX_VALUE,20) {
        @Override
        public void onTick(long millisUntilFinished) {
            isTimerRunning = true;
            if (index<8){
                drawable.setOrientation(orientations[index]);
                index ++;
            }else{
                drawable.setOrientation(orientations[0]);
                index = 1;
            }

        }

        @Override
        public void onFinish() {
            isTimerRunning = false;
        }
    };
}

在gradient标签中有一个uselevel的属性,如果这个属性值指定为true,那么drawable的level值就会影响到drawable中发生变化的起始位置,通过上面的静态图对比就可以发现其中的不同了!

属性angle的值必须是45的倍数,不然会抛出xml解析异常,具体的可以通过查看源码来分析:

        if (st.mGradient == LINEAR_GRADIENT) {
            int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
            angle %= 360;

            if (angle % 45 != 0) {
                throw new XmlPullParserException(a.getPositionDescription()
                        + "<gradient> tag requires 'angle' attribute to "
                        + "be a multiple of 45");
            }

            st.mAngle = angle;

            switch (angle) {
                case 0:
                    st.mOrientation = Orientation.LEFT_RIGHT;
                    break;
                case 45:
                    st.mOrientation = Orientation.BL_TR;
                    break;
                case 90:
                    st.mOrientation = Orientation.BOTTOM_TOP;
                    break;
                case 135:
                    st.mOrientation = Orientation.BR_TL;
                    break;
                case 180:
                    st.mOrientation = Orientation.RIGHT_LEFT;
                    break;
                case 225:
                    st.mOrientation = Orientation.TR_BL;
                    break;
                case 270:
                    st.mOrientation = Orientation.TOP_BOTTOM;
                    break;
                case 315:
                    st.mOrientation = Orientation.TL_BR;
                    break;
            }
        } 

从这段代码就可以看出,不是任何时候都会对angle进行解析的,只有当梯度变化为线性梯度时,才会对angle进行解析。

if (st.mGradient == LINEAR_GRADIENT) {
                    final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
                    switch (st.mOrientation) {
                    case TOP_BOTTOM:
                        x0 = r.left;            y0 = r.top;
                        x1 = x0;                y1 = level * r.bottom;
                        break;
                    case TR_BL:
                        x0 = r.right;           y0 = r.top;
                        x1 = level * r.left;    y1 = level * r.bottom;
                        break;
                    case RIGHT_LEFT:
                        x0 = r.right;           y0 = r.top;
                        x1 = level * r.left;    y1 = y0;
                        break;
                    case BR_TL:
                        x0 = r.right;           y0 = r.bottom;
                        x1 = level * r.left;    y1 = level * r.top;
                        break;
                    case BOTTOM_TOP:
                        x0 = r.left;            y0 = r.bottom;
                        x1 = x0;                y1 = level * r.top;
                        break;
                    case BL_TR:
                        x0 = r.left;            y0 = r.bottom;
                        x1 = level * r.right;   y1 = level * r.top;
                        break;
                    case LEFT_RIGHT:
                        x0 = r.left;            y0 = r.top;
                        x1 = level * r.right;   y1 = y0;
                        break;
                    default:/* TL_BR */
                        x0 = r.left;            y0 = r.top;
                        x1 = level * r.right;   y1 = level * r.bottom;
                        break;
                    }

                    mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
                            gradientColors, st.mPositions, Shader.TileMode.CLAMP));
                } else if (st.mGradient == RADIAL_GRADIENT) {
                    x0 = r.left + (r.right - r.left) * st.mCenterX;
                    y0 = r.top + (r.bottom - r.top) * st.mCenterY;

                    float radius = st.mGradientRadius;
                    if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
                        // Fall back to parent width or height if intrinsic
                        // size is not specified.
                        final float width = st.mWidth >= 0 ? st.mWidth : r.width();
                        final float height = st.mHeight >= 0 ? st.mHeight : r.height();
                        radius *= Math.min(width, height);
                    } else if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION_PARENT) {
                        radius *= Math.min(r.width(), r.height());
                    }

                    if (st.mUseLevel) {
                        radius *= getLevel() / 10000.0f;
                    }

                    mGradientRadius = radius;

                    if (radius <= 0) {
                        // We can't have a shader with non-positive radius, so
                        // let's have a very, very small radius.
                        radius = 0.001f;
                    }

                    mFillPaint.setShader(new RadialGradient(
                            x0, y0, radius, gradientColors, null, Shader.TileMode.CLAMP));
                } else if (st.mGradient == SWEEP_GRADIENT) {
                    x0 = r.left + (r.right - r.left) * st.mCenterX;
                    y0 = r.top + (r.bottom - r.top) * st.mCenterY;

                    int[] tempColors = gradientColors;
                    float[] tempPositions = null;

                    if (st.mUseLevel) {
                        tempColors = st.mTempColors;
                        final int length = gradientColors.length;
                        if (tempColors == null || tempColors.length != length + 1) {
                            tempColors = st.mTempColors = new int[length + 1];
                        }
                        System.arraycopy(gradientColors, 0, tempColors, 0, length);
                        tempColors[length] = gradientColors[length - 1];

                        tempPositions = st.mTempPositions;
                        final float fraction = 1.0f / (length - 1);
                        if (tempPositions == null || tempPositions.length != length + 1) {
                            tempPositions = st.mTempPositions = new float[length + 1];
                        }

                        final float level = getLevel() / 10000.0f;
                        for (int i = 0; i < length; i++) {
                            tempPositions[i] = i * fraction * level;
                        }
                        tempPositions[length] = 1.0f;

                    }
                    mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));
                }

从上面这段代码中可以看出当useLevel为true的时候,level值是如何影响到不同的gradient的。

好了,关于GradientDrawable和ShapeDrawable就说这么多,如果还是不太清楚可以给我留言(应该不会有人留言,毕竟太简单了!!!)

这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!
公众号

代码地址:
MateriaDesign中包含的代码比较杂,劳烦您自己提取!!!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值