如何在Android画布的onDraw()方法中快速绘制位图

15

我正在尝试在Android的单击方法上绘制标记。当我绘制标记时,它会绘制出来,但需要更多时间来绘制,即30-40毫秒,有时需要2-3秒。这是我编写的类代码,其中包含绘图方法。

public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {

    private ArrayList<OverlayItem> overlayItemList = new ArrayList<OverlayItem>();

    public MyItemizedOverlay(Drawable pDefaultMarker,
            ResourceProxy pResourceProxy) {
        super(pDefaultMarker, pResourceProxy);
    }

    @Override
    public void draw(Canvas canvas, MapView mapView, boolean arg2) {
        super.draw(canvas, mapView, arg2);

        // ---translate the GeoPoint to screen pixels---
        Point screenPts = new Point();
        mapView.getProjection().toPixels(p, screenPts);

        // ---add the marker---
        Bitmap bmp = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_darkblue);
        Bitmap bmp1 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_green);
        Bitmap bmp2 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_bue);
        Bitmap bmp3 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_light);
        Bitmap bmp4 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_light);
        Bitmap bmp5 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_light);
        Bitmap bmp6 = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_light);
        if (count == 1) {
            int caller = getIntent().getIntExtra("button", 0);
            switch (caller) {
            case R.id.btMap:
                canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
                bmp.recycle();
                break;
            case R.id.imageButton1:
                canvas.drawBitmap(bmp1, screenPts.x, screenPts.y - 50, null);
                bmp1.recycle();
                break;
            case R.id.imageButton2:
                canvas.drawBitmap(bmp2, screenPts.x, screenPts.y - 50, null);
                bmp2.recycle();
                break;
            case R.id.imageButton3:
                canvas.drawBitmap(bmp3, screenPts.x, screenPts.y - 50, null);
                bmp3.recycle();
                break;
            case R.id.imageButton4:
                canvas.drawBitmap(bmp4, screenPts.x, screenPts.y - 50, null);
                bmp4.recycle();
                break;
            case R.id.imageButton5:
                canvas.drawBitmap(bmp5, screenPts.x, screenPts.y - 50, null);
                bmp5.recycle();
                break;
            case R.id.imageButton6:
                canvas.drawBitmap(bmp6, screenPts.x, screenPts.y - 50, null);
                bmp6.recycle();
                break;
            }
        }
        // Bitmap bmp = BitmapFactory.decodeResource(getResources(),
        // R.drawable.pin_annotation_green);
        // if (count == 1) {
        // canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
        // }
}
3个回答

19

应该在构造函数中初始化所有位图。解码位图需要很长时间。您可以使用 HashMap(键,值)来存储它们。然后在 onDraw 中,获取匹配的位图并直接绘制它。

例如:

public class MyView extends View{

    private HashMap<String, Bitmap> mStore = new HashMap<String, Bitmap>();
    public MyView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub

        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub

        int caller = getIntent().getIntExtra("button", 0);
        Bitmap bmp = null;
        switch (caller) {
        case R.id.btMap:
            bmp = mStore.get(R.id.btMap);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            bmp = null;
            break;
        case R.id.imageButton1:
            bmp = mStore.get(R.id.imageButton1);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp1.recycle();
            bmp1 = null;
            break;
        }

        super.onDraw(canvas);
    }

    public void init() {
        Bitmap bmp = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_darkblue);
        mStore.put(R.id.btMap, bmp);

        bmp = BitmapFactory.decodeResource(getResources(),
                R.drawable.pin_annotation_green);
        mStore.put(R.id.imageButton1, bmp);
    }
}

基于你的代码,这是我所做的。你必须检查一些重复的资源ID。

private ArrayList<OverlayItem> overlayItemList = new ArrayList<OverlayItem>();
private HashMap<String, Bitmap> mStore = new HashMap<String, Bitmap>();

