一开始是这样的,热门的控件(LabeView),是三角形的,

图片是圆角的.

然后下面的TextView带背景颜色的是长方形的。

都不是圆角.

 

 

 【如何绘制真正的圆角矩形控件?】

 

    一般 ImageView 使用 OnDraw,虽然能弄成圆角,比如在 FrameLayout( 就是继承ViewGroup的控件)下,它显示是正常的圆角。但是,如果再放一个文本(设置背景颜色)或者按钮,layout_width 占满 FrameLayout控件的话,你就发现,只是ImageView圆角了,Button或者文本 超出了ImageView的圆角,FrameLayout 并不是真正的圆角.

    这样看来,这并不是我想要的效果。

    继续分析源码.

    

【绘制过程分析】

 

ImageView.java
 
@Override 
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas); 
        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 :  第一步,绘制背景.
    ... ...
dirtyOpaque) { // (A4)
        final Drawable background = mBackground; // (A5)
    ... ...
    // Step 3, draw the content:绘制本身的内容(一般继承viewGroup无).
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了.

 

要了解更多,https://github.com/FrozenFreeFall/Android-tv-widget 

 

下载源码 查看 ReflectItemView.java吧.

 

最终的效果。