TV开发之如何绘制真正的圆角布局控件
一开始是这样的,热门的控件(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是如何绘制控件,以及子控件出来的。
我们又知道clipPath这个函数,也知道 Path 这个函数.
然后就可以开始着手写代码了.
为了实现真正的圆角,我重写了 draw.
@Override
public void draw(Canvas canvas) {
// 设置圆角.
canvas.save();
if (mIsDrawShape) {
canvas.clipPath(getShapePath());
}
super.draw(canvas);
canvas.restore();
}
clipPath 可以让绘制的内容只在限制的区域内显示.
那么 super.draw 绘制的控件内容,只能在 clipPath的区域显示了.
如果你的区域是一个圆角的矩形,哈哈哈.
现在无论放什么,都OK了.
要了解更多,https://github.com/FrozenFreeFall/Android-tv-widget
下载源码 查看 ReflectItemView.java吧.
来看看最后的效果吧.