I have a custom LinearLayout class that overwrites its onDraw() method. It goes into each row of ListView, as shown in the third picture. Everything looks great until I scroll the listview upward. Then the dotted line rendered from drawLine() call gets drawn over LinearLayout that sits above ListView. This is strange because the orange rounded rectangles get drawn without this problem.
My custom LinearLayout does call "this.setWillNotDraw(false);" I tried calling "LinearLayout1.postinvalidate();" in the ListView's onScrollListener, but it fails to get the top LinearLayout to redraw. What can I do to prevent the lines from getting drawn on top of LinearLayout?
Had to resort to the following workaround:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap offScreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas offScreenCanvas = new Canvas(offScreenBitmap);
// do your drawings onto offScreenBitmap here
Paint offScreenPaint = new Paint();
offScreenPaint.setAntiAlias(true);
offScreenPaint.setFilterBitmap(true);
offScreenPaint.setDither(true);
canvas.drawBitmap(offScreenBitmap,0,0,offScreenPaint);
}
My code actually prepares offScreenBitmap at the beginning, because its contents are static and need to be drawn only once.
Related
I am drawing a rectangle in the canvas on the first attempt and again I am attempting to draw one more rectangle at a different position. When I finally add them to LinearLayout I see the only 2nd rectangle, the first rectangle is lost.
Bitmap place = Bitmap.createBitmap(400,800,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(place);
DrawRect dr = new DrawRect();
dr.setLocation(10,10);
dr.draw(canvas);
dr.setLocation(10,80);
dr.draw(canvas);
ll.removeAllViews();
ll.addView(dr);
How do I make both the rectangles to be visible?
removeAllViews() will remove all view of LinearLayout.
You are adding view after removeAllViews() thats why you see 2nd rectangle only.
So remove thisll.removeAllViews(); line and run again.
I found the problem in the code, i should not use the same DrawRect object for drawing one more item, instead use the same but different DrawRect object does the magic.
I'm trying to override onDraw in an EditText subclass, to show a custom subtitle.
I've got it working, but there are a few bugs.
Basicall, all I need to do is draw StaticLayout at a certain offset from top left corner of the view.
Unfortunantly, all I get in my onDraw method is canvas. The size of the canvas is equal to the size of the whole screen (320x480 on a device with 320x480 display) and its clip bounds can be pretty much anything - it can be the whole view; it can be only top or bottom part of the view if the view is inside scrollview and partially visible; it can even be same arbitrary rect inside the view, probably because superclass invalidates only some of its region.
So if I have this view with size 320x48, I can get canvas with size 320x480 and clipping rect (200, 200, 300, 230) (left, top, right, bottom). I don't understand how this clipping rect maps to my view coordinates.
I need to know where is top left corner of the clipping rect relative to the top left corner of the view. Unfortunantly, I cannot figure out how to get this.
Added:
This code will work on all os versions that I've tested:
private int[] coordinates = new int[2];
private Matrix identityMatrix = new Matrix();
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
getLocationInWindow(coordinates);
canvas.setMatrix(identityMatrix);
canvas.translate(coordinates[0], coordinates[1]);
//do the drawing in EditText coordinate space
canvas.restore();
}
However, I still have one question: why does it work?
I've trying overriding View class and it's onDraw method will always recieve a canvas which size matches that of the View itself. Canvas for direct View subclass will have no clipping rect. Same for TextView (direct ancestor of the EditText class). But it's not the same for EditText. Canvas that gets passed to onDraw method of EditText will always (or not?) have the size of the screen, and a custom clipping rect. This whole "translate by view coordinates in window thing" seems very hacky. I don't understand why I should translate the coordinate space.
I've tried hacking android source code for the answers, but found none. EditText has no onDraw of its own. Theoretically, there should be no difference between overriding TextView onDraw and EditText onDraw. But there is a difference. The canvas object passed to onDraw method will be different depending on whether its TextView or EditText. Why? How do I know when I should apply transformation to matrix, and when I shouldn't?
you can have it with the View.getLocationOnScreen method
I'm trying to drop a smooth shadow under a view element. Here's is what I've done so far:
Subclass FrameLayout (my ShadowViewport), which overrides dispatchDraw(...) and does:
Call dispatchDraw of superclass and save output as bitmap
Extract alpa from this bitmap
Blur
Draw blurred extracted alpha with some offset
Draw original view bitmap
#Override
protected void dispatchDraw(Canvas canvas) {
Log.d("ShadowViewport", "dispatchDraw");
final Bitmap output = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
final Canvas sCanvas = new Canvas(output);
// Draw internal view in canvas
super.dispatchDraw(sCanvas);
final Bitmap original = output.copy(Config.ARGB_8888, true);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0x55000000);
paint.setMaskFilter(new BlurMaskFilter(SHADOW_SIZE, Blur.NORMAL));
canvas.drawBitmap(output.extractAlpha(), 0, SHADOW_SIZE, paint);
canvas.drawBitmap(original, 0, 0, null);
}
Result
Find image here.
As you can see, the left box has a nice generated shadow, even for rounded corners ;)
The question
The on dispatchDraw function is called very often, for example while scrolling. Is there any way for caching?
Enabling the android:hardwareAccelerated="true" attribute in the AndroidManifest will enable automatic caching of the image. Ithe dispatchDraw method is not called for example, during scrolling.
I also think it is possible to use the setDrawingCacheEnabled on your Layout Class. To make that work you should call getDrawingCache first, and the the returned bitmap if it is not null. This seems a bit like a "poor man's" optimization, but is probably a good idea to get performance out of older phones.
I have a rotated TextView class whose Canvas I rotate in the onDraw method:
#Override
protected void onDraw(final Canvas canvas) {
canvas.save();
canvas.rotate(-90);
canvas.translate(-getHeight(), 0);
super.onDraw(canvas);
canvas.restore();
}
The TextView is being positioned and rotated correctly, but the text seems to be getting clipped off:
How can I expand the Canvas correctly such that the TextView does not cut the text off?
Note the highlight area is the result of drawing the clip bounds of canvas right before super.onDraw(canvas) so I know the clip region is correct.
Of course the text is clipped off!
If you rotate the canvas, you just rotate the drawing inside the View, whereas the bounds (which don't depend by Canvas, but they're defined by the View) aren't rotated.
Therefore you have to rotate the whole View, just like the code below:
this.setRotation(-90); // 'this' is your view.
This code was supposed to convert text to image
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setTextSize(16);
paint.setAntiAlias(true);
paint.setTypeface(Typeface.MONOSPACE);
Bitmap bm = Bitmap.createBitmap(16, 16, Bitmap.Config.ALPHA_8);
float x = bm.getWidth();
float y = bm.getHeight();
Canvas c = new Canvas(bm);
c.drawText("Test", x, y, paint);
}
Is this code ok? If yes, how can I make this new bitmap visible on screen? I tried this code which produced an error
setContentView(c); //<- ERROR!
I am confused with the element Canvas as there is not such element in XML which I can use in the code.
setContentView(View) takes a View and Canvas is not a View.
I am not sure that you want to create a Canvas on your own. There are ways to get a Canvas passed to you from the Android Framework though. One way you can do this is by creating a custom View. To do this, you will need to create a new class that extends View.
When overriding a View class, you will have the ability to override the onDraw(Canvas) method. This is probably where you want to do what you are attempting to do in your onCreate() method in the code you posted.
This link gives a good overview of what is required to create your own custom view.
First: If you draw your text at the x and y position you specified, you draw it
at the lower right corner, starting with exactly that pixel. Nothing will be drawn on your canvas. Try bm.getWidth()/2, for height the same for test drawing. You can optimize that later.
Second: Canvas is not a View (does not extend the View class). You can only set Views via set ContentView(). What I recommend here is writing a XML layout containing only a single ImageView and set that via setContentView(R.layout.mylayout).
After that, you can use findViewById() to grab that ImageView and use ImageView.setImageBitmap(bm) to show your bitmap on it.
You dont have to do anything with the canvas, once you created it with your bitmap. Everything you draw inside the canvas from that point on is found in the Bitmap immediately.
Therefore you can't specify the Canvas in XML. It's just an "Editor" to edit pictures, so to speak and not an actual UI element.