自定义视图最重要的部分是外观。绘制自定义视图可能很简单,也可能很复杂,具体取决于应用的需求。这节课将介绍一些最常见的操作。
除了本课程,您还可以在画布和可绘制对象中找到其他相关信息。
替换 onDraw()
绘制自定义视图最重要的一步是替换
但是,在调用任何绘制方法之前,必须先创建
创建绘制对象
需要绘制什么,由
如何绘制,由
例如,
因此,在绘制任何内容之前,您需要创建一个或多个PieChart 示例在名为 init 的方法中执行此操作,该方法是从 Java 的构造函数调用的,但我们可以在 Kotlin 中进行内联初始化:
Kotlin
private val textPaint = Paint(ANTI_ALIAS_FLAG).apply {
color = textColor
if (textHeight == 0f) {
textHeight = textSize
} else {
textSize = textHeight
}
}
private val piePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
textSize = textHeight
}
private val shadowPaint = Paint(0).apply {
color = 0x101010
maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL)
}Java
private void init() {
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(textColor);
if (textHeight == 0) {
textHeight = textPaint.getTextSize();
} else {
textPaint.setTextSize(textHeight);
}
piePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
piePaint.setStyle(Paint.Style.FILL);
piePaint.setTextSize(textHeight);
shadowPaint = new Paint(0);
shadowPaint.setColor(0xff101010);
shadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
...
提前创建对象是一项重要的优化措施。视图会非常频繁地重新绘制,并且许多绘制对象的初始化都需要占用很多资源。在
处理布局事件
为了正确绘制自定义视图,您需要知道它的大小。复杂的自定义视图通常需要根据其在屏幕上所占区域的大小和形状执行多次布局计算。不要妄自假设视图在屏幕上的大小。即使只有一个应用使用您的视图,该应用也需要处理纵向和横向模式下的不同屏幕尺寸、多种屏幕密度和各种宽高比。
尽管
系统会在首次为您的视图分配大小时调用 PieChart 示例中,PieChart 视图在
为视图指定大小时,布局管理器会假定其大小包含视图的所有内边距。您必须在计算视图大小时处理内边距值。以下是PieChart.onSizeChanged()的代码段,其中说明了如何执行此操作:
Kotlin
// Account for padding
var xpad = (paddingLeft + paddingRight).toFloat()
val ypad = (paddingTop + paddingBottom).toFloat()
// Account for the label
if (showText) xpad += textWidth
val ww = w.toFloat() - xpad
val hh = h.toFloat() - ypad
// Figure out how big we can make the pie.
val diameter = Math.min(ww, hh)Java
// Account for padding
float xpad = (float)(getPaddingLeft() + getPaddingRight());
float ypad = (float)(getPaddingTop() + getPaddingBottom());
// Account for the label
if (showText) xpad += textWidth;
float ww = (float)w - xpad;
float hh = (float)h - ypad;
// Figure out how big we can make the pie.
float diameter = Math.min(ww, hh);
如果您需要更精细地控制视图的布局参数,请实现
以下是 PieChart 尝试使其面积足够大,以使饼图大小与其标签一致:
Kotlin
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Try for a width based on our minimum
val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth
val w: Int = View.resolveSizeAndState(minw, widthMeasureSpec, 1)
// Whatever the width ends up being, ask for a height that would let the pie
// get as big as it can
val minh: Int = View.MeasureSpec.getSize(w) - textWidth.toInt() + paddingBottom + paddingTop
val h: Int = View.resolveSizeAndState(
View.MeasureSpec.getSize(w) - textWidth.toInt(),
heightMeasureSpec,
0
)
setMeasuredDimension(w, h)
}Java
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Try for a width based on our minimum
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
// Whatever the width ends up being, ask for a height that would let the pie
// get as big as it can
int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);
setMeasuredDimension(w, h);
}
在此代码中,有三点需要注意:
计算时会考虑视图的内边距。如前文所述,这由视图负责计算。
辅助方法
绘制!
创建好对象并定义了测量代码后,您可以实现
使用
例如,以下代码绘制了 PieChart。它组合使用了文本、线条和形状。
Kotlin
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.apply {
// Draw the shadow
drawOval(shadowBounds, shadowPaint)
// Draw the label text
drawText(data[mCurrentItem].mLabel, textX, textY, textPaint)
// Draw the pie slices
data.forEach {
piePaint.shader = it.mShader
drawArc(bounds,
360 - it.endAngle,
it.endAngle - it.startAngle,
true, piePaint)
}
// Draw the pointer
drawLine(textX, pointerY, pointerX, pointerY, textPaint)
drawCircle(pointerX, pointerY, pointerSize, mTextPaint)
}
}Java
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw the shadow
canvas.drawOval(
shadowBounds,
shadowPaint
);
// Draw the label text
canvas.drawText(data.get(currentItem).mLabel, textX, textY, textPaint);
// Draw the pie slices
for (int i = 0; i < data.size(); ++i) {
Item it = data.get(i);
piePaint.setShader(it.shader);
canvas.drawArc(bounds,
360 - it.endAngle,
it.endAngle - it.startAngle,
true, piePaint);
}
// Draw the pointer
canvas.drawLine(textX, pointerY, pointerX, pointerY, textPaint);
canvas.drawCircle(pointerX, pointerY, pointerSize, mTextPaint);
}