Android xml中定义的shape与Drawable之间的关系
2015-09-17 18:10
387 查看
最近遇到一个项目,需要设置非常多不同颜色的小标签:
等等;
这种情况很多同学第一反应肯定是写xml来定义shape完成这样的样式;
可是他们没有想过,如果非常多的颜色的时候,那都要为每一份颜色写一个xml文件吗?当然,这样的代码是没法让人接受的。
笔者在这样的情况下尝试了很多种写法:
1.重写TextView, 在onDraw中重新生成一份Bitmap来作为背景。但是效果不如人意
2、通过xml中定义的shape=“rectangle”,那么猜想一定是通过xxx Shape来实现的,于是写了如下代码:
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
看到了吧,根据不同的标签,解析成不同的Drawable。
2、由于当前例子需要的标签很简单:
根据标签对当前Drawable设置就能拿到想要的效果;
3、最终实现代码:
使用方式:
tv.strokeColor(color).textColor(color).update();
写这篇文章目的在于,很多开发同学总喜欢让UI切图,或者是用最笨重的方式堆代码是不可取的。
堆代码的结果不仅造成包物理大小变大,同时造成代码可复用性非常低,当代码复用性越低,那么出错的概率越大。
另外,比如在很多点击事件中,其实用一张图片就可以完成所有状态的处理(方法就是通过View的Alpha的转换就能实现)
希望这些细节能帮到需要的开发同学。
Have Fun !!!
等等;
这种情况很多同学第一反应肯定是写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 !!!
相关文章推荐
- Android: Service中创建窗口显示
- android系统平台显示驱动开发简要:LCD常用接口篇『二』
- ViewPager的setOnPageChangeListener过时。
- Android自定义对话框去掉白色边框
- Android 单元测试搭建
- 12.Android AsyncTask 技巧
- Android 杀掉进程
- android 获取一些系统指定路径的方法整理
- android JSON数据解析
- Android视频缩略图(二)
- 解决android开发在大屏手机图片出现内存溢出
- Android 事件分发机制测试以及总结
- Android判断GPS是否开启和强制帮用户打开GPS
- Android开发时,提示no resource for "theme......."in package "android"
- Unable to execute dex: Multiple dex files define Landroid/support/v4/accessi问题解决
- Android ListView工作原理完全解析,带你从源码的角度彻底理解,androidlistview
- Android平台的开发环境的发展演变
- Android 图片平铺 技术
- Android权限之sharedUserId和签名
- android github