0

The event onDraw() is emitted every time the View need to be repainted cause some graphic changes inside.

Unfortunately if the View is hidden (invisible) this event is not emitted since, obviously, there is no need to repaind anything. However I would to know if there is some trick to "cheat" the View to emit the onDraw() event and redraw itself exactly like it would really showed inside the screen.

Basically I need to capture screenshot status of a View component in all its changes but whitout show it (running it in background).

I guess it would be very hard to get such result but, just in case, I'll try to ask.

Thank you

Rami
  • 7,879
  • 12
  • 36
  • 66
Suppaman
  • 151
  • 1
  • 12

2 Answers2

0

I believe a View's visibility is checked by its parent and not the View itself. You can pass in a Canvas backed by a Bitmap straight in to View#draw(Canvas canvas) and it will draw itself on to the Bitmap. However, the based on the source code of View#setVisibility(), the View's background will still be invisible.

public void setVisibility(int visibility) {
  setFlags(visibility, VISIBILITY_MASK);
  if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
}

Everything else should appear in the View as is (unless it's children are also set to invisible of course).

EDIT:

Converting a view to Bitmap without displaying it in Android?

There are examples there on how to do that.

public static Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    view.draw(canvas);
    return returnedBitmap;
}

EDIT 2:

Since setVisibility() is not part of the View, you could override it and simply not set the View to be invisible. Something like:

   boolean isInvisible = false;

   @Override
   public void setVisibility(int visibility) {
       if (visibility == View.INVISIBLE) {
           invisible = true;
       } else {
           invisible = false;
           super.setVisibility();
       }
   }

   @Override
   public void onDraw(Canvas canvas) {
       // change state code
       if (!invisible) {
           // draw code
           super.onDraw(canvas);
       }
   }

   @Override
   public void draw(Canvas canvas) {
      if (!invisible) {
          // draw code
          super.draw(canvas);
      }
   }

I have no idea what side-effects this would cause so be extremely careful and weary. Perhaps someone else would have a better solution. Another solution is you can simply call onDraw() on a different canvas whenever you want to draw it. This would require you to create a super class that is the parent layout View of the View you want to draw. Then in the parent's onDraw() method, call the child's onDraw() method separately if it's visibility is set to INVISIBLE.

Community
  • 1
  • 1
DeeV
  • 35,865
  • 9
  • 108
  • 95
  • Hi Thank you for reply. Unfortunately my problem is not take a screenshot, I already know the way to get it. My problem is to be "advised" through onDraw() if something is changed inside the View (inside there is a dynamic content). onDraw() is emitted when the View need to be repainted in some part and this allow me to know if something is changed but this event is not emitted in case of invisible state. – Suppaman Nov 20 '14 at 07:48
  • Ok. If I understand you right, you want onDraw to be called even when the View is invisible because the state of the View changes in each iteration of the draw, correct? Optimally you would not change the View state in onDraw() but outside of it somewhere else. Other than that, you could override `setVisibility()` to catch an `INVISIBLE` parameter and use it yourself. I have no idea what side-effects that would have. – DeeV Nov 20 '14 at 13:04
  • You understoond well but, on the contrary, I don't understand your suggestion. Please note I need to voluntary keep hidden the view but, at the same time, I need the view to "work" as it would be showed (than generate the onDraw() event in case of content repaint). Unfortunately I guess my need is too much strange to be realized... – Suppaman Nov 20 '14 at 20:29
  • Right. You keep the view hidden by simply not drawing to the Canvas. You can do any extra work outside of that. The reason onDraw() isn't called in your View is because the INVISIBLE flag is set so the parent doesn't bother. If you simply never set that flag, then the parent will continue to draw, but you have to make sure the View doesn't draw itself instead. Canvas object simply draw to a Bitmap. Once all the views draw, the Bitmap is displayed to the screen. – DeeV Nov 20 '14 at 20:34
  • If there are child Views that need to be drawn as well (but remain invisible), then you need to call their `onDraw()` methods as well, but with a separate canvas so they don't draw on the main Canvas provided by the Window. – DeeV Nov 20 '14 at 20:35
  • This is the point but opposite. Usually the "engine" managing the view content (that can be an animation, a progressbra and so on) emite the onDraw() event for "inform" the system a new paint is required for update the view content image. This link explain why the onDraw() event isn't called if the view is not visile: http://developer.android.com/training/basics/activity-lifecycle/stopping.html – Suppaman Nov 20 '14 at 20:52
0

If someone is interested I managed to solve this problem by override the invalidate() method. Instead of onDraw() that is called by the system only if the view is currently visible the invalidate() function is called "internally" and can be used to check if the view need to be reapinted in the same way.

Suppaman
  • 151
  • 1
  • 12