Drawing Bitmap in Android? - android

My project is about image processing in android.I have a bitmap that I have loaded from a resource file (an PNG image). I want to draw it. But I couldn't.
Here my code snippet:
mB = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
Canvas c = new Canvas(mB);
Paint p = new Paint();
c.drawBitmap(mB,0,0,p);
it didn't work. Is the code true? .Is there any thing more that I must do?

You should use an ImageView instead and load it by
imageView.setImageResource(R.drawable.picture);
If you want to manually draw it with a Canvas, you have to use a canvas that is passed into a draw() method and implement a Custom View.
Update to add example CustomView:
public class CustomView extends View {
private Paint mPaint;
private Drawable mDrawable;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mDrawable = context.getResources().getDrawable(R.drawable.some_drawable);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
}

There are a couple of things you are missing.
First, I think you're misunderstanding the Canvas(Bitmap b) constructor. The Bitmap passed in there is one that the Canvas will draw into. This could be just a new Bitmap that you constructed.
Second, it is recommended that you use the Canvas that is passed to you in your View's onDraw method. Presumably that View is one from your Activity, either fetched from your XML layout via findViewById or constructed and passed to setContentView in the Activity's onCreate() method.
So, you are going to have to subclass View and override the onDraw method to get your drawing done. Something like:
public class MyView extends View {
#Override
public void onDraw (Canvas c) {
Bitmap mB = BitmapFactory.decodeResource(this.getContext().getResources(), R.drawable.picture);
c.drawBitmap(mB, 0, 0, null);
}
}
Then, in your Activity, you'll need to create an instance of your new View and pass it to the Activity via setContentView:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mv = new MyView(this);
setContentView(mv);
}
You can instead call the setContentView(View v, ViewGroup.LayoutParameters lp) overload if you want to set up the LayoutParameters.
I haven't tested any of this, but it should at least get you on the right path.

Related

Canvas DrawLine Is Invisible

I am trying to draw a single line in Android using canvas
My class :
public class LineDrawer extends View {
public LineDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
float left = 20;
float top = 20;
float right = 50;
float bottom = 100;
canvas.drawLine(left, top, right, bottom, paint);
}
}
My Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LineDrawer lineDrawer = new LineDrawer(this);
setContentView(R.layout.activity_Main);
}
}
I cannot find where is the problem , I try all the solutions in the internet but nothing happen , still a blank activity..
Should I import some code ?
lineDrawer is created but not added anywhere. Just creating a view is not enough, you need to add it to the current displayed views to be taken into account and drawn. You have two options:
Add it to your XML layout. You will have to add the following constructor to your custom view.
public LineDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
Use addView(). Anyway, given how simple is your example, I'll use first (common) method.
As an additional comment, the Paint paint object should be created on view initialization, as is a costly operation. See in the original documentation for more information about this.

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
}

Is there a way to extract a Canvas or Bitmap from a View?

I have this class that extends View and draws a line:
public class MyDraw extends View
{
Paint paint = new Paint();
public MyDraw(Context context)
{
super(context);
paint.setColor(Color.BLUE);
}
#Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawLine(1, 1, 100, 100, paint);
}
}
I would like to use the existing view from the Context to draw on top of it. Is it possible?
If you are just trying to get the view as a bitmap, you can get it from the drawing cache. This should work.
view.buildDrawingCache;
Bitmap bm = view.getDrawingCache
You cannot use the exisitng view instance and add it again as it already has a parent assigned to it and it wil cause an exception.

Passing arguments to SurfaceView via Constructor

This might not be the best practice for this but its seems like it would be the easiest. I need to pass a bitmap to a SurfaceView so its ready with onDraw is called. But I cant seem to figure out the syntax for when its assigned. Note the extra bitmap in the constructor.
public class SampleView extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder holder;
Bitmap mBitmap = null;
public SampleView (Context context, AttributeSet attrs, Bitmap bitmap) {
super(context, attrs);
if(bitmap != null)
mBitmap = bitmap
SurfaceHolder holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
double aspectRatio = ((double) mBitmap.getWidth()) / mBitmap.getHeight();
Rect dest = new Rect(0, 0, this.getWidth(),(int) (this.getHeight() / aspectRatio));
canvas.drawBitmap(mBitmap, null, dest, paint);
}
Then i'm trying to assign it, but I don't have a clue how to set the view and pass the bitmap
setContentView(R.layout.sample); //has my custom view
mSampleView = (SampleView) findViewById(R.id.sampleView);
This maybe me not understanding the ways you can init a view. I have seen examples using "addview()". The base goal of this is to display a bitmap in a surfaceview. Based on my googling you cant access the canvas other than the onDraw so I would need to pass it in when its init (i think). I'm also not sure that the constructor is called when I set the var "mSampleView" or when I use setContentView() to call the layout. If its the latter all this mute and makes me wonder if there is a way to access the canvas or target the main activity. If I could target the main activity i could just access it there when needed but I dont have a very good grasp on scope in android/java.
Is it a bad practice to pass data this way? Would be there a more standard way to passdata other than a global datastore.
To clarify my comment a bit more...You can't pass another object directly to the constructor of a class when its inherited class doesn't support that object in the constructor call.
You would have to pass the bitmap afterwards in another method call, either via the value, or by passing a reference to the object.
Your getting extremely confused with the way the onDraw method works with the objects that it uses. I know because I had trouble understanding it for the first time also.
Here is an example of something you could do.
First construct the view and give the view a bitmap
private Bitmap myReferencedBitmap;
public SampleView (Context context, AttributeSet attrs) {
super(context, attrs);
}
Now pass in the bitmap object you want to use.
public fooBitmap(Bitmap b) {
myReferencedBitmap = b;
}
Now use onDraw with the referenced bitmap
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(myReferencedBitmap, null, null, paint);
}
Now whenever you modify the object b that you passed into the SampleView class, and then invalidate the view, the onDraw method will use the new object, because myReferencedBitmap holds a reference to the (object) value.
Another thing to look at is byref vs byval in Java.
Good luck.

Custom Widget using LinearLayout not getting onDraw()

I'm creating a custom widget by extending LinearLayout:
public class MyWidget extends LinearLayout {
private static Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);
static {
PAINT.setColor(Color.RED);
}
public MyWidget(Context context) {
this(context, null);
}
public MyWidget(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight()/2, canvas.getWidth()/2, PAINT);
// never gets called :-(
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// this gets called, but with a canvas sized after the padding.
}
}
I can add children just fine, but I'm never getting my custom onDraw() being called. dispatchDraw() gets called, but that seems to have a different canvas (the one that's within the padding. I need to draw on the whole layout area). Is there some flag that needs to get set to get onDraw() called for the layout?
You need to call setWillNotDraw(false) in your constructor.
Because by default a layout does not need to draw, so an optimization is to not call is draw method. By calling setWillNotDraw(false) you tell the UI toolkit that you want to draw.

Categories

Resources