一开始是这样的,热门的控件(LabeView),是三角形的,
图片是圆角的.
然后下面的TextView带背景颜色的是长方形的。
都不是圆角.
【如何绘制真正的圆角矩形控件?】
一般 ImageView 使用 OnDraw,虽然能弄成圆角,比如在 FrameLayout( 就是继承ViewGroup的控件)下,它显示是正常的圆角。但是,如果再放一个文本(设置背景颜色)或者按钮,layout_width 占满 FrameLayout控件的话,你就发现,只是ImageView圆角了,Button或者文本 超出了ImageView的圆角,FrameLayout 并不是真正的圆角.
这样看来,这并不是我想要的效果。
继续分析源码.
【绘制过程分析】
ImageView.java
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable == null) {
return; // couldn't resolve the URI
}
... ...
mDrawable.draw(canvas);
... ...
}
如果你使用 setBackgroundDrawable 设置ImageView,那么mDrawable就是null.
setBackgroundDrawable 是设置的背景,具体看 View.java.
public ImageView(Context context, AttributeSet attrs, int defStyle) {
... ...
Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
if (d != null) {
setImageDrawable(d);
... ...
}
以上代码发现 src="@dra..."
如果你使用 setImageDrawable 设置ImageView,那么就
将 mDrawable 绘制出来, 是我们设置ImageView 图片或者资源图片时候赋的.
从上面的代码也分析的出来,ImageView的背景和src还有有区别的噢.
有何种区别,继续看看源码.
FrameLayout.java
FrameLayout 并没有重写 onDraw 函数, 看看继承的ViewGroup.
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
ViewGroup.java
ViewGroup 也没有重写 onDraw函数,再看看继承的View.
View.java
View 找到了 onDraw 函数.
protected void onDraw(Canvas canvas) {
}
不过它只是一个空函数,什么事情都没有做.
来找找看,谁调用了 onDraw吧 ,不错就是 draw 函数.
这是draw函数给出的注释。
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers : 处理渐变
* 6. Draw decorations (scrollbars for instance)
*/
public void
draw(Canvas canvas) {
// Step 1, draw the background, if needed : 第一步,绘制背景.
... ...
if (!
dirtyOpaque) { // (A4)
final Drawable background = mBackground; // (A5)
... ...
// Step 3, draw the content:绘制本身的内容(一般继承viewGroup无).
if (!
dirtyOpaque)
onDraw(canvas);
// Step 4, draw the children:绘制子控件.
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers:渐变
... ...
// Step 6, draw decorations (scrollbars):绘制滚动条.
onDrawScrollBars(canvas);
}
如果不需要绘制渐变,则跳过第2和5步。
A4:
dirtyOpaque 表示 dirty 区是否是不透明 为 True, 透明为 false【一般Android的几乎都是透明的,都为false】.
如果View系统不支持Alpha通道,不需要绘制背景,因为视图本身会占满整个区域,背景会完全被挡住。
A5
:mBackground 就是设置背景时候传入的.
dispatchDraw 内部绘制子控件,也是调用的childView.
draw(... ...
------------------
通过上面的分析,我们大概知道了流程。
android是如何绘制控件,以及子控件出来的。
我们知道 Path 这个函数.
然后就可以开始着手写代码了.
-------------------
为了实现真正的圆角,我重写了 draw.
开始使用clippath,会有锯齿问题.
后来又改了一种方法。
mShapePaint = new Paint();
mShapePaint.setColor(Color.WHITE);
mShapePaint.setAntiAlias(true);
mShapePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
@Override
public void draw(Canvas canvas) {
if (mIsDrawShape && isDrawShapeRadiusRect(mRadiusRect)) {
drawShapePathCanvas(canvas);
} else {
super.draw(canvas);
}
}
private void drawShapePath(Canvas canvas) {
int width = getWidth();
int height = getHeight();
int count = canvas.save();
// 绘制圆角
Canvas shapeCanvas = canvas;
int count2 = shapeCanvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
Path path = DrawUtils.addRoundPath3(width, height, mRadius);
super.draw(shapeCanvas);
shapeCanvas.drawPath(path, mShapePaint);
shapeCanvas.restoreToCount(count2);
canvas.restoreToCount(count);
}
那么 super.draw 绘制的控件内容,区域显示了.
如果你的区域是一个圆角的矩形,哈哈哈.
现在无论放什么控件,Framelayout都是圆角的,OK了.
下载源码 查看 ReflectItemView.java吧.
最终的效果。