尽管设置了属性,但画布绘图未正确绘制
Posted
技术标签:
【中文标题】尽管设置了属性,但画布绘图未正确绘制【英文标题】:Canvas drawing not being drawn properly despite setting properties 【发布时间】:2015-11-27 02:40:53 【问题描述】:我刚刚创建了 2 张图纸,试图让其中一张看起来与另一张相同,但我没有运气。至于屏幕截图,顶部的绘图是使用画布创建的,底部的绘图是使用 XML 创建的。 XML 绘图对我来说是完美的,但不幸的是,根据 android,我不应该使用它,因为它会返回“太多视图”警告。我已经尝试了以下画布绘制方法,但仍然没有得到想要的结果。
我已经在十几个模拟器上测试了这个项目,尽管 XML 绘图有这么多视图,但它们都只用了不到一秒钟的时间就出现了。
-
根据设备的方向使用不同的绘图
使用浮点值而不是整数
多次调整浮点值
有没有人知道还有什么其他选项可以解决这个烦人的画布问题,并确保无论屏幕大小和方向如何,画布绘图看起来都与 XML 绘图完全相同?在这种情况下是否应该忽略“太多视图”警告和/或我将视图计数限制增加到 80 以上?所有相关的帮助将不胜感激。
Java 代码
http://pastebin.com/VXgkJR2Z
XML 代码
http://pastebin.com/JyVvxS5n
【问题讨论】:
所以你必须能够切换这些矩形或其他东西吗? 您可以创建自定义视图或使用include
来分离您的布局并绕过 lint 警告。我认为这不会对性能产生任何影响。毕竟,这只是一个警告。
【参考方案1】:
您可以在下面看到处理代码后的结果。跟xml差不多,但是如果用mignifier看的话还是有区别的。
所以在你画画的时候有一些重要的事情。
-
不要使用整数。你正在失去精确度。我将所有 int 替换为浮动在您的代码中。
小心循环。您的循环精度也在下降。
代码中的常见模式:
float boxWidth = (getWidth() - mSideRectWidth) / 8; // I replaced int to float
for (int i = 0; i < 8; i++)
float position = i * boxWidth; // loss of precision
...
最好在循环中计算位置:
for (int i = 0; i < 8; i++)
float position = i * (getWidth() - mSideRectWidth) / 8;
...
-
不要忘记笔画宽度。在计算形状和线条的位置时,您会错过该值。
这是我的完整代码:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
public class Car extends View
private final Paint mBlackPaint = new Paint();
private final Paint mRedPaint = new Paint();
private final TextPaint mTextPaint;
public static final int BOXES_COUNT = 8;
private float oneDp;
private float textSize;
private float windowHeight;
public Car(Context context, AttributeSet attrs)
super(context, attrs);
oneDp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
getResources().getDisplayMetrics());
windowHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,
getResources().getDisplayMetrics());
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15,
getResources().getDisplayMetrics());
mRedPaint.setColor(Color.parseColor("#CC3333"));
mBlackPaint.setAntiAlias(true);
mBlackPaint.setColor(Color.BLACK);
mBlackPaint.setStrokeWidth(oneDp);
mBlackPaint.setStyle(Paint.Style.STROKE);
mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(textSize);
mWindowPaint = new Paint();
mWindowPaint.setAntiAlias(true);
mWindowPaint.setColor(Color.parseColor("#CC3333"));
mWindowPaint.setStyle(Paint.Style.STROKE);
mWindowPaint.setStrokeWidth(oneDp);
private Paint mWindowPaint;
RectF rect = new RectF();
RectF rect2 = new RectF();
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
if (getWidth() == 0) return;
int w = canvas.getWidth();
int h = canvas.getHeight();
//draw red rectangles
float mSideRectWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5,
getResources().getDisplayMetrics());
canvas.drawRect(0, 0, mSideRectWidth, getHeight(), mRedPaint); //draw left end rectangle
canvas.drawRect(getWidth() - mSideRectWidth, 0, getWidth(), getHeight(),
mRedPaint); //draw right end rectangle
//draw grey boxes
setBackgroundColor(Color.parseColor("#808080"));
for (int i = 0; i < BOXES_COUNT; i++)
float leftPosition = mSideRectWidth
+ i * oneDp
+ (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * i / BOXES_COUNT;
float rightPosition = mSideRectWidth
+ i * oneDp
+ (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * (i + 1)
/ BOXES_COUNT;
if (i == 0)
fillRectLeft(canvas, leftPosition, rightPosition, (i + 1) + "");
else if ( i == BOXES_COUNT - 1)
fillRectRight(canvas, leftPosition, rightPosition, (i + 1) + "");
else
fillRect(canvas, leftPosition, rightPosition, (i + 1) + "");
//draw black lines
for (int i = 1; i < BOXES_COUNT; i++)
float position = mSideRectWidth + (getWidth() - mSideRectWidth * 2) * i / BOXES_COUNT;
canvas.drawLine(position, 0, position, getHeight(), mBlackPaint);
private void fillRect(Canvas canvas, float left, float right, String number)
rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
float xPos = left + ((right - left) / 2);
float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
//((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
canvas.drawText(number, xPos, yPos, mTextPaint);
//canvas.drawRect(rect, mWindowPaint);
// top row
rect2.set(left + oneDp / 2, 0 + oneDp / 2, left + (right - left) * 20 / 100 - oneDp / 2,
windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 27 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 47 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 53 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 73 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
// bottom row
rect2.set(left + oneDp / 2, getHeight() - windowHeight + oneDp / 2,
left + (right - left) * 20 / 100 - oneDp / 2, getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 27 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 47 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 53 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 73 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
private void fillRectLeft(Canvas canvas, float left, float right, String number)
rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
float xPos = left + ((right - left) / 2);
float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
//((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
canvas.drawText(number, xPos, yPos, mTextPaint);
//canvas.drawRect(rect, mWindowPaint);
// top row
rect2.set(left + (right - left) * 4 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 24 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 42 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 62 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
// bottom row
rect2.set(left + (right - left) * 4 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 24 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 42 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 62 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
private void fillRectRight(Canvas canvas, float left, float right, String number)
rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
float xPos = left + ((right - left) / 2);
float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
//((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
canvas.drawText(number, xPos, yPos, mTextPaint);
//canvas.drawRect(rect, mWindowPaint);
// top row
rect2.set(left + (right - left) * 0 / 105 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 20 / 105 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 38 / 105 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 58 / 105 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 78 / 105 + oneDp / 2, 0 + oneDp / 2,
left + (right - left) * 98 / 105 - oneDp / 2, windowHeight - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
// bottom row
rect2.set(left + (right - left) * 0 / 105 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 20 / 105 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 38 / 105 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 58 / 105 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
rect2.set(left + (right - left) * 78 / 105 + oneDp / 2,
getHeight() - windowHeight + oneDp / 2, left + (right - left) * 98 / 105 - oneDp / 2,
getHeight() - oneDp / 2);
canvas.drawRect(rect2, mWindowPaint);
肖像
风景
【讨论】:
如果您将float
更改为double
,您的答案将会得到改善。在大多数硬件上,单浮点数相对于双浮点数的唯一优势是,如果您正在处理它们的巨大数组,则单浮点数占用一半的空间,并使用一半的内存带宽。否则,double
将获胜。即使您有充分的理由存储浮点数,您也应该在计算中使用它们时将它们提升为双倍。
如何将红色窗口更改为mWindowPaint.setStyle(Paint.Style.STROKE_AND_FILL)
而不是mWindowPaint.setStyle(Paint.Style.STROKE)
?特别是对于方框 2-7 中的一个红色窗口。
@MacaronLover 是的,你是对的。只需在绘制填充矩形之前添加行mWindowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
,然后在绘制空矩形之前返回mWindowPaint.setStyle(Paint.Style.STROKE)
。
@Ilya 酷。我明白这一点,但有点失落。例如。只需要在方框 6 中填充右上角的红色窗口,需要做什么?
@MacaronLover 在上面的代码中的fillRect
方法绘制位于中间的框(由垂直黑线分隔)。为了您的目的,最好使用有关填充或空窗口的信息创建 Box 类(布尔 topLeft、topMiddle、topRight、bottomLeft、bottomMiddle、bottomRight)。然后使用此信息向 fillRect 方法添加附加参数。最后,以下一个方式绘制if (box.topLeft) mWindowPaint.setStyle(Paint.Style.FILL_AND_STROKE); else mWindowPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(rect2, mWindowPaint);
【参考方案2】:
如何组合这两个变体 - 使用 addView(your_view, your_layout_params)
以编程方式添加这些视图【讨论】:
以上是关于尽管设置了属性,但画布绘图未正确绘制的主要内容,如果未能解决你的问题,请参考以下文章