在Android的onDraw()方法中,直接在画布上绘制和先在位图上绘制再绘制到画布上有什么区别?

8
我正在编写一个自定义视图来显示信号。为了缩短我的onDraw()时间,我将我已经绘制的所有内容缓存到Bitmap中,并在每个onDraw()调用中仅附加它。通过这样做,我可以节省大量时间,因为我只需要一次绘制一些像素,而不是重新绘制整个视图。
然而有一件事情困扰着我 - 直接绘制到提供的画布似乎比先在位图上绘制再将位图绘制到画布上提供了更准确的绘制。通过查看以下图片的下部,您可以看到差异:
我上传了一个演示项目,展示了这种差异:https://github.com/gardarh/android-uglybitmapdrawing/,但相关代码如下:
@Override
public void onDraw(Canvas canvas) {
    if(cachedBitmap == null) {
        cachedBitmap = Bitmap.createBitmap(getWidth(), 200, Config.ARGB_8888);
        cachedCanvas = new Canvas(cachedBitmap);
    }

    for(int i = 0; i < COORDS.length; i++) {
        float[] curCoords = COORDS[i];
        canvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
        cachedCanvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
    }
    canvas.drawBitmap(cachedBitmap, 0, 120, null);
}

为什么这两个轨迹不同,更重要的是,我如何使下面的轨迹看起来像上面的轨迹?

你好。非常感谢您分享您的代码。我也正在尝试制作一个应用程序,在Android屏幕上显示信号图形,您的代码帮助我得到了一些想法。然而,我在Android方面也是初学者,因此对于您的代码有一些基本问题,请您耐心解答。第一个问题是,MainActivity类中没有调用CachedDrawingJava类中的任何函数的代码,但我能够发现另一个类中的onDraw()函数至少被调用了一次。这个调用是什么时候和如何发生的? - user13267
我的图表需要在应用程序屏幕内指定的矩形区域内绘制,但是到目前为止我能找到的示例图形代码都是在整个屏幕上绘制。在你的代码中,黑色波浪线是在占据整个屏幕的图表上绘制还是只在屏幕的一部分上绘制?你的代码的哪一部分处理这个问题? - user13267
这是活动中唯一重要的一行代码:setContentView(R.layout.activity_main);现在让我们看看 https://github.com/gardarh/android-uglybitmapdrawing/blob/master/res/layout/activity_main.xml:你会看到这个:<com.noxmedical.cachedviewtest.CachedDrawingView ... />这又带领我们去了 https://github.com/gardarh/android-uglybitmapdrawing/blob/master/src/com/noxmedical/cachedviewtest/CachedDrawingView.java - 所有的工作都在 onDraw() 中完成。系统会在需要重新绘制屏幕时自动调用 onDraw()。 - gardarh
视图的大小由布局XML文件控制(在我的情况下,layout_height和layout_width设置为match_parent以填充屏幕),但您可以将其设置为固定宽度,例如layout_width="100dp" layout_height="100dp"。希望这有所帮助,祝你好运! - gardarh
3个回答

4

Android框架API提供了2D绘图API以进行简单动画,这不需要主要的动态更改。使用这些API实现的方式有两种。

1. 绘制到View
2. 绘制到Canvas

1. 在View上绘制一个圆

当您的UI界面不需要应用程序中的动态更改时,将其绘制到View是更好的选择。 这可以通过扩展View类并定义一个onDraw()回调方法来轻松实现。
使用给定的Canvas进行所有绘制, 使用各种Canvas.draw...()方法(例如:canvas.drawCircle(x / 2, y / 2, radius, paint);)。 onDraw()是在视图最初绘制时调用的回调方法。

下面是绘制圆形的简单示例代码:-

MainActivity.java

 public class MainActivity extends Activity {

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(new MyView(this));
     }

     public class MyView extends View {
         public MyView(Context context) {
              super(context);
              // TODO Auto-generated constructor stub
         }

         @Override
         protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);
            int x = getWidth();
            int y = getHeight();
            int radius;
            radius = 100;
            Paint paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.WHITE);
            canvas.drawPaint(paint);
            // Use Color.parseColor to define HTML colors
            paint.setColor(Color.parseColor("#FB9J2F"));
            canvas.drawCircle(x / 2, y / 2, radius, paint);
        }
     } }

2. 在画布上绘制矩形

如果您需要定期重新绘制应用程序中的动态2D图形,则在画布上进行绘制是更好的选择。画布为您提供了一个接口,用于实际绘制图形的表面。

如果您需要创建新的画布,则必须定义将实际执行绘制操作的位图。对于画布,始终需要位图。

以下示例说明如何绘制矩形:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/mylayout">     </LinearLayout>

MainActivity.java

public class MainActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Paint paint = new Paint();
        paint.setColor(Color.parseColor("#DD4N5C"));
        Bitmap bitmap = Bitmap.createBitmap(512, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap); 
        canvas.drawRect(150, 150, 250, 250, paint); 
        LinearLayout layout = (LinearLayout) findViewById(R.id.mylayout);
        layout.setBackgroundDrawable(new BitmapDrawable(bitmap));   
     }

     }

4
差异的原因在于画布绘制是由硬件加速(GPU)完成,而位图绘制是由软件(CPU)完成。如果禁用硬件加速,它们将变得完全相同。 如果将X坐标乘以10,您将看到差异在于线条连接的方式。这些都是微小的一像素差异,我不会过多关注它们。我不确定哪个更准确,它们似乎只是稍微不同的实现。

没错,通过禁用硬件加速进行验证。不过这很奇怪。 - gardarh

0

我不同意你通过位图方式来节省很多。你可以考虑使用一种数据结构来存储绘制的信号,并将其转换为可以序列化/反序列化的JSON对象。 至于你的问题,这只是一个有教养的猜测,但它更多地与绘制信号的重新缩放有关,因为你在创建位图时没有使用getHeight()。


使用位图可以节省很多时间,我已经测量过了。请注意,这只是一个更大绘图的一部分。位图不应该被缩放,我使用的canvas.drawBitmap()签名不应该缩放位图 - 我使用常量200代替getHeight()方法以简化代码,这应该没有关系。还有其他潜在的方法可以实现这个功能,例如使用SurfaceView。然而,现在我只想知道为什么结果图像会有所不同。 - gardarh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接