How to draw "crisp and sharp" lines on an low resolution ImageView? - android

I am drawing lines on an ImageView by doing something like this:
Bitmap imageBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap duplicateBitmap = Bitmap.createBitmap(imageBitmap.getWidth(),imageBitmap.getHeight(),Bitmap.Config.RGB_565);
Canvas targetCanvas = new Canvas(duplicateBitmap);
targetCanvas.drawBitmap(imageBitmap,0,0,null);
Paint paint = new Paint();
targetCanvas.drawLine(0f,100f, imageBitmap.getWidth(),100f,paint);
imageView.setImageDrawable(new BitmapDrawable(getResources(),duplicateBitmap));
This works fine when the Image has a decent or good resolution.
But it looks like this when the image has a low resolution. (Which is a bad experience)
Now, how do I draw sharp and crisp lines on a low res image?
For once I thought I should mask the image with a high res canvas. But then I was clueless about how to proceed with this idea. Also, this might be a memory inefficient implementation.

I finally figured out with #D. Karchnak's guidance...
Let's say your phone's screen is 1080x1920, you create a layout with a
fullscreen ImageView, therefore this ImageView is also 1080x1920. Now
you load a .png image as a BitmapDrawable. This PNG image has a
resolution of 400x400, therefore the bitmap inside BitmapDrawable is
also 400x400. If you draw this "small" bitmap to a fullscreen
ImageView, it will get blurred out, because of low resolution. If you
draw a line to this 400x400 bitmap, this line will also get blurred
out. That's also why it works with decent or good resolution.
All I need to do is, create a subclass of android's ImageView and then shift my drawing logic into the onDraw method (of course after overriding it). Like so:
public class FineLineImageView extends AppCompatImageView {
public FineLineImageView(Context context) {
this(context, null);
}
public FineLineImageView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FineLineImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
// logic for drawing goes here
}
}

If you really need a Bitmap with the result, make sure the duplicateBitmap is the same size as the ImageView you're drawing it with.
If all you need is to draw a grid over a ImageView, I'd create a custom class, extending ImageView. Override it's draw(Canvas) method and use provided Canvas to draw the grid.
Code for this would look something like this:
class GridImageView(context: Context....): ImageView(...) {
val gridPaint = Paint() //Set color, strokeWidth etc...
#Override
fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//TODO - complex grid drawing here...
canvas.drawLine(x1, y1, x2, y2, paint)
}
}

Related

Paint over the specific area in Android

I want to draw over the specific Line, Rectangle or Bitmap using Canvas. If i draw over the Bitmap, it will take the square shape empty background also.
So i want to draw over that particular Bitmap area only.
create a bitmap with "bmp1" name from your desire image
create a custom view
create a class and extend View like this
class MyCustomView extends View{
private Rect m_ImageRect;
private Rect m_TextRect ;
//you need these constructor
//you can init paint object or anything on them
public MyCustomView (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
m_Context = context;
}
public MyCustomView (Context context, AttributeSet attrs)
{
super(context, attrs);
m_Context = context;
}
public MyCustomView (Context context)
{
super(context);
m_Context = context;
}
//then override on draw method
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
//here frist create two rectangle
//one for your image and two for text you want draw on it
m_ImageRect = canvas.getClipBounds();
m_TextRect = canvas.getClipBounds();
//it gives you an area that can draw on it,
//the width and height of your rect depend on your screen size device
canvas.drawBitmap(your bitmap(bmp1), null, m_ImageRect , paint);
canvas.save();
canvas.clipRect(m_TextRect);
canvas.drawText("your text", the x position you want to start draw,
the y position you want to start draw, m_paintText);
canvas.restore();
}
}
at the end put the custom view on your layout,and set field on it to send value to view for draw every thing you want
i hope it's help you,if this is not what you want!
post your code so maybe i can help you more
Seems like you need clipping. See exampls: http://www.example8.com/category/view/id/15543 , Understanding Android Canvas Clipping , http://jtomlinson.blogspot.com/2008/10/clipping.html
With clipping you can specify, which regions should be 'editable'.

Drawing over a view and all it's children

