How to draw Bitmap fast in onDraw() method in canvas android

匿名 (未验证) 提交于 2019-12-03 02:06:01

问题:

I am trying to draw a marker on single tap method in android. when i draw the marker it will draw but it will take more time to draw i.e 30-40 milliseconds some times it takes 2-3 seconds. Here is my code for class in which i have draw method.

public class MyItemizedOverlay extends ItemizedOverlay {      private ArrayList overlayItemList = new ArrayList();      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);         // } } 

回答1:

You should initialize all bitmaps in Constructor. Decoding bitmap takes a long time. You can use a HashMap (key, value) to store them. Then in onDraw, get the matched bitmap and draw it directly.

For example

public class MyView extends View{      private HashMap mStore = new HashMap();     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);     } } 

Here is what I've done based on your code. You have to check some duplicated resource IDs.

private ArrayList overlayItemList = new ArrayList(); private HashMap mStore = new HashMap();  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);     // } } 


回答2:

The idea to optimize your code is to perform only the operations necessary for drawing. So you, should remove from your onDraw method :

  • any instanciation : they take a long time, onDraw is called often and you don't want to create so many new objects. Store screenPts during onLayout and reuse the same points, always.
  • BitmapFactory.decodeResource : this takes quite a long time. Decode your bitmap first, store them and only draw them during onDraw.
  • recycle the bitmaps when you don't need them any more, not each time you drew them.

For instance :

  • decode your bitmaps during onResume
  • recycle them during onPause
  • decoding should occur inside an async task. When the async task is over, raise a flag to indicate to onDraw that images are ready and can be drawn.
  • it's very important to decode images in background as it takes a long time. Do not do this in main UI Thread. Otherwise your app will look unresponsive
  • calculate your screenPts inside onLayout and reuse the same points all the time.
  • don't call getIntent during onDraw either.

Briefly, minimize the operations during onDraw and you will achieve very fast drawing, around 60 FPS.

You should also consider removing that (ugly) switch and use an hashmap to associate the values of count and the bitmap to draw. An array would even be faster and maybe more appropriate here.



回答3:

You should remove all BitmapFactory.decodeResource() calls from your draw() method. Decode bitmap only once and keep reference to it. Then just call canvas.drawBitmap() in your draw() method.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!