Android Blog Canvas cavas draw drawable gesture image scale ui

Custom View to draw, rotate, erase and convert images to black and white.

Print Friendly

This article describes how to create a reusable class that may upload images from our Gallery or take an image, put them on a canvas and move them around the display, resize them, erase them and convert them to black and white. This tutorial uses a class that extends View and uses onTouchEvents. That is class also takes care of the VM Price range challenge of loading images from the gallery by optimizing the memory each image makes use of. It uses android 2.2 and above.

Earlier than shopping for this text, you possibly can download the apk file right here: http://ece301-examples.googlecode.com/files/CanvasView.apk

This can be a class I created as a part of an software I’m engaged on the place I want customers to choose images and put them onto the display, transfer them around and resize them. So lets get began.

First, we create a undertaking “CanvasView”. The package deal identify is com.canvas.view.library. We create two courses; Stye.java and TouchView.java. Fashion.java will be the exercise.

The primary.xml will include the next code:



Now we’ll start modifying TouchView. TouchView will prolong View, thus permitting us to create custom views that we will draw on and do different things. First, I’ll present the variables that can be used and the constructor.

public class TouchView extends View

personal static last int INVALID_POINTER_ID = -1;

personal last String TAG = “TESTESTEST”;

personal Drawable mImage;

public void setmImage(Drawable picture)
mImage = image;

// width and peak of unique picture
personal float mImageWidth;
personal float mImageHeight;

// when image is scaled, we use this to calculate the bounds of the picture
personal int mImageWidthScaled;
personal int mImageHeightScaled;

public float mPosX = 150;
public float mPosY = 300;

public float getmPosX()
return mPosX;

public void setmPosX(float mPosX)
this.mPosX = mPosX;

public float getmPosY()
return mPosY;

public void setmPosY(float mPosY)
this.mPosY = mPosY;

personal float mLastTouchX;
personal float mLastTouchY;

personal Paint mBorderLeftLine;
personal Paint mBorderTopLine;
personal Paint mBorderRightLine;
personal Paint mBorderBottomLine;

personal int mActivePointerId = INVALID_POINTER_ID;
personal ScaleGestureDetector mScaleDetector;
personal float mScaleFactor = 1f;

// this is to tell Type what view number I am within the array.
personal int mNumberView;

// this is what draws the purple line around the TouchView to tell the consumer
// this one is presently chosen
personal boolean mSelected = false;

public void setmSelected(boolean mSelected)
this.mSelected = mSelected;

personal Fashion mStyle;

// that is to hold a reference to the original image, so once we ship, we will tar
// the images which are needed.
personal String mUri;

public void setImageLocation(String path)
this.mUri = path;

public TouchView(Context context,Fashion type, BitmapDrawable image, int rely, float scaleFactor)
tremendous(context);
this.mImage = image;
mImageWidth = picture.getBitmap().getWidth();
mImageHeight = image.getBitmap().getHeight();
mImageWidthScaled = (int) (mImageWidth*scaleFactor);
mImageHeightScaled = (int) (mImageHeight*scaleFactor);
this.mNumberView = rely;
this.mStyle = fashion;
this.mScaleFactor = scaleFactor;
init(context);

Lets solely concentrate on the constructor right now. The constructor passes a Drawable which would be the picture for this view. The imageWidth and Peak have to be defined in the constructor. What these values will do is give me the precise image width and peak used within the view. Later we’ll use these values for when we have now totally different views and the consumer clicks on particular person ones, that view consumes the onTouchView occasion. Intrinsic Width and peak did not work nicely for me.

The mNumberView will define which View I’m. In Fashion.java I’ll have an ArrayList with the listing of views. That is how I manage the views.

mScaleFactor is the size issue for zooming in and out the picture.

Next piece of code initializes values for drawing to happen when onDraw() is known as.

personal void init(Context context)
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mBorderLeftLine = new Paint();
mBorderRightLine = new Paint();
mBorderBottomLine = new Paint();
mBorderTopLine = new Paint();
setBorderParams(Colour.RED,2);