I'm trying to apply a visual effect to a viewgroup. My idea is to grab a bitmap of the viewgroup, shrink it down, expand it back up, and draw it over the viewgroup to give it a blocky, low quality effect.
I've got most of the way there using this code:
public class Blocker {
private static final float RESAMPLE_QUALITY = 0.66f; // less than 1, lower = worse quality
public static void block(Canvas canvas, Bitmap bitmap_old) {
block(canvas, bitmap_old, RESAMPLE_QUALITY);
}
public static void block(Canvas canvas, Bitmap bitmap_old, float quality) {
Bitmap bitmap_new = Bitmap.createScaledBitmap(bitmap_old, Math.round(bitmap_old.getWidth() * RESAMPLE_QUALITY), Math.round(bitmap_old.getHeight() * RESAMPLE_QUALITY), true);
Rect from = new Rect(0, 0, bitmap_new.getWidth(), bitmap_new.getHeight());
RectF to = new RectF(0, 0, bitmap_old.getWidth(), bitmap_old.getHeight());
canvas.drawBitmap(bitmap_new, from, to, null);
}
}
I simply pass in the canvas to draw on and a bitmap of what needs to be scaled down+up and it works well.
public class BlockedLinearLayout extends LinearLayout {
private static final String TAG = BlockedLinearLayout.class.getSimpleName();
public BlockedLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomAttributes(context, attrs);
setup();
}
public BlockedLinearLayout(Context context) {
super(context);
setup();
}
private void setup() {
this.setDrawingCacheEnabled(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// block(canvas); If I call this here, it works but no updates
}
#Override
public void onDraw(Canvas canvas) {
// block(canvas); If I call this here, draws behind children, still no updates
}
private void block(Canvas canvas) {
Blocker.block(canvas, this.getDrawingCache());
}
}
The problem I'm having is in my viewgroup. If I run the block method in the viewgroup's draw, it draws over everything but doesn't ever update when child views change. I've traced function calls with Log, and the draw method seems to be running, but nothing changes.
I've also tried implementing this in onDraw. This draws the bitmap behind all the children views, and again they aren't updating.
Can anyone explain how I would go about fixing this?
Try this:
#Override
protected void dispatchDraw(Canvas canvas) {
// call block() here if you want to draw behind children
super.dispatchDraw(canvas);
// call block() here if you want to draw over children
}
And call destroyDrawingCache() and then, buildDrawingCache() each time you change a child.
Draw() method will work well for you.
I'm now trying to make a count time view in a circle shape, when time is passing, the view will reducing his angle. It's used to cover profile photo(a circle shape photo).
Starting with Android API 23, you can use onDrawForeground(Canvas) to draw on top of child views: https://developer.android.com/reference/android/view/View#onDrawForeground(android.graphics.Canvas)
Unlike onDraw() though, be sure to call through to the super class:
#Override
public void onDrawForeground(final Canvas canvas) {
super.onDrawForeground(canvas);
// Your code here
}

Setting background image in a custom view

I have created a class which extends View class.
public class SplashScreen extends View
and i use it by setting contentview
View splash = new SplashScreen(this);
setContentView(splash);
I need to set background image but I can't use layout. I think I need to do canvas drawing but I don't know how to do.
protected void onDraw(Canvas canvas) {
ballBounds.set(ballX-ballRadius, ballY-ballRadius, ballX+ballRadius, ballY+ballRadius);
paint.setColor(Color.LTGRAY);
// canvas.drawImage(R.drawable.background_image); (Ps: I know there is no function such as drawImage)"
canvas.drawOval(ballBounds, paint);}
If you want to just set the background you can do
public SplashScreen(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundResource(R.drawable.background);
}
you can add image on canvas as:
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic180);
Matrix matrix=new Matrix();
matrix.postScale(0.8f, 0.8f);
matrix.postRotate(45);
Bitmap dstbmp=Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),
bmp.getHeight(),matrix,true);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(dstbmp, 10, 10, null);
Don't you just want to change SplachScreen type to activity? Then setContentView change to whatever your layout is and if you want that splach screen appear before your application, make it first in Manifest and in splashscreen end, destroy activity and start your application menu activity. Then you won't need View class and less work finding where is problem

Android canvas issue

I am new to android. Now I generated two ImageView in my Android XML file. I want to use canvas to draw two circles in each view. But the problem is, how can I deal with the coordinates? How can I know the coordinates? And how can I center them? Thanks!
ImageViews are for displaying image files, normally. If you want to draw your view yourself, you create your own View and override the onDraw method. Here is a class that draws a big red circle inside itself:
public class CircleView extends View {
public CircleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircleView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
Paint red = new Paint();
red.setColor(0xffff0000);
int height = getHeight();
int width = getWidth();
int radius = width < height ? width/2 : height/2;
canvas.drawCircle(width/2, height/2, radius, red);
}
}
You cannnot draw cirlces in an ImageView. You can only draw a circle in a bitmap and apply that bitmap to an ImageView.
Or you can create custom views and draw directly on their canvas.
In both cases you need to find the size of these views after they are created. Then you will know the coordinates as the 0,0 starts in the top left corner.

