自定义Drawable(一)
这篇文章要介绍的是自定义Drawable,是通过继承drawable子类,重写draw(Canvas canvas)方法,实现稍复杂的drawable。准确的来说这篇要介绍的是自定义GradientDrawable
在Android开发中,会经常使用shpae 标签,如下段代码,通过xml绘制一个Drawable. 这种方式的非常简洁方便,且在Studio中可视化,但是缺点也非常明显,就是不能绘制稍复杂的drawable对象。通常我们用它来绘制一些单一的图标或背景。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/button_bg_press_color"/>
</shape>
如何自定义Drawable
Shape标签绘制出来的是什么?
上述代码绘制出来的是一个GradientDrawable对象,使用不同的标签绘制出来的drawable对象也不一样。
自定义GradientDrawable
public class MyGradinentDrawable extends GradientDrawable {
public MyGradinentDrawable() {
// setCornerRadius(5);
// setAlpha(155);
// setColor(Color.YELLOW);
// setStroke(10,Color.BLUE);
}
public MyGradinentDrawable(Orientation orientation, int[] colors) {
super(orientation, colors);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Rect rect = getDirtyBounds();//获取它所绘制的区域
int left = rect.left;
int right = rect.right;
int top = rect.top;
int bottom = rect.bottom;
int width = right - left;//绘制区域的宽度
int height = bottom - top;//绘制区域的高度
Paint paint = new Paint();
paint.setColor(Color.RED);
// paint.setTypeface(Paint.)
RectF rectF = new RectF(rect);
canvas.drawRoundRect(rectF,30,30,paint);//绘制圆角矩形区域
// canvas.drawText("自定义GradientDrawable",);
}
@Override
protected void onBoundsChange(Rect r) {
super.onBoundsChange(r);//绘制区域发生改变
}
}
扩展自定义属性
声明自定义属性,与自定义View声明属性是一模一样的
<declare-styleable name="RhomboidDrawable">
<attr name="rhd_radius" format="dimension" />
<attr name="rhd_bg_color" format="color|reference" />
<attr name="rhd_angle" format="integer" />
</declare-styleable>
使用自定义属性
<?xml version="1.0" encoding="utf-8"?>
<drawable
class="com.example.demoandroid.xml.RhomboidDrawable"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:rhd_radius="5dp"
app:rhd_angle="65"
app:rhd_bg_color="#ff00ff">
</drawable>
获取自定义属性
重写 inflate 方法, 重写四个参数的方法 , 确保可以调用到
备注:
1)获取自定义属性必须在super.inflater()方法前调用,这是因为如果一旦在其后面调用, 则获取不到自定义属性值了
2)下段代码中的obtainAttributes()方法可能调用不到, 需要自己重写.
@Override
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
int[] rhomboidDrawable = R.styleable.RhomboidDrawable;
TypedArray typedArray = obtainAttributes(r, theme, attrs, rhomboidDrawable);
mAngle = typedArray.getInteger(R.styleable.RhomboidDrawable_rhd_angle, 90);
mRadius = typedArray.getDimensionPixelOffset(R.styleable.RhomboidDrawable_rhd_radius, 0);
mColor = typedArray.getColor(R.styleable.RhomboidDrawable_rhd_bg_color, Color.TRANSPARENT);
typedArray.recycle();
super.inflate(r, parser, attrs, theme);
}
protected static TypedArray obtainAttributes(Resources res,
Resources.Theme theme, AttributeSet set, int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
如何使用
直接new 一个对象,设置到View作为背景,GradientDrawable 本身是Drawable的子类,可以直接使用。
上述MyGradinentDrawable 类是绘制了一个圆角矩形背景,其实我们可以绘制其它图形,只要我们的代码可以实现
View tv = findViewById(R.id.tv);
final MyGradinentDrawable drawable = new MyGradinentDrawable();
// Rect rect = new Rect(0,100,0,200);//设置显示区域
// drawable.setBounds(rect);//设置尺寸
drawable.setColor(Color.RED);//设置填充颜色
drawable.setStroke(10, Color.BLUE);//设置边框
tv.setBackground(drawable);
API
drawable.setBounds(rect);//设置绘制区域 这个区域尺寸的起点是根据使用的控件来计算的
drawable.setColor(Color.RED);//设置填充颜色
drawable.setStroke(10, Color.BLUE);//设置边框 ,第一形参是边框线宽度,第二形参是边框颜色值
F&A
1)绘制的图层问题。使用drawable对象调用setColor()等Api执行绘制,执行代码是在GradinentDrawable的draw()方法的源码中,如果我们在重写该方法并绘制其它图形时,且绘制代码在super.draw(canvas)之后,那自定义绘制的图形是在setColor()等api绘制的图层之上。
2)setPadding APi不会影响绘制区域
实践
实践一:绘制带圆角的平形四边形
需求:绘制带圆角的平形四边形,角度65度,圆角5dp,根据使用控件的尺寸自行适配
效果如下:
public class MyGradinentDrawable extends GradientDrawable {
private Paint mPaint;
public MyGradinentDrawable() {
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
float radius = 20;//这里随意写的
CornerPathEffect corEffect = new CornerPathEffect(radius);
mPaint.setPathEffect(corEffect);
}
public MyGradinentDrawable(Orientation orientation, int[] colors) {
super(orientation, colors);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Rect rect = getBounds();
int left = rect.left;
int right = rect.right;
int top = rect.top;
int bottom = rect.bottom;
int width = right - left;
int height = bottom - top;
int lenght = getTanLenght(65, height);
Point p0 = new Point(left, bottom);
Point p1 = new Point(lenght, top);
Point p2 = new Point(right, top);
Point p3 = new Point(right - lenght, bottom);
Path path = new Path();
path.moveTo(p0.x,p0.y);
path.lineTo(p1.x,p1.y);
path.lineTo(p2.x,p2.y);
path.lineTo(p3.x,p3.y);
path.close();
canvas.drawPath(path, mPaint);
// canvas.drawText("自定义GradientDrawable",);
}
public static int getTanLenght(int angle, int lenght) {
double var = Math.toRadians(angle);
return (int) (lenght / Math.tan(var));
}
}
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:gravity="center"
/>
View tv = findViewById(R.id.tv);
final MyGradinentDrawable drawable = new MyGradinentDrawable();
tv.setBackground(drawable);