In my imageview I want to draw a bitmap everytime imageview is touched without erasing the previous bitmap. Below code draws a new bitmap but also erases the previous one. How can I keep the previous bitmap while adding new one? Thanks
imageview.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
x = event.getX();
y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
Bitmap.Config config = bm.getConfig();
int width = bm.getWidth();
int height = bm.getHeight();
Bitmap bm2 = Bitmap.createBitmap(width, height, config);
Canvas c = new Canvas(bm2);
c.drawBitmap(bm, 0, 0, null);
Bitmap repeat = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Bitmap repeat2 = Bitmap.createScaledBitmap(repeat, 50, 50, false);
c.drawBitmap(repeat2, x, y, p);
imageview.setImageBitmap(bm2);
break;
return true;
}
});
}
If you will use the same bitmap each time...
Make the Bitmap a member variable and initialize it in your onCreate system.
Create an ArrayList as a member variable.
Add a new Point each time you touch the ImageView.
Loop through the Point List and draw the same Bitmap onto your ImageView canvas.
I think you need to use an array of Bitmaps, then use the onClick to iterate through the array.
You need to have two layers for that. When you change some pixels of a bitmap, it won't be able to handle two layers. So you need to create another layer on image View and set new bitmap over there.
you have to keep track of pixels you parsed through in an arraylist, so that you can you can handle as eraser also by resetting those pixels to transparent.
Do it like this.
Bitmap bm2 = null;
Canvas c = null;
imageview.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
x = event.getX();
y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
Bitmap.Config config = bm.getConfig();
int width = bm.getWidth();
int height = bm.getHeight();
if(bm2==null){
bm2 = Bitmap.createBitmap(width, height, config);
c = new Canvas(bm2);
}
c.drawBitmap(bm, 0, 0, null);
Bitmap repeat = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Bitmap repeat2 = Bitmap.createScaledBitmap(repeat, 50, 50, false);
c.drawBitmap(repeat2, x, y, p);
imageview.setImageBitmap(bm2);
break;
return true;
}
});
}
Related
I am able to erase image by converting it in to bitmap and setting it in canvas using following code. But i am not able to set undo and redo functionality. Following code change the source bitmap so how i save path and perform undo and redo functionality?
public class MyCustomView extends View
{
private Bitmap sourceBitmap;
ImageButton undo, redo;
private Canvas sourceCanvas = new Canvas();
private Paint destPaint = new Paint();
private Path destPath = new Path();
Boolean IsEraserSet = false;
public MyCustomView(Context context, Bitmap rawBitmap)
{
super(context);
//converting drawable resource file into bitmap
// Bitmap rawBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.attire);
//converting bitmap into mutable bitmap
this.undo = undo;
this.redo = redo;
sourceBitmap = Bitmap.createBitmap(rawBitmap.getWidth(), rawBitmap.getHeight(), Bitmap.Config.ARGB_8888);
sourceCanvas.setBitmap(sourceBitmap);
sourceCanvas.drawBitmap(rawBitmap, 0, 0, null);
destPaint.setAlpha(0);
destPaint.setAntiAlias(true);
destPaint.setStyle(Paint.Style.STROKE);
destPaint.setStrokeJoin(Paint.Join.ROUND);
destPaint.setStrokeCap(Paint.Cap.ROUND);
//change this value as per your need
destPaint.setStrokeWidth(50);
destPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
sourceCanvas.drawPath(destPath, destPaint);
canvas.drawBitmap(sourceBitmap, 0, 0, null);
// sourceCanvas.drawPath(destPath, destPaint);
// canvas.drawBitmap(sourceBitmap, 0, 0, null);
}
public void setEraser(Boolean value){
IsEraserSet = value;
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
if(!IsEraserSet){
return true;
}
float xPos = event.getX();
float yPos = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
destPath.moveTo(xPos, yPos);
break;
case MotionEvent.ACTION_MOVE:
destPath.lineTo(xPos, yPos);
break;
case MotionEvent.ACTION_UP:
upTouch();
break;
}
invalidate();
return true;
}
}
After erasing image its look like as below.I have added a background image below erased image. So actually i have to erase my top image to show the background image.But how can i add undo and redo functionality? Any help will always appreciated.
You should have a java.util.LinkedList<Path> instead of a Path. Iterate that list in onDraw and draw those paths to the canvas. When user clicks undo you just remove the last Path from the list and when user clicks redo, you add the recently removed Paths, that will be in a backup java.util.Stack<Path>.
I am using Android's Fingerpaint demo[1] to experiment with Canvas and Bitmaps. I want to draw an object on a screen, and continue drawing the object after the screen has rotated. The Fingerpaint demo erases the screen after a screen rotation - I want to preserve the contents of the screen and just rotate it along with the screen.
With my code, I can rotate the screen and the image I've drawn. But I'm no longer able to add any additional path markings to the bitmap. It becomes like a read-only Bitmap. Does anyone know what I am I doing wrong?
Here's the code where I save the image and restore it after a rotation. Notice that I am saving it as a PNG in a byte array (in onSaveInstanceState()) and then creating a new Bitmap from that byte array in onCreate() (I think this is okay?):
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
Log.d("TAG", "Saving state...");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
canvasView.mBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
imageByteArray = stream.toByteArray();
savedInstanceState.putSerializable("ByteArray", imageByteArray);
super.onSaveInstanceState(savedInstanceState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
canvasView = new MyView(this);
setContentView(canvasView);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFF00FF00);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
if (savedInstanceState != null) {
Log.d("TAG", "Restoring any bitmaps...");
byte[] imageByteArray = (byte[]) savedInstanceState.getSerializable("ByteArray");
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap savedImage = BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length, opt);
canvasView.mBitmap = savedImage;
}
}
In my custom view, MyView, here's the code where I rotate the bitmap when the screen changes:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int orientation = display.getRotation();
Log.d("CANVAS", "Rotation: " + orientation);
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
} else {
Matrix rotator = new Matrix();
rotator.postRotate(orientation * 3);
Bitmap rotatedBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), rotator, true);
mCanvas = new Canvas(rotatedBitmap);
mCanvas.drawBitmap(rotatedBitmap, 0, 0, mPaint);
}
}
Just about everything else is the same as in the Fingerpaint demo. I can push down to make markings on the screen, but when I lift my finger the path I've created is not applied to the bitmap.
Here's a duplication of the onTouchEvent() to illustrate (I did not modify it):
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
Thanks in advance for any insight. I suspect my understanding of how Canvas should work is not correct, and hence my confusion!
[1] The full Fingerpaint demo is here: https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.java
BitmapFactory.decodeByteArray() ignores the option where you asked for a mutable bitmap and gives you one that isn't mutable because that's how it rolls.
https://developer.android.com/reference/android/graphics/BitmapFactory.html#decodeByteArray(byte[], int, int, android.graphics.BitmapFactory.Options)
decodeByteArray
Bitmap decodeByteArray (byte[] data,
int offset,
int length,
BitmapFactory.Options opts)
Decode an immutable bitmap from the specified byte array.
You need to create a new bitmap object, using one of the ways to create a mutable bitmap. Then paint the immutable bitmap into the mutable one. Then toss the one you loaded with that function.
The reason for the immutability is that the bitmap was created from those bytes you gave it. So for speed they are backed by those bytes. They are the same memory. And the way actual bitmap objects work is different than that, and you can't just magically make a mutable bitmap by forcing those bytes from normal memory into the GPU. You can just make a second bitmap object and paint the data into that second bitmap.
You must control your configuration change for the activity.
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="#string/app_name">
For more details just go for https://developer.android.com/guide/topics/resources/runtime-changes.html
Hope it helps..
I want to draw at specific coordinates of an image which is displayed in an imageview. I use the src attribute of a imageview to load the image into the view. This code is used in an custom imageview to draw on the image:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float scaleh =(float)canvas.getHeight()/(float)orginalheight();
float scalew = ((float)canvas.getWidth()/(float)orginalwidth());
canvas.drawRect(10*scaleh,9*scalew,20*scaleh,30*,scalewpaint);
}
This code draws the rectangle at the wrong location. What is wrong?
You should get the dimensions of the View from the ImageView itself rather than Canvas especially when the ulterior goal is to Draw on Image inside ImageView.
A Canvas works for you as a pretense, or interface, to the actual
surface upon which your graphics will be drawn — it holds all of your
"draw" calls. Via the Canvas, your drawing is actually performed upon
an underlying Bitmap, which is placed into the window.
Firstly, in order to get the Coordinates, implement onTouchListener for ImageView. This will give you X and Y co ordinates to draw the Canvas on.
imageView.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
xDown = event.getX();
yDown = event.getY();
break;
case MotionEvent.ACTION_MOVE:
xUp = event.getX();
uUp = event.getY();
canvas.drawLine(xDown, yDown, xUp, uUp, paint);
imageView.invalidate();
xDown = xUp;
yDown = uUp;
break;
case MotionEvent.ACTION_UP:
xUp = event.getX();
uUp = event.getY();
canvas.drawLine(xDown, yDown, xUp, uUp, paint);
imageView.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}});
Then create a Canvas to draw on top of ImageView.
//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 canvas = new Canvas(tempBitmap);
paint = new Paint();
paint.setColor(YOUR_DESIRED_COLOR);
paint.setStrokeWidth(INT_VALUE);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
//Attach the canvas to the ImageView
imageViewsetImageBitmap(tempBitmap);
you can also use canvas.drawRect(left,top,right,bottom,paint); if you want to draw Rectangle simply.
Hope it helps.
My image is bigger, but just a part of it have to be displayed on the screen. I have done this. My problem is that, the image has to be rotated by finger. The code below works as it should on a small image. But on my image it has a wrong behave, repositioning the image up and down and rotate it weirdly. How to make my image to rotate normally?, as a small image. Then I have to be able to get the color touched. I get an answer here, but I still have to work at it. But first, please tell me how could I solve the problem with rotation, and if you have ideas about the second issue, with getting the color touched I would like to hear them.
this is the image:
this is how it should appear on the screen:
XML Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageView android:id="#+id/imag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="#drawable/roata"
android:layout_alignParentBottom="true"
android:layout_marginBottom="-200dp"
android:scaleType="center"/>
</RelativeLayout>
Java code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
picture = (ImageView)findViewById(R.id.imag);
picture.setOnTouchListener(onTableTouched);
}
public android.view.View.OnTouchListener onTableTouched = new android.view.View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent evt)
{
double r = Math.atan2(evt.getX() - picture.getWidth() / 2, picture.getHeight() / 2 - evt.getY());
int rotation = (int) Math.toDegrees(r);
if (evt.getAction() == MotionEvent.ACTION_DOWN)
{
//
}
if (evt.getAction() == MotionEvent.ACTION_MOVE)
{
updateRotation(rotation);
}
if (evt.getAction() == MotionEvent.ACTION_UP)
{
//
}
return true;
}
};
private void updateRotation(double rot)
{
float newRot = new Float(rot);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
Matrix matrix = new Matrix();
matrix.postRotate(newRot - 50);
Bitmap redrawnBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
picture.setImageBitmap(redrawnBitmap);
}
UPDATE:
Because I got nowhere with Bitmap and Matrix I tried to adjust the RotateAnimtaion() to get something as similar as I could. I think modifying the second param and the duration of the animation dynamic, we can get a similar result without changing the Y position. Now, the problem is that the image is crop, I think because of scaleType="center". I will post code and a screen shoot. Could this be improved?
Animation a = new RotateAnimation(0.0f, param,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
a.setFillEnabled(true);
a.setFillAfter(true);
a.setDuration(param);
picture.startAnimation(a);
just declare Bitmap as globally as:
private WeakReference<Bitmap> bitmap;
and use
bitmap =new WeakReference(BitmapFactory.decodeResource(getResources(), R.drawable.roata));
instead of
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
I think the problem is , you are trying to redraw the same Bitmap in every updateRotation() call.
How about removing this line,
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
from updateRotation(double rot) and have it declared globally , so that you need not create the same Bitmap again and again which is why you are getting the OutOfMemory.
Try this,
Bitmap bitmap=null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
picture = (ImageView)findViewById(R.id.imag);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
picture.setOnTouchListener(onTableTouched);
}
public android.view.View.OnTouchListener onTableTouched = new android.view.View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent evt) {
double r = Math.atan2(evt.getX() - picture.getWidth() / 2, picture.getHeight() / 2 - evt.getY());
int rotation = (int) Math.toDegrees(r);
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
//
}
if (evt.getAction() == MotionEvent.ACTION_MOVE) {
updateRotation(rotation);
}
if (evt.getAction() == MotionEvent.ACTION_UP) {
//
}
return true;
}
};
private void updateRotation(double rot) {
float newRot = new Float(rot);
Matrix matrix = new Matrix();
matrix.postRotate(newRot - 50);
Bitmap redrawnBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
picture.setImageBitmap(redrawnBitmap);
}
I am having Image Quality problems using Canvas and canvas.scale(Scale, Scale); they look exactly like the following:
android: quality of the images resized in runtime
I believe I have read all the posts on image quality problems when re-sizing bitmaps, but it doesn't seem to help when scaling with a Canvas scale(float scale).
I have tried many different options as suggested by the image quality posts.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = 1;
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;//I thought this would do it
CurrentPicture = BitmapFactory.decodeFile(path, options);//Also tried decodeStream()
PicturePaint = new Paint();
//PicturePaint = new Paint(Paint.FILTER_BITMAP_FLAG); //I also tried this
//PicturePaint = new Paint(Paint.ANTI_ALIAS_FLAG); //I also tried this
canvas.scale(Scale, Scale);
canvas.drawBitmap(CurrentPicture, 0, 0, PicturePaint);
I believe this the last barrier to achieving my goals. I am quite concerned as I am in trouble if I can't get the image quality problem solved. Any help is appreciated.
Thanks!
The system will not let me post a picture, so following is a link.
PictureSample
I don't no whether my answer is correct or not i have a code for canvas. i wish this might help you.
public class FotosurpriseActivity extends Activity {
/** Called when the activity is first created. */
Bitmap overlay;
Paint pTouch;
int X = -100;
int Y = -100;
Canvas c2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
Bitmap mBitmapover = BitmapFactory.decodeResource(getResources(), R.drawable.ss);
overlay = BitmapFactory.decodeResource(getResources(), R.drawable.ss).copy(Config.ARGB_8888, true);
c2 = new Canvas(overlay);
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
// pTouch.setXfermode(new PorterDuffXfermode(Mode.TARGET);
pTouch.setColor(Color.TRANSPARENT);
pTouch.setMaskFilter(new BlurMaskFilter(15, Blur.NORMAL));
setContentView(new BitMapView(this, mBitmap, mBitmapover));
}
class BitMapView extends View {
Bitmap mBitmap = null;
Bitmap mBitmapover = null;
public BitMapView(Context context, Bitmap bm, Bitmap bmover) {
super(context);
mBitmap = bm;
mBitmapover = bmover;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
}
case MotionEvent.ACTION_UP:
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
// called when view is drawn
Paint paint = new Paint();
paint.setFilterBitmap(true);
// The image will be scaled so it will fill the width, and the
// height will preserve the image’s aspect ration
/*
* double aspectRatio = ((double) mBitmap.getWidth()) /
* mBitmap.getHeight(); Rect dest = new Rect(0, 0,
* this.getWidth(),(int) (this.getHeight() / aspectRatio)); double
* aspectRatio2 = ((double) mBitmapover.getWidth()) /
* mBitmapover.getHeight(); Rect dest2 = new Rect(0, 0,
* this.getWidth(),(int) (this.getHeight() / aspectRatio2));
* canvas.drawBitmap(mBitmap, null, dest, paint);
* canvas.drawBitmap(mBitmapover, null, dest2, paint);
*/
// draw background
canvas.drawBitmap(mBitmap, 0, 0, null);
// copy the default overlay into temporary overlay and punch a hole
// in it
c2.drawBitmap(mBitmapover, 0, 0, null); // exclude this line to show
// all as you draw
c2.drawCircle(X, Y, 80, pTouch);
// draw the overlay over the background
canvas.drawBitmap(overlay, 0, 0, null);
}
}
}
Do you have a screenshot that would show the quality issue you are talking about? If filtering is enabled on the Paint it should be fine.
I tried many and many code.
I just add this in my manifest and I had the best quality image.
<uses-sdk android:minSdkVersion="9" />
I think the problem is the canvas is created in low resolution. If u try to print on the screen the resolution of your device, u can see it is wrong.
With the add in the manifest the resolution change.