personal void setBorderParams(int shade, float width)
mBorderLeftLine.setColor(colour);
mBorderLeftLine.setStrokeWidth(width);
mBorderRightLine.setColor(shade);
mBorderRightLine.setStrokeWidth(width);
mBorderBottomLine.setColor(colour);
mBorderBottomLine.setStrokeWidth(width);
mBorderTopLine.setColor(shade);
mBorderTopLine.setStrokeWidth(width);
mImage.setBounds(0, 0,mImage.getIntrinsicWidth(),mImage.getIntrinsicHeight());

public void onDraw(Canvas canvas)
tremendous.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mImage.draw(canvas);
if (mSelected)
canvas.drawLine(zero,
zero,
mImage.getIntrinsicWidth(),
0,
mBorderTopLine);
canvas.drawLine(zero, mImage.getIntrinsicHeight(),
mImage.getIntrinsicWidth(),
mImage.getIntrinsicHeight(),
mBorderBottomLine);
canvas.drawLine(0,
zero,
0,
mImage.getIntrinsicHeight(),
mBorderLeftLine);
canvas.drawLine(mImage.getIntrinsicWidth(),
0,
mImage.getIntrinsicWidth(),
mImage.getIntrinsicHeight(),
mBorderRightLine);

canvas.restore();

The constructor calls the tactic init(context); As a result of I’m drawing a purple rectangle around the picture that is presently in use by a consumer, I create Paints for each line and setBorderParams(); units the colour and stroke width of those strains. ALSO, essential is mImage.setBounds(); If I do not do that, the picture (Drawable) won’t draw when onDraw() known as.

onDraw() technique merely calls canvas.translate() to transfer the image across the display through the use of mPosX and mPosY values. These values are set in my onTouchEvent and we’ll see how later.
OnDraw() additionally makes use of canvas.scale() to scale the picture depending when a consumer uses multitouch to stretch the picture.
OnDraw() additionally attracts the a pink rectangle around the presently selected image by a consumer. That is finished with a boolean, mSelected.

I feel up to now, things are easy. However the troublesome a part of this class is the onTouchEvent technique. onTouchEvent is used for the following:

1. Discover out when the consumer does multitouch.
2. Move the picture round by setting the values mPosX and mPosY
three. Verify if this view is the one being selected because we will have more than 1 of those views on the display at any time. (Within the image for this article I’ve 4. Every image represents an object of this view).

public boolean onTouchEvent(MotionEvent event)
//ImageView view = (ImageView) v;
mScaleDetector.onTouchEvent(event);
boolean intercept = false;
//boolean defaultResult = onTouchEvent(event);

change (occasion.getAction() & MotionEvent.ACTION_MASK)

case MotionEvent.ACTION_DOWN:
mLastTouchX = occasion.getX();
mLastTouchY = event.getY();
mActivePointerId = occasion.getPointerId(zero);
// this should imply that I accept the contact out of all my views
/*if (((mLastTouchX >= mPosX) && (mLastTouchX = mPosY) && (mLastTouchY = mPosX) && (mLastTouchX = mPosY) && (mLastTouchY > MotionEvent.ACTION_POINTER_ID_SHIFT;
remaining int pointerId = event.getPointerId(pointerIndex2);
if (pointerId == mActivePointerId)
// This was our lively pointer going up. Select a brand new
// lively pointer and regulate accordingly.
last int newPointerIndex = pointerIndex2 == 0 ? 1 : zero;
mLastTouchX = occasion.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = occasion.getPointerId(newPointerIndex);

break;
default:
//return defaultResult;

return intercept;

When first doing an ACION_DOWN I get the pixel values the place the consumer is touching. The if statement is checking if these pixel values (x,y) are inside the image location. So my picture is at place (100,200) with a width of 50 and peak of 50, then it’s checking if the contact falls inside these bounds, if it does, then I set the boolean intercept to true, else to false. See that at the finish we return intercept. Because of this if onTouchEvent returns false, this view doesn’t eat the contact and is passed to other views. Also mActivePointerId = event.getPointerId(zero); is getting the pointerId for multitouch events.

Right here is the code again for the case ACTION_DOWN:

case MotionEvent.ACTION_DOWN:
mLastTouchX = event.getX();
mLastTouchY = occasion.getY();
mActivePointerId = occasion.getPointerId(0);
if (((mLastTouchX >= mPosX) && (mLastTouchX = mPosY) && (mLastTouchY <= mPosY + mImageHeightScaled)))
Log.i(TAG,"My view is here: "+mNumberView);
intercept = true;
mSelected = true;
mStyle.setmCurrentView(mNumberView);

Log.i(TAG,"Action down");
Log.i(TAG,"x is: "+mLastTouchX);
Log.i(TAG,"y is: "+mLastTouchY);
break;

Now on ACTION_UP recalculates the width and peak of the image in case it was stretched. It also makes the view unfocused. This is in order that once we attempt to click on on another object, the program doesn’t assume we’re still targeted on this object and strikes this one as an alternative. Right here it is once more:

case MotionEvent.ACTION_UP:
setFocusable(false);
mImageWidthScaled = (int) (mImageWidth*mScaleFactor);
mImageHeightScaled = (int) (mImageHeight*mScaleFactor);
/* mPosX = (int) occasion.getX();
mPosY = (int) occasion.getY();*/
mActivePointerId = INVALID_POINTER_ID;
// stop the purple rectangle from being drawn across the View
mSelected = false;
break;