Android: Drawing a canvas to an ImageView

I'm new to android programming and what I'm trying to figure out is this;
In my layout i have a TextView, ImageView, and Button, all on a vertically oriented LinearLayout.
I want to be able to dynamically draw circles in the ImageView, without disturbing the rest of my layout(textview/button). I'm trying to create a canvas, and use the drawcircle function within canvas to set the location of the circle. And then draw that canvas to my imageview in some way. I cannot get this to work, is there a trick to this? Or is my method fundamentally wrong? How would i go about drawing circles to the ImageView without recreating my entire layout?
Thanks!
I had the same challenge and came to the conclusion that overwriting onDraw will at least in the general case not work. My blog explains the reasons. What worked very well for me is the following:
Create a new image bitmap and attach a brand new canvas to it.
Draw the image bitmap into the canvas.
Draw everything else you want into the canvas.
Attach the canvas to the ImageView.
Here is a code snippet for this:
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
ImageView myImageView = ...
Bitmap myBitmap = ...
Paint myRectPaint = ...
int x1 = ...
int y1 = ...
int x2 = ...
int y2 = ...
//Create a new image bitmap and attach a brand new canvas to it
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth(), myBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas tempCanvas = new Canvas(tempBitmap);
//Draw the image bitmap into the cavas
tempCanvas.drawBitmap(myBitmap, 0, 0, null);
//Draw everything else you want into the canvas, in this example a rectangle with rounded edges
tempCanvas.drawRoundRect(new RectF(x1,y1,x2,y2), 2, 2, myPaint);
//Attach the canvas to the ImageView
myImageView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap));
ImageView imageView=(ImageView) findViewById(R.id.image);
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
canvas.drawCircle(50, 50, 10, paint);
imageView.setImageBitmap(bitmap);
I think that a better approach would be to create a custom ImageView and Override the onDraw method. Something like:
public class CustomView extends ImageView {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrst) {
super(context, attrst);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
MyBitmapFactory bitMapFac = null;
public void setBitmapFactory(MyBitmapFactory bitMapFac)
{
this.bitMapFac = bitMapFac;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
/*instantiate a bitmap and draw stuff here, it could well be another
class which you systematically update via a different thread so that you can get a fresh updated
bitmap from, that you desire to be updated onto the custom ImageView.
That will happen everytime onDraw has received a call i.e. something like:*/
Bitmap myBitmap = bitMapFac.update(); //where update returns the most up to date Bitmap
//here you set the rectangles in which you want to draw the bitmap and pass the bitmap
canvas.drawBitmap(myBitMap, new Rect(0,0,400,400), new Rect(0,0,240,135) , null);
super.onDraw(canvas);
//you need to call postInvalidate so that the system knows that it should redraw your custom ImageView
this.postInvalidate();
}
}
It would be a good idea to implement some logic that checks if there is a fresh bitmap to acquire via the update() method, so that the code inside onDraw won't execute every time and put overhead to the system.
And then use your custom view wherever you need it. The easiest way would be to declare it directly inside the activity_layout.xml as such:
<com.mycustomviews.CustomView
android:id="#+id/customView"
android:layout_centerInParent="true"
android:layout_height="135dp"
android:layout_width="240dp"
android:background="#android:color/transparent"/>
And then access is in your code like any other view by using:
customView = (CustomView) findViewById(R.id.customView);
If you have a xml with layout with all your elements arranged in vertical orienttion. You can achieve what you want by making a class in your package which extends Class View and override its onDraw method. draw circle in it as you want . then instead of adding imageView in the layout add this your own view in the xml layout.like the following
< LinearLayout >
< TextView> < /TextView>
< Button> < /Button>
<com.prac.MyView> </ com.prac.MyView>
< /LinearLayout>
Check the following link for 2D graphics. Its a great tutorial to read.
http://organicandroid.blogspot.com/2010/08/starting-to-play-with-graphics.html
Hope this help : )
There are a couple of ways to do what you want, but using an ImageView the way you describe isn't one of them. One possibility is to have the ImageView display an animated Drawable. You can then focus on having your Drawable implementation draw the circles. Another is to create a new Bitmap every time you want the image to change and set the ImageView to display the new Bitmap. The usual way of doing this, though, is to create a custom View subclass. The API Demos sample project has an example of a custom view, and you can find lots tutorials with Google.

Categories

Resources