public MyItemizedOverlay(Drawable pDefaultMarker,
        ResourceProxy pResourceProxy) {
    super(pDefaultMarker, pResourceProxy);

    Bitmap bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_darkblue);
    mStore.put(R.id.btMap, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_green);
    mStore.put(R.id.imageButton1, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_bue);
    mStore.put(R.id.imageButton2, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_light); 
    mStore.put(R.id.imageButton3, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_light); // check it
    mStore.put(R.id.imageButton4, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_light); // check it
    mStore.put(R.id.imageButton5, bmp);
    bmp = BitmapFactory.decodeResource(getResources(),
            R.drawable.pin_annotation_light); // check it
    mStore.put(R.id.imageButton6, bmp);

}

@Override
public void draw(Canvas canvas, MapView mapView, boolean arg2) {
    super.draw(canvas, mapView, arg2);

    // ---translate the GeoPoint to screen pixels---
    Point screenPts = new Point();
    mapView.getProjection().toPixels(p, screenPts);

    // ---add the marker---
    if (count == 1) {
        int caller = getIntent().getIntExtra("button", 0);
        Bitmap bmp = null;

        switch (caller) {
        case R.id.btMap:
            bmp = mStore.get(R.id.btMap);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton1:
            bmp = mStore.get(R.id.imageButton1);
            canvas.drawBitmap(bmp1, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton2:
            bmp = mStore.get(R.id.imageButton2);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton3:
            bmp = mStore.get(R.id.imageButton3);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton4:
            bmp = mStore.get(R.id.imageButton4);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton5:
            bmp = mStore.get(R.id.imageButton5);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        case R.id.imageButton6:
            bmp = mStore.get(R.id.imageButton6);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
            bmp.recycle();
            break;
        }
    }
    // Bitmap bmp = BitmapFactory.decodeResource(getResources(),
    // R.drawable.pin_annotation_green);
    // if (count == 1) {
    // canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);
    // }
}

让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/40102/discussion-between-gaurav-kumar-and-yushulx - Gaurav kumar
谢谢分享代码。但我认为在第一个示例中不需要使用switch语句。只需这样做:int caller = getIntent().getIntExtra("button", 0); Bitmap bmp = mStore.get(caller); canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null); bmp.recycle(); bmp = null; break; } - Jiechao Wang

8
代码优化的思路是只执行绘制所需的操作。因此,您应该从onDraw方法中删除以下内容:
  • 实例化:它们需要很长时间,在调用onDraw方法时经常创建新对象会导致性能问题。在onLayout期间存储screenPts并重复使用相同的点。
  • BitmapFactory.decodeResource:这需要很长时间。请先解码位图,将其存储起来,并仅在onDraw期间绘制它们。
  • 不要再每次绘制它们时回收位图,而是在不再需要它们时回收位图。
例如:
  • 在onResume期间解码位图
  • 在onPause期间回收它们
  • 解码应发生在异步任务内。当异步任务完成后,请升起一个标志,表示可以绘制图像了。
  • 在后台中解码图像非常重要,因为它需要很长时间。不要在主线程中执行此操作,否则您的应用程序将看起来没有响应。
  • 在onLayout中计算您的screenPts,并始终重复使用相同的点。
  • 也不要在onDraw中调用getIntent。
简而言之,最小化onDraw期间的操作,您将获得非常快的绘制速度,约为60 FPS。
此外,您还应考虑删除(丑陋的)开关并使用哈希映射将计数值和要绘制的位图相关联。在这里使用数组可能更快,也更适合。

4
@gaurav kumar,我认为上面的回答已经很完整了,不认为需要任何代码。首先,你应该尝试理解活动生命周期,然后就可以轻松地应用上述内容。您应该了解每种方法将被调用多少次以及何时将被调用。然后尝试重用您的代码并始终使用线程或异步任务(在Android中推荐)从UI线程中删除繁重的代码,这样您就完成了。 “Snicolas”提供的概念非常通用,可适用于所有平台。 - Bharat Sharma

2
你应该从你的draw()方法中删除所有的BitmapFactory.decodeResource()调用。只需解码一次位图并保留对它的引用。然后在你的draw()方法中调用canvas.drawBitmap()即可。

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