如何使用 canvas 在 ImageView 上绘制不受缩放或缩放影响的线条?
How to draw lines on an ImageView using canvas which are unaffected by zooming or scaling?
我正在通过执行以下操作在 ImageView 上画线:
Bitmap imageBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap duplicateBitmap = Bitmap.createBitmap(imageBitmap.getWidth(),imageBitmap.getHeight(),Bitmap.Config.RGB_565);
Canvas targetCanvas = new Canvas(duplicateBitmap);
targetCanvas.drawBitmap(imageBitmap,0,0,null);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(3f);
targetCanvas.drawLine(0f,100f, imageBitmap.getWidth(),100f,paint);
imageView.setImageDrawable(new BitmapDrawable(getResources(),duplicateBitmap));
不放大时看起来还不错。
但是当我放大时,线条变粗了。 (这是预期的行为)
现在,如何在图像视图上绘制线条,以便当用户放大时线条的粗细不受影响?
P.S: 我尝试先“放大”图像,然后在图像上画线。 (点击按钮或其他东西)。但是不行,线还是很粗
我还发现,当图像的分辨率较低时,绘制的线条会更粗。所以我假设在图像上绘图也与图像分辨率有某种关系。
我正在考虑用高分辨率 canvas 屏蔽 ImageView,但那样会降低内存效率。另外,我对如何实际实现这一点一无所知。
我不知何故相信 的答案可以解决我的问题。
使用此 TouchImageView 库通过 OnTouchImageViewListener
和 isZoomed()
进行缩放和检测缩放。如果缩放,则使用缩放区域重新绘制 canvas。这是代码:
touchImageView.setOnTouchImageViewListener(() -> {
if (touchImageView.isZoomed()){
//gets the zoomed area
Bitmap result = Bitmap.createBitmap(touchImageView.getWidth(), touchImageView.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(result);
touchImageView.draw(c);
}
});
正确的方法是使用视图动态呈现线条 canvas,而不是位图本身。
你的实现失败的原因是,如果你将网格直接渲染到位图中,那么你实际上是在改变位图像素,所以从逻辑上讲,如果你缩放位图,那么网格像素也会缩放,因为 所有位图+网格都变成了同一个图像。
一种解决方案是使用任何 ready-made 缩放图像视图小部件。您会在 GitHub 中找到很多,例如:
那么您有 2 个选择,扩展小部件以创建您自己的子类,或者直接在小部件代码中执行实现。由你决定。
在这两种情况下,您需要做的是覆盖其 onDraw 回调,并在 [=40= 之后立即执行网格绘制](canvas) 来电。
无需创建新的位图或新的 canvas 实例。使用方法的canvas进行绘图
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
// Render the grid
final int slotWidth = this.getWidth() / 3;
final int slotHeight = this.getHeight() / 3;
this.paint.setColor(this.gridColor);
// Horizontal lines
canvas.drawLine(0, slotHeight, this.getWidth(), slotHeight, this.paint);
canvas.drawLine(0, (slotHeight * 2), this.getWidth(), (slotHeight * 2), this.paint);
// Vertical lines
canvas.drawLine(slotWidth, 0, slotWidth, this.getHeight(), this.paint);
canvas.drawLine((slotWidth * 2), 0, (slotWidth * 2), this.getHeight(), this.paint);
}
注意,避免在onDraw方法中创建新对象,例如Paint实例等,因为onDraw可以在短时间内调用多次。无论如何,Lint 应该就此警告您。不要在onDraw、onMeasure等方法中创建新的对象实例,对于此类场景总是precache/reuse它们
一个更简单的选择,是扩展一个视图并创建一个新的专用网格视图小部件,与实际的缩放图像视图无关。同理,所以,也如上例重写onDraw方法,渲染网格如图所示。然后将这个新视图放入布局设计中,恰好位于缩放图像视图控件的顶部,确保两个控件具有相同的指标。
我正在通过执行以下操作在 ImageView 上画线:
Bitmap imageBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap duplicateBitmap = Bitmap.createBitmap(imageBitmap.getWidth(),imageBitmap.getHeight(),Bitmap.Config.RGB_565);
Canvas targetCanvas = new Canvas(duplicateBitmap);
targetCanvas.drawBitmap(imageBitmap,0,0,null);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(3f);
targetCanvas.drawLine(0f,100f, imageBitmap.getWidth(),100f,paint);
imageView.setImageDrawable(new BitmapDrawable(getResources(),duplicateBitmap));
不放大时看起来还不错。
但是当我放大时,线条变粗了。 (这是预期的行为)
现在,如何在图像视图上绘制线条,以便当用户放大时线条的粗细不受影响?
P.S: 我尝试先“放大”图像,然后在图像上画线。 (点击按钮或其他东西)。但是不行,线还是很粗
我还发现,当图像的分辨率较低时,绘制的线条会更粗。所以我假设在图像上绘图也与图像分辨率有某种关系。
我正在考虑用高分辨率 canvas 屏蔽 ImageView,但那样会降低内存效率。另外,我对如何实际实现这一点一无所知。
我不知何故相信
使用此 TouchImageView 库通过 OnTouchImageViewListener
和 isZoomed()
进行缩放和检测缩放。如果缩放,则使用缩放区域重新绘制 canvas。这是代码:
touchImageView.setOnTouchImageViewListener(() -> {
if (touchImageView.isZoomed()){
//gets the zoomed area
Bitmap result = Bitmap.createBitmap(touchImageView.getWidth(), touchImageView.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(result);
touchImageView.draw(c);
}
});
正确的方法是使用视图动态呈现线条 canvas,而不是位图本身。
你的实现失败的原因是,如果你将网格直接渲染到位图中,那么你实际上是在改变位图像素,所以从逻辑上讲,如果你缩放位图,那么网格像素也会缩放,因为 所有位图+网格都变成了同一个图像。
一种解决方案是使用任何 ready-made 缩放图像视图小部件。您会在 GitHub 中找到很多,例如:
那么您有 2 个选择,扩展小部件以创建您自己的子类,或者直接在小部件代码中执行实现。由你决定。
在这两种情况下,您需要做的是覆盖其 onDraw 回调,并在 [=40= 之后立即执行网格绘制](canvas) 来电。 无需创建新的位图或新的 canvas 实例。使用方法的canvas进行绘图
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
// Render the grid
final int slotWidth = this.getWidth() / 3;
final int slotHeight = this.getHeight() / 3;
this.paint.setColor(this.gridColor);
// Horizontal lines
canvas.drawLine(0, slotHeight, this.getWidth(), slotHeight, this.paint);
canvas.drawLine(0, (slotHeight * 2), this.getWidth(), (slotHeight * 2), this.paint);
// Vertical lines
canvas.drawLine(slotWidth, 0, slotWidth, this.getHeight(), this.paint);
canvas.drawLine((slotWidth * 2), 0, (slotWidth * 2), this.getHeight(), this.paint);
}
注意,避免在onDraw方法中创建新对象,例如Paint实例等,因为onDraw可以在短时间内调用多次。无论如何,Lint 应该就此警告您。不要在onDraw、onMeasure等方法中创建新的对象实例,对于此类场景总是precache/reuse它们
一个更简单的选择,是扩展一个视图并创建一个新的专用网格视图小部件,与实际的缩放图像视图无关。同理,所以,也如上例重写onDraw方法,渲染网格如图所示。然后将这个新视图放入布局设计中,恰好位于缩放图像视图控件的顶部,确保两个控件具有相同的指标。