ACTION_CANCEL doesn’t actually do something so I gained’t go over it.

ACTION_MOVE verify that presently the view is just not being scaled (stretched) and if it isn’t, then it moves the picture across the display.

case MotionEvent.ACTION_MOVE:
remaining int pointerIndex = occasion.findPointerIndex(mActivePointerId);
last float x = event.getX(pointerIndex);
remaining float y = occasion.getY(pointerIndex);
if (!mScaleDetector.isInProgress())
last float dx = x – mLastTouchX;
ultimate float dy = y – mLastTouchY;

mPosX += dx;
mPosY += dy;

// Invalidate to request a redraw
invalidate();

else
Log.i(TAG,”Now scaling is occurring”);

// Keep in mind this contact place for the subsequent move occasion
mLastTouchX = x;
mLastTouchY = y;

break;

Lastly POINTER_UP is used for multitouch occasions and primarily here for stretching the picture when a consumer makes use of multitouch.

case MotionEvent.ACTION_POINTER_UP:
ultimate int pointerIndex2 = (occasion.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)
>> MotionEvent.ACTION_POINTER_ID_SHIFT;
remaining int pointerId = occasion.getPointerId(pointerIndex2);
if (pointerId == mActivePointerId)
// This was our lively pointer going up. Select a brand new
// lively pointer and regulate accordingly.
remaining int newPointerIndex = pointerIndex2 == 0 ? 1 : zero;
mLastTouchX = occasion.getX(newPointerIndex);
mLastTouchY = occasion.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);

break;

Now in order to do multitouch and have the ability to scale the image, we’ve got to create a class inside this class. It extends ScaleGestureDetector and this is the rationale why we’d like to use android 2.2.

personal class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
@Override
public boolean onScale(ScaleGestureDetector detector)
mScaleFactor *= detector.getScaleFactor();

// Don’t let the thing get too small or too giant.
mScaleFactor = Math.max(0.05f, Math.min(mScaleFactor, 2.0f));
invalidate();
Log.i(TAG,”New Picture measurement: widht: “+mImage.getIntrinsicWidth()+” peak: “+mImage.getIntrinsicHeight());
return true;

All it does it once we detect that scaling is occurring, it calculates the size issue. within the onDraw technique we’ve canvas.scale(). That is the place this worth is available in and scales the image accordingly.

Finally as an added bonus, I do some picture processing to turn the picture from shade to black and white

public void greyScaler()
ColorMatrix cm = new ColorMatrix();
cm.set(new float[]
.21f, .71f, .07f, zero, 0,
.21f, .71f, .07f, 0, 0,
.21f, .71f, .07f, 0, 0,
0, 0, zero, 1, zero );
mImage.setColorFilter(new ColorMatrixColorFilter(cm));
invalidate();

It took me some time to determine it out since I never labored with ColorMatrix before. The array you see is a multiplier. It comes from here: http://developer.android.com/reference/android/graphics/ColorMatrix.html

Principally the RGB colors + Alpha are multiplied by the matrix values above. One algorithm for black and white is to as follows .21*R + .71*G + .07B. That is what I’ve above. invalidate known as every time we would like the onDraw() technique to be referred to as manually by us. In my case, after altering the picture filter, it won’t replace till onDraw known as, thus I name it.

In case you are following me up to now, we aren’t completed but.We now have created the Object that may care for all the work for us to transfer the picture, scale it, and so on. This class could be extended and different methods added or you’ll be able to add other strategies your self that you simply may want. Like some additional picture processing, rotation, and so on.

Fashion.java extends Exercise and holds an ArrayList of sort TouchView to hold monitor of our object creation. Let’s begin with the variables and onCreate technique:

public class Fashion extends Exercise

personal ultimate static String TAG = “TESTESTESTEST”;

// to take an image
personal static remaining int CAMERA_PIC_REQUEST = 1111;
personal static last int GALLERY_PIC_REQUEST = 1112;

personal Button mCameraButton;
personal Button mBwButton;
personal Button mTrashButton;

// current view is the current chosen view – hopefully it will work ok
personal int mCurrentView = zero;

public int getmCurrentView()
return mCurrentView;

public void setmCurrentView(int mCurrentView)
this.mCurrentView = mCurrentView;

// this tells me what number of views I presently have.
personal int mViewsCount = 0;

personal ArrayList mViewsArray = new ArrayList();

personal static Type mStyle;

public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.format.primary);

mStyle = this;

mTrashButton = (Button) findViewById(R.id.trash_button);
mTrashButton.setClickable(true);
mTrashButton.setOnClickListener(new View.OnClickListener()

@Override
public void onClick(View v)
Log.i(TAG,”Trash clicked”);
Log.i(TAG,”Array measurement is: “+mViewsArray.measurement());
if (mViewsArray.measurement() > zero)
Log.i(TAG,”Ought to remove this view”);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.removeView(mViewsArray.get(mCurrentView));
mViewsArray.remove(mCurrentView);
mViewsCount -=1;

);

mCameraButton = (Button) findViewById(R.id.camera_button);
mCameraButton.setClickable(true);
mCameraButton.setOnClickListener(new View.OnClickListener()

@Override
public void onClick(View v)
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle(“Select:”);
last CharSequence[] chars = “Take Picture”, “Choose from Gallery”;
builder.setItems(chars, new android.content material.DialogInterface.OnClickListener()

@Override
public void onClick(DialogInterface dialog, int which)
if(which == zero)
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);
else
if(which == 1)
Intent intent = new Intent(Intent.ACTION_PICK, android.supplier.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
startActivityForResult(intent,GALLERY_PIC_REQUEST);

dialog.dismiss();

);
builder.show();

);

mBwButton = (Button) findViewById(R.id.bw_button);
mBwButton.setClickable(true);
mBwButton.setOnClickListener(new View.OnClickListener()

@Override
public void onClick(View v)
if (mViewsArray.measurement() > 0)
((TouchView) mViewsArray.get(mCurrentView)).greyScaler();

else
Toast.makeText(v.getContext(), “Please select an image first before using this function.”, Toast.LENGTH_SHORT).present();

);

The onCreate technique holds my click listeners for my buttons to erase, take photograph and make black and white.

The trash button removes the view from each the format and then ArrayList, mViewsArray. Right here it’s once more:

mTrashButton.setOnClickListener(new View.OnClickListener()

@Override
public void onClick(View v)
Log.i(TAG,”Trash clicked”);
Log.i(TAG,”Array measurement is: “+mViewsArray.measurement());
if (mViewsArray.measurement() > zero)
Log.i(TAG,”Should remove this view”);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.removeView(mViewsArray.get(mCurrentView));
mViewsArray.take away(mCurrentView);
mViewsCount -=1;

);

mViewsCount keeps a rely of TouchViews created.

mCameraButton creates an Alert to let a consumer choose between utilizing a present picture from the Gallery or take a picture. Right here it’s once more:

mCameraButton.setOnClickListener(new View.OnClickListener()

@Override
public void onClick(View v)
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle(“Select:”);
remaining CharSequence[] chars = “Take Picture”, “Choose from Gallery”;
builder.setItems(chars, new android.content.DialogInterface.OnClickListener()

@Override
public void onClick(DialogInterface dialog, int which)
if(which == 0)
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);
else
if(which == 1)
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
startActivityForResult(intent,GALLERY_PIC_REQUEST);

dialog.dismiss();

);
builder.show();

);

It also makes use of android actions: android.supplier.MediaStore.ACTION_IMAGE_CAPTURE and Intent.ACTION_PICK with android.supplier.MediaStore.Images.Media.INTERNAL_CONTENT_URI.

mBwButton calls the greyScaler() technique in the TouchView to make the image black and white.

When creating these courses, I assumed it will be useful to not only load the images from the gallery or from taking a picture, but in addition to hold monitor of its path. Nevertheless, once we name startActivityForResult(). We do not get absolutely the path, we I wanted to add this perform so I might get it.

[soucecode language=”java”]public String getPath(Uri uri)
String[] filePathColumn=MediaStore.Images.Media.DATA;

Cursor cursor=getContentResolver().question(uri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex=cursor.getColumnIndex(filePathColumn[0]);
Log.i(TAG,”Image path is: “+cursor.getString(columnIndex));
return cursor.getString(columnIndex);

[/sourcecode]

Lastly, the last thing we’d like to do right here is onActivityResult();

protected void onActivityResult(int requestCode, int resultCode, Intent knowledge)
// usedView is a bool that checks is a view was destroyed and this was reused.
// if it wasn’t reused, this implies we create a brand new one.
if (requestCode == CAMERA_PIC_REQUEST)
attempt
Uri selectedImage = knowledge.getData();
getPath(selectedImage);
InputStream is;
is = getContentResolver().openInputStream(selectedImage);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//BitmapFactory.decodeStream(bis,null,opts);
BitmapFactory.decodeStream(is,null,opts);

//The new measurement we would like to scale to
remaining int REQUIRED_SIZE=200;

//Discover the right scale value. It ought to be the facility of two.
int scale=1;
whereas(opts.outWidth/scale/2>=REQUIRED_SIZE || opts.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;

Log.i(TAG,”Scale is: “+scale);
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
is = null;
System.gc();
InputStream is2 = getContentResolver().openInputStream(selectedImage);

Bitmap returnedImage = BitmapFactory.decodeStream(is2, null, opts);
Log.i(TAG,”Image width from bitmap: “+returnedImage.getWidth());
Log.i(TAG,”Image peak from bitmap: “+returnedImage.getHeight());
Log.i(TAG,”Creating another View”);
TouchView newView = new TouchView(this,mStyle,new BitmapDrawable(returnedImage),mViewsCount,1f);
newView.setImageLocation(getPath(selectedImage));
newView.setClickable(true);
// under is to ensure pink border is drawn on new chosen picture
newView.setmSelected(true);
mViewsArray.add(newView);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.addView(mViewsArray.get(mViewsCount));
newView.invalidate();
mViewsCount+=1;

catch(NullPointerException e)
//Do nothing
catch (FileNotFoundException e)
// TODO Auto-generated catch block
e.printStackTrace();

if (requestCode == GALLERY_PIC_REQUEST)
attempt
Uri selectedImage = knowledge.getData();
getPath(selectedImage);
InputStream is;
is = getContentResolver().openInputStream(selectedImage);
BitmapFactory.Choices opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//BitmapFactory.decodeStream(bis,null,opts);
BitmapFactory.decodeStream(is,null,opts);

//The brand new measurement we would like to scale to
last int REQUIRED_SIZE=200;

//Discover the right scale value. It must be the facility of 2.
int scale=1;
while(opts.outWidth/scale/2>=REQUIRED_SIZE || opts.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;

Log.i(TAG,”Scale is: “+scale);
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
is = null;
System.gc();
InputStream is2 = getContentResolver().openInputStream(selectedImage);

Bitmap returnedImage = BitmapFactory.decodeStream(is2, null, opts);
Log.i(TAG,”Image width from bitmap: “+returnedImage.getWidth());
Log.i(TAG,”Picture peak from bitmap: “+returnedImage.getHeight());
Log.i(TAG,”Creating one other View”);
TouchView newView = new TouchView(this,mStyle,new BitmapDrawable(returnedImage),mViewsCount,1f);
newView.setImageLocation(getPath(selectedImage));
newView.setClickable(true);
// under is to guarantee pink border is drawn on new selected picture
newView.setmSelected(true);
mViewsArray.add(newView);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.addView(mViewsArray.get(mViewsCount));
newView.invalidate();
mViewsCount+=1;
catch (FileNotFoundException e)

catch (NullPointerException e)

Now, this technique is longer than it needs to be. Each GALLERY request and PIC REQUEST use the identical algorithm. Nevertheless, I decided to separate them just in case certainly one of them wanted something totally different than the opposite. Or if I needed to change the dimensions from grabbing images from Gallery vs taking the picture.

The algorithm is this:

Uri selectedImage = knowledge.getData();
getPath(selectedImage);
InputStream is;
is = getContentResolver().openInputStream(selectedImage);
BitmapFactory.Choices opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//BitmapFactory.decodeStream(bis,null,opts);
BitmapFactory.decodeStream(is,null,opts);

//The brand new measurement we would like to scale to
remaining int REQUIRED_SIZE=200;

//Find the right scale value. It ought to be the facility of 2.
int scale=1;
whereas(opts.outWidth/scale/2>=REQUIRED_SIZE || opts.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;

Log.i(TAG,”Scale is: “+scale);
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
is = null;
System.gc();
InputStream is2 = getContentResolver().openInputStream(selectedImage);

Bitmap returnedImage = BitmapFactory.decodeStream(is2, null, opts);
Log.i(TAG,”Image width from bitmap: “+returnedImage.getWidth());
Log.i(TAG,”Picture peak from bitmap: “+returnedImage.getHeight());
Log.i(TAG,”Creating one other View”);
TouchView newView = new TouchView(this,mStyle,new BitmapDrawable(returnedImage),mViewsCount,1f);
newView.setImageLocation(getPath(selectedImage));
newView.setClickable(true);
// under is to ensure pink border is drawn on new selected picture
newView.setmSelected(true);
mViewsArray.add(newView);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.addView(mViewsArray.get(mViewsCount));
newView.invalidate();
mViewsCount+=1;

First I get the Uri of the image.I shortly discovered that I used to be operating out of reminiscence after loading only 3 images because I was loading the images to reminiscence and it was exceeding my VM Finances (they have been 8 megapixel images). Following this: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

I created a scaled model of my images which drastically decreased the memory utilization. You’ll be able to read about it, however the primary half for this was:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//BitmapFactory.decodeStream(bis,null,opts);
BitmapFactory.decodeStream(is,null,opts);

//The brand new measurement we would like to scale to
ultimate int REQUIRED_SIZE=200;

//Discover the right scale value. It must be the facility of 2.
int scale=1;
whereas(opts.outWidth/scale/2>=REQUIRED_SIZE || opts.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;

Log.i(TAG,”Scale is: “+scale);
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
is = null;
System.gc();
InputStream is2 = getContentResolver().openInputStream(selectedImage);

Bitmap returnedImage = BitmapFactory.decodeStream(is2, null, opts);

I gained’t go over the small print as you’ll be able to read about it on the link above, but this was an awesome optimization to my code.

Finally, As soon as I’ve the bitmap I exploit BitmapDrawable to move a Drawable from a Bitmap to the constructor of TouchView together with other values. I then call my RelativeLayout and add the view to relative format (vs within the mTrashButton removes the view from my relative format). This all happens here:

Bitmap returnedImage = BitmapFactory.decodeStream(is2, null, opts);
Log.i(TAG,”Image width from bitmap: “+returnedImage.getWidth());
Log.i(TAG,”Picture peak from bitmap: “+returnedImage.getHeight());
Log.i(TAG,”Creating another View”);
TouchView newView = new TouchView(this,mStyle,new BitmapDrawable(returnedImage),mViewsCount,1f);
newView.setImageLocation(getPath(selectedImage));
newView.setClickable(true);
// under is to ensure purple border is drawn on new selected picture
newView.setmSelected(true);
mViewsArray.add(newView);
RelativeLayout format = (RelativeLayout) findViewById(R.id.style_layout);
format.addView(mViewsArray.get(mViewsCount));
newView.invalidate();
mViewsCount+=1;

And that’s it! You now have a primary library to use in your purposes. Ought to save you a variety of time.