您的位置:首页 > 移动开发 > Android开发

Android xml中定义的shape与Drawable之间的关系

2015-09-17 18:10 387 查看
最近遇到一个项目,需要设置非常多不同颜色的小标签:









等等;

这种情况很多同学第一反应肯定是写xml来定义shape完成这样的样式;

可是他们没有想过,如果非常多的颜色的时候,那都要为每一份颜色写一个xml文件吗?当然,这样的代码是没法让人接受的。

笔者在这样的情况下尝试了很多种写法:

1.重写TextView, 在onDraw中重新生成一份Bitmap来作为背景。但是效果不如人意

public void setBackgroundRounded(int w, int h, View v)
    {
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        double dH = (metrics.heightPixels / 100) * 1.5;
        int iHeight = (int)dH;

        
        iHeight = mCornerSize;
        
        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);
        
        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
        paint.setAntiAlias(true);
        paint.setColor(mBgColor);
//        paint.setColor(mContext.getResources().getColor(R.color.blue));
        RectF rec = new RectF(0, 0, w, h);
        c.drawRoundRect(rec, iHeight, iHeight, paint);

        v.setBackgroundDrawable(new BitmapDrawable(getResources(), bmp));
    }


2、通过xml中定义的shape=“rectangle”,那么猜想一定是通过xxx Shape来实现的,于是写了如下代码:

float[] outerR = new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
        RoundRectShape roundRectShape = new RoundRectShape(outerR, null, null);
        ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape);
        Paint paint = shapeDrawable.getPaint();
        if (style.equals(Paint.Style.FILL)) {
            paint.setColor(solidColor);
        } else if (style.equals(Paint.Style.STROKE)) {
            paint.setColor(strokeColor);
            paint.setStrokeWidth(strokeWidth);
        }
        paint.setStyle(style);
        paint.setAntiAlias(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(shapeDrawable);
        } else {
            setBackgroundDrawable(shapeDrawable);
        }


OK,这两种方式弄完,都非常不理想,最终效果都是圆角非常的生硬,并且不圆滑。 跟xml中定义的shape效果相差太远。
看来这两种方式都让人难以接受,那只有从xml的源码角度来分析Android API怎么做到的;

1、View.setBackgroundResource(R.drawable.xxx); 从这里入口,需要找到xml文件如何加载出来的:

执行流程:

View.setBackgroundResource

--> Context.getDrawable

-->Resources.getDrawable

-->Resources.loadDrawable

-->Resources.loadDrawableForCookie

-->Drawable.createFromXml

-->Drawable.createFromXmlInner

public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
            Theme theme) throws XmlPullParserException, IOException {
        final Drawable drawable;

        final String name = parser.getName();
        switch (name) {
            case "selector":
                drawable = new StateListDrawable();
                break;
            case "animated-selector":
                drawable = new AnimatedStateListDrawable();
                break;
            case "level-list":
                drawable = new LevelListDrawable();
                break;
            case "layer-list":
                drawable = new LayerDrawable();
                break;
            case "transition":
                drawable = new TransitionDrawable();
                break;
            case "ripple":
                drawable = new RippleDrawable();
                break;
            case "color":
                drawable = new ColorDrawable();
                break;
            case "shape":
                drawable = new GradientDrawable();
                break;
            case "vector":
                drawable = new VectorDrawable();
                break;
            case "animated-vector":
                drawable = new AnimatedVectorDrawable();
                break;
            case "scale":
                drawable = new ScaleDrawable();
                break;
            case "clip":
                drawable = new ClipDrawable();
                break;
            case "rotate":
                drawable = new RotateDrawable();
                break;
            case "animated-rotate":
                drawable = new AnimatedRotateDrawable();
                break;
            case "animation-list":
                drawable = new AnimationDrawable();
                break;
            case "inset":
                drawable = new InsetDrawable();
                break;
            case "bitmap":
                drawable = new BitmapDrawable(r);
                if (r != null) {
                    ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
                }
                break;
            case "nine-patch":
                drawable = new NinePatchDrawable();
                if (r != null) {
                    ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
                }
                break;
            default:
                throw new XmlPullParserException(parser.getPositionDescription() +
                        ": invalid drawable tag " + name);

        }
        drawable.inflate(r, parser, attrs, theme);
        return drawable;
    }


看到了吧,根据不同的标签,解析成不同的Drawable。

2、由于当前例子需要的标签很简单:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <stroke
        android:width="2px"
        android:color="@color/general_blue" />
    <solid android:color="@color/transparent" />
    <corners android:radius="3dp" />
</shape>
因此,只需要创建一个GradientDrawable即可。

根据标签对当前Drawable设置就能拿到想要的效果;

3、最终实现代码:

public class RoundedRectangleTextView extends TextView {

    private int strokeColor;
    private int strokeWidth;
    private int solidColor;
    private int radius;
    private int textColor;

    private GradientDrawable drawable;

    public RoundedRectangleTextView(Context context) {
        this(context, null);
    }

    public RoundedRectangleTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        strokeWidth = getResources().getDimensionPixelSize(R.dimen.divider) * 2;
        solidColor = getResources().getColor(R.color.transparent);
        radius = getResources().getDimensionPixelSize(R.dimen.dimen_3dp);
        textColor = getResources().getColor(R.color.text_content);

        drawable = new GradientDrawable();
        update();
    }

    public void changeWholeColorByResId(int strokeColorResId, int strokeWidthResId, int solidColorResId, int radiusResId, int textColorResId) {
        strokeColor(getResources().getColor(strokeColorResId)).
                strokeWidth(getResources().getDimensionPixelSize(strokeWidthResId)).
                solidColor(getResources().getColor(solidColorResId)).
                radius(getResources().getDimensionPixelSize(radiusResId)).
                textColor(getResources().getColor(textColorResId)).invalidate();
    }

    private void changeWholeColor(int strokeColor, int strokeWidth, int solidColor, int radius, int textColor) {
        drawable.setStroke(strokeWidth, strokeColor);
        drawable.setCornerRadius(radius);
        drawable.setShape(GradientDrawable.RECTANGLE);
        drawable.setColor(solidColor);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(drawable);
        } else {
            setBackgroundDrawable(drawable);
        }
        setTextColor(textColor);
    }

    public RoundedRectangleTextView strokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
        return this;
    }

    public RoundedRectangleTextView strokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
        return this;
    }

    public RoundedRectangleTextView solidColor(int solidColor) {
        this.solidColor = solidColor;
        return this;
    }

    public RoundedRectangleTextView radius(int radius) {
        this.radius = radius;
        return this;
    }

    public RoundedRectangleTextView textColor(int textColor) {
        this.textColor = textColor;
        return this;
    }

    public void update(){
        changeWholeColor(strokeColor, strokeWidth, solidColor, radius, textColor);
    }
}


使用方式:

tv.strokeColor(color).textColor(color).update();

写这篇文章目的在于,很多开发同学总喜欢让UI切图,或者是用最笨重的方式堆代码是不可取的。

堆代码的结果不仅造成包物理大小变大,同时造成代码可复用性非常低,当代码复用性越低,那么出错的概率越大。

另外,比如在很多点击事件中,其实用一张图片就可以完成所有状态的处理(方法就是通过View的Alpha的转换就能实现)

希望这些细节能帮到需要的开发同学。

Have Fun !!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: