In my android app, I have a crop image. So, I programming a custom view as my crop box. I can move the crop box. But my problem is how can I drag border of the crop box and change width and height of it. How can I do it?
Attr Class:
public class Attr {
public static final float CROP_BOX_START_X = 5;
public static final float CROP_BOX_START_Y = 5;
public static final float CROP_BOX_END_X = 305;
public static final float CROP_BOX_END_Y = 105;
}
CropBox Class:
public class CropBox extends View {
private Paint paint = new Paint();
public CropBox(Context context) {
super(context);
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
float[][] circleXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2},
{Attr.CROP_BOX_END_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2}
};
float[][] lineXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y}
};
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1);
for(int i = 0 ; i < circleXY.length ; i++)
canvas.drawCircle(circleXY[i][0], circleXY[i][1], 5, paint);
paint.setStrokeWidth(2);
for(int i = 0 ; i < lineXY.length ; i++)
canvas.drawLine(lineXY[i][0], lineXY[i][2], lineXY[i][2], lineXY[i][3], paint);
}
}
CropTestActivity Class:
public class CropTestActivity extends Activity {
private ImageView imageView;
private CropBox cropBox;
private RelativeLayout relativeLayout;
private RelativeLayout.LayoutParams layoutParams;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
layoutParams.leftMargin = imageView.getWidth() / 2 - (int)((Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2) + imageView.getLeft();
layoutParams.topMargin = imageView.getHeight() / 2 - (int)((Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2) + imageView.getTop();
}
});
relativeLayout.addView(cropBox, layoutParams);
cropBox.setOnTouchListener(new Crop(imageView));
}
}
Crop Class:
public class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private float cropBoxStartX = Attr.CROP_BOX_START_X;
private float cropBoxStartY = Attr.CROP_BOX_START_Y;
private float cropBoxEndX = Attr.CROP_BOX_END_X;
private float cropBoxEndY = Attr.CROP_BOX_END_Y;
private ImageView imageView;
private PointF start = new PointF();
public Crop(ImageView imageView) {
this.imageView = imageView;
}
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if(event.getX() > cropBoxStartX && event.getX() < cropBoxEndX && event.getY() > cropBoxStartY && event.getY() < cropBoxEndY)
mode = BOX_DRAG;
else if(event.getX() == cropBoxStartX || event.getX() == cropBoxEndX || event.getY() == cropBoxStartY || event.getY() == cropBoxEndY)
mode = BORDER_DRAG;
else
mode = NONE;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
while(layoutParams.topMargin + 5 < imageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + (cropBoxEndX - cropBoxStartX + 5) > imageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + (cropBoxEndY - cropBoxStartY + 5) > imageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < imageView.getLeft())
layoutParams.leftMargin++;
}
else if(mode == BORDER_DRAG) {
}
break;
}
view.setLayoutParams(layoutParams);
return true;
}
}
Layout XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/crop_test_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/android_image"
android:src="#drawable/android"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:contentDescription="#string/android_image_description" >
</ImageView>
</RelativeLayout>
Before Resize:
After Resize:
Thanks for your help.
Following is the solution,
Modified onCreate from Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this, imageView);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
relativeLayout.addView(cropBox);
}
Modified CropBox class:
public class CropBox extends View {
private static final int CROP_BOX_START_X = 5;
private static final int CROP_BOX_START_Y = 5;
private static final int CROP_BOX_END_X = 305;
private static final int CROP_BOX_END_Y = 105;
private static final int DRAG_SQUARE = 75;
public ImageView mImageView;
boolean mIsFirstClick = false;
private Paint paint = new Paint();
private Rect mRect;
public CropBox(Context context, ImageView aBaseView) {
super(context);
mImageView = aBaseView;
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y, CROP_BOX_END_X, CROP_BOX_END_Y);
setOnTouchListener(new Crop());
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
paint.setStrokeWidth(2);
paint.setColor(Color.CYAN);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mRect, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right, mRect.bottom-DRAG_SQUARE, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right-DRAG_SQUARE, mRect.bottom, paint);
}
class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private PointF start = new PointF();
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if((event.getX() <= mRect.right && event.getX() >=(mRect.right - DRAG_SQUARE))
&& (event.getY() >= mRect.top && event.getY() >=(mRect.bottom - DRAG_SQUARE))){
mode = BORDER_DRAG;
mIsFirstClick = false;
}
else if(mRect.contains((int)event.getX(), (int)event.getY())) {
mode = BOX_DRAG;
if (mIsFirstClick){
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y,
CROP_BOX_END_X, CROP_BOX_END_Y);
mIsFirstClick = false;
} else {
mIsFirstClick = true;
}
}
else{
mode = NONE;
mIsFirstClick = true;
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
mIsFirstClick = false;
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
}
else if(mode == BORDER_DRAG) {
if (event.getX() > view.getLeft() && event.getY() > view.getTop()){
mRect.right = (int) event.getX();
mRect.bottom = (int) event.getY();
}
}
while(layoutParams.topMargin + 5 < mImageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + mRect.right > mImageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + mRect.bottom > mImageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < mImageView.getLeft())
layoutParams.leftMargin++;
break;
}
view.setLayoutParams(layoutParams);
invalidate();
return true;
}
}
}
Some points I would like to mention.
Merged Attr and Crop in CropBox
No need of creating a rectangle from lines. You can use Rect.
Never initialize an array/object in Draw method
Added a feature: if double touched on rectangle it returns to original position
There might be some hitches about the restricting the rect in imageview. I am sure you can fix those... :)
Other than this there is another interesting way using scaling of canvas Image in Canvas with touch events
Use that class instead of Cropbox and try it.
Hope it helps..
Related
I try to make image editor application. in this application i try to put multiple sticker on image that sticker can move , zoom , rotate, and also selected sticker can erase . can anyone help me ? here is code that i done with sticker move,rotate,zoom but i cant erase selected sticker .
public class MainActivity extends AppCompatActivity {
Bitmap originalBitmap,originalBitmap1;
RelativeLayout rlImageViewContainer;
private ImageView ivRedo;
private ImageView ivUndo;
Button btn_erase;
private ArrayList<Path> paths;
private ArrayList<Path> redoPaths;
Paint destPaint = new Paint();
Path destPath = new Path();
Bitmap destBitmap;
Canvas destCanvas = new Canvas();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a);
originalBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.b);
initView();
}
private void initView() {
ivUndo = (ImageView) findViewById(R.id.iv_undo);
ivRedo = (ImageView) findViewById(R.id.iv_redo);
rlImageViewContainer = findViewById(R.id.rl_image_view_container);
final PhotoSortrView photoSortrView = new PhotoSortrView(this);
rlImageViewContainer.addView(photoSortrView,0);
photoSortrView.setBackgroundColor(getResources().getColor(R.color.tran));
photoSortrView.addImages(this, originalBitmap);
photoSortrView.addImages(this, originalBitmap1);
ivUndo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// undo();
}
});
ivRedo.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// redo();
}
});
btn_erase = (Button) findViewById(R.id.btn_erase);
btn_erase.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (photoSortrView.isErase) {
btn_erase.setBackgroundColor(Color.RED);
photoSortrView.isErase = false;
photoSortrView.isMove = true;
} else {
btn_erase.setBackgroundColor(Color.GREEN);
photoSortrView.isErase = true;
photoSortrView.isMove = false;
}
}
});
}
}
PhotoSortrView.java
public class PhotoSortrView extends View implements MultiTouchController.MultiTouchObjectCanvas<MultiTouchEntity> {
private static final String TAG = "PhotoSortr ####### ";
// private static final int[] IMAGES = { R.drawable.m74hubble };
private Path drawPath = new Path();
// drawing and canvas paint
private Paint drawPaint = new Paint(), canvasPaint = new Paint();
// initial color
private int paintColor = Color.TRANSPARENT;
// canvas
private Canvas drawCanvas = new Canvas();
// canvas bitmap
private Bitmap canvasBitmap;
private ArrayList<MultiTouchEntity> imageIDs = new ArrayList<MultiTouchEntity>();
// --
private MultiTouchController<MultiTouchEntity> multiTouchController = new MultiTouchController<MultiTouchEntity>(this);
// --
private MultiTouchController.PointInfo currTouchPoint = new MultiTouchController.PointInfo();
private static final int UI_MODE_ROTATE = 1, UI_MODE_ANISOTROPIC_SCALE = 2;
private int mUIMode = UI_MODE_ROTATE;
// --
private static final float SCREEN_MARGIN = 100;
private int displayWidth, displayHeight;
public boolean isErase = false;
public boolean isMove = true;
Paint destPaint = new Paint();
Path destPath = new Path();
Bitmap destBitmap;
Canvas destCanvas = new Canvas();
int height,width;
// ---------------------------------------------------------------------------------------------------
public PhotoSortrView(Context context) {
this(context, null);
setupPaint();
init(context);
}
private void setupPaint() {
destPaint.setAlpha(0);
destPaint.setAntiAlias(true);
destPaint.setStyle(Paint.Style.STROKE);
destPaint.setStrokeJoin(Paint.Join.ROUND);
destPaint.setStrokeCap(Paint.Cap.ROUND);
destPaint.setStrokeWidth(20);
destPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public PhotoSortrView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
setupDrawing();
}
public PhotoSortrView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
Log.e(TAG, "init: " );
Resources res = context.getResources();
setBackgroundColor(Color.TRANSPARENT);
DisplayMetrics metrics = res.getDisplayMetrics();
this.displayWidth = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? Math
.max(metrics.widthPixels, metrics.heightPixels) : Math.min(
metrics.widthPixels, metrics.heightPixels);
this.displayHeight = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? Math
.min(metrics.widthPixels, metrics.heightPixels) : Math.max(
metrics.widthPixels, metrics.heightPixels);
}
public void addImages(Context context, Bitmap resourceId) {
Log.e(TAG, "addImages: " );
// setupDrawing();
Resources res = context.getResources();
imageIDs.add(new ImageEntity(resourceId, res));
float cx = SCREEN_MARGIN + (float)
(Math.random() * (displayWidth - 2 * SCREEN_MARGIN));
float cy = SCREEN_MARGIN + (float)
(Math.random() * (displayHeight - 2 * SCREEN_MARGIN));
imageIDs.get(imageIDs.size() - 1).load(context, cx, cy);
invalidate();
}
public void removeAllImages() {
imageIDs.removeAll(imageIDs);
invalidate();
}
public void removeImage() {
if (imageIDs.size() > 0) {
imageIDs.remove(imageIDs.size() - 1);
}
invalidate();
}
public int getCountImage() {
return imageIDs.size();
}
// ---------------------------------------------------------------------------------------------------
#Override
protected void onDraw(Canvas canvas) {
Log.e(TAG, "onDraw: " );
if (isErase){
Log.e(TAG, "onDraw: isErase" );
if (destBitmap != null){
destCanvas.drawPath(destPath,destPaint);
canvas.drawBitmap(destBitmap,0,0,null);
}
}
else if (isMove){
height = imageIDs.get(0).getHeight();
width = imageIDs.get(0).getWidth();
Log.e(TAG, "onDraw: isMove");
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath,drawPaint);
int n = imageIDs.size();
for (int i = 0; i < n; i++)
imageIDs.get(i).draw(canvas);
}
super.onDraw(canvas);
}
// ---------------------------------------------------------------------------------------------------
public void trackballClicked() {
mUIMode = (mUIMode + 1) % 3;
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//collegeActivity.set
int action = event.getAction();
Log.e(TAG, "onTouchEvent: " );
float touchX = event.getX();
float touchY = event.getY();
// respond to down, move and up events
if (isErase){
Log.e(TAG, "onTouchEvent: isErase" );
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
destPath.moveTo(touchX,touchY);
break;
case MotionEvent.ACTION_MOVE:
destPath.lineTo(touchX,touchY);
break;
}
} else if (isMove) {
Log.e(TAG, "onTouchEvent: isMove" );
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
return multiTouchController.onTouchEvent(event);
}
invalidate();
return true;
}
private void setupDrawing() {
Log.e(TAG, "setupDrawing: " );
// prepare for drawing and setup paint stroke properties
drawPath = new Path();
drawPaint.setAlpha(0);
drawPaint.setColor(0);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(10);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
// size assigned to view
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w > 0 && h > 0) {
Log.e(TAG, "onSizeChanged: " );
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
}
// update color
public void setColor(String newColor) {
Log.e(TAG, "setColor: " );
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
public MultiTouchEntity getDraggableObjectAtPoint(MultiTouchController.PointInfo pt) {
float x = pt.getX(), y = pt.getY();
int n = imageIDs.size();
for (int i = n - 1; i >= 0; i--) {
ImageEntity im = (ImageEntity) imageIDs.get(i);
if (im.containsPoint(x, y))
return im;
}
return null;
}
public void selectObject(MultiTouchEntity img, MultiTouchController.PointInfo touchPoint) {
currTouchPoint.set(touchPoint);
if (img != null) {
// Move image to the top of the stack when selected
drawPaint.setColor(Color.TRANSPARENT);
imageIDs.remove(img);
imageIDs.add(img);
destCanvas = new Canvas();
destBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
destCanvas.setBitmap(destBitmap);
destCanvas.drawBitmap(img.getBitmap(),0,0,null);
} else {
// Called with img == null when drag stops.
}
invalidate();
}
public void getPositionAndScale(MultiTouchEntity img, MultiTouchController.PositionAndScale objPosAndScaleOut) {
// requires averaging the two scale factors)
objPosAndScaleOut.set(img.getCenterX(), img.getCenterY(),
(mUIMode & UI_MODE_ANISOTROPIC_SCALE) == 0,
(img.getScaleX() + img.getScaleY()) / 2,
(mUIMode & UI_MODE_ANISOTROPIC_SCALE) != 0, img.getScaleX(),
img.getScaleY(), (mUIMode & UI_MODE_ROTATE) != 0,
img.getAngle());
}
/**
* Set the position and scale of the dragged/stretched image.
*/
public boolean setPositionAndScale(MultiTouchEntity img,
MultiTouchController.PositionAndScale newImgPosAndScale, MultiTouchController.PointInfo touchPoint) {
currTouchPoint.set(touchPoint);
boolean ok = img.setPos(newImgPosAndScale);
if (ok)
invalidate();
return ok;
}
public boolean pointInObjectGrabArea(MultiTouchController.PointInfo pt, MultiTouchEntity img) {
return false;
}
}
I used this to perform pinch zoom and drag.
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
#Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
#Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "onScale" + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
}
The problem that I am having with this is that when I zoom in, then drag, it snaps to the middle of the screen. I think the problem lies at:
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
I am not sure how to fix this, can anyone please help me?
I fixed it by changing onScale to:
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "mode:" + this.mode + ", onScale:" + scaleFactor);
if (this.lastScaleFactor == 0.0f || Math.signum(scaleFactor) == Math.signum(this.lastScaleFactor)) {
this.scale *= scaleFactor;
this.scale = Math.max(MIN_ZOOM, Math.min(this.scale, MAX_ZOOM));
this.lastScaleFactor = scaleFactor;
} else {
this.lastScaleFactor = 0.0f;
}
if (this.mSurfaceView != null) {
int orgWidth = getWidth();
int _width = (int) (((float) orgWidth) * this.scale);
int _height = (int) (((float) getHeight()) * this.scale);
LogUtil.b(new StringBuilder(String.valueOf(_width)).append(", ").append(_height).toString());
LayoutParams params = (LayoutParams) this.mSurfaceView.getLayoutParams();
params.height = _height;
params.width = _width;
this.mSurfaceView.setLayoutParams(params);
child().setScaleX(this.scale);
child().setScaleY(this.scale);
}
return true;
}
I have two questions.
1) I would ideally want this signature for my constructor of a class that extends View, because it works for any kind of layout.
public HexagonView(Context context, float radius, PointF origin, int background, Layout layout) {
But, this cannot work because I have to call the constructor with RelativeLayout like so:
HexagonView hexagon = new HexagonView(this, 75, new PointF(200,300), Color.BLUE, (RelativeLayout) findViewById(R.id.activity_main));
Can this be changed to accept multiple layouts? For example, if(layout instanceof RelativeLayout) then cast it. (That doesn't seem to work)
2) The reason why I am passing the layout is so I can later add views easily to that same parent layout. I have a feeling that this isn't a safe programming practice. Is this okay, or should I be going about this a different way?
Edit: I think I figured out my first question about why I couldn't pass it. I believe it is because I assumed LinearLayout and RelativeLayout extended Layout, but in fact they extend ViewGroup
To capture the signature you have to take a linear or relative layout, you capture its signature in a bitmap by creating the drawing cache enabled true and then get its drawing cache for the layout and store it in a bitmap and then display that bitmap in the imageview.
You can take help from the following code
First create an inner class signature which extends the View
public class signature extends View {
private static final float STROKE_WIDTH = 5f;
private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
private Paint paint = new Paint();
private Path path = new Path();
private float lastTouchX;
private float lastTouchY;
private final RectF dirtyRect = new RectF();
public signature(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(STROKE_WIDTH);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
lastTouchX = eventX;
lastTouchY = eventY;
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(eventX, eventY);
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
path.lineTo(historicalX, historicalY);
}
path.lineTo(eventX, eventY);
break;
default:
debug("Ignored touch event: " + event.toString());
return false;
}
invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = eventX;
lastTouchY = eventY;
return true;
}
private void debug(String string) {
}
private void expandDirtyRect(float historicalX, float historicalY) {
if (historicalX < dirtyRect.left) {
dirtyRect.left = historicalX;
} else if (historicalX > dirtyRect.right) {
dirtyRect.right = historicalX;
}
if (historicalY < dirtyRect.top) {
dirtyRect.top = historicalY;
} else if (historicalY > dirtyRect.bottom) {
dirtyRect.bottom = historicalY;
}
}
private void resetDirtyRect(float eventX, float eventY) {
dirtyRect.left = Math.min(lastTouchX, eventX);
dirtyRect.right = Math.max(lastTouchX, eventX);
dirtyRect.top = Math.min(lastTouchY, eventY);
dirtyRect.bottom = Math.max(lastTouchY, eventY);
}
}
In the main activity create an object of the class signature like
signature sign;
mLinearSign.addView(sign, LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
and when you want in your program to capture the signature follow the following lines of code
ImageView ImgVwSign;
Bitmap sign = Bitmap.createBitmap(480, 800,Bitmap.Config.ARGB_8888);
mLinearSign.setDrawingCacheEnabled(true);
sign =mContent.getDrawingCache();
ImgVwSign.setImageBitmap(sign );
I would like to include a color picker in my paint program. So anyone here has already done something like this please give me some tutorials or piece of code to get me started. I really need to get the whole idea of adding this. I already have set up the canvas for the drawing so I'd like to add the color picker to it. Any ideas are welcome. Thanks.
Your class should implement ColorPickerDialog.OnColorChangedListener
public class MainActivity implements ColorPickerDialog.OnColorChangedListener
{
private Paint mPaint;
mPaint = new Paint();
// on button click
new ColorPickerDialog(this, this, mPaint.getColor()).show();
}
ColorPicker Dialog
public class ColorPickerDialog extends Dialog {
public interface OnColorChangedListener {
void colorChanged(int color);
}
private OnColorChangedListener mListener;
private int mInitialColor;
private static class ColorPickerView extends View {
private Paint mPaint;
private Paint mCenterPaint;
private final int[] mColors;
private OnColorChangedListener mListener;
ColorPickerView(Context c, OnColorChangedListener l, int color) {
super(c);
mListener = l;
mColors = new int[] {
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
0xFFFFFF00, 0xFFFF0000
};
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(s);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(32);
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setColor(color);
mCenterPaint.setStrokeWidth(5);
}
private boolean mTrackingCenter;
private boolean mHighlightCenter;
#Override
protected void onDraw(Canvas canvas) {
float r = CENTER_X - mPaint.getStrokeWidth()*0.5f;
canvas.translate(CENTER_X, CENTER_X);
canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
if (mTrackingCenter) {
int c = mCenterPaint.getColor();
mCenterPaint.setStyle(Paint.Style.STROKE);
if (mHighlightCenter) {
mCenterPaint.setAlpha(0xFF);
} else {
mCenterPaint.setAlpha(0x80);
}
canvas.drawCircle(0, 0,
CENTER_RADIUS + mCenterPaint.getStrokeWidth(),
mCenterPaint);
mCenterPaint.setStyle(Paint.Style.FILL);
mCenterPaint.setColor(c);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(CENTER_X*2, CENTER_Y*2);
}
private static final int CENTER_X = 100;
private static final int CENTER_Y = 100;
private static final int CENTER_RADIUS = 32;
private int floatToByte(float x) {
int n = java.lang.Math.round(x);
return n;
}
private int pinToByte(int n) {
if (n < 0) {
n = 0;
} else if (n > 255) {
n = 255;
}
return n;
}
private int ave(int s, int d, float p) {
return s + java.lang.Math.round(p * (d - s));
}
private int interpColor(int colors[], float unit) {
if (unit <= 0) {
return colors[0];
}
if (unit >= 1) {
return colors[colors.length - 1];
}
float p = unit * (colors.length - 1);
int i = (int)p;
p -= i;
// now p is just the fractional part [0...1) and i is the index
int c0 = colors[i];
int c1 = colors[i+1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
return Color.argb(a, r, g, b);
}
private int rotateColor(int color, float rad) {
float deg = rad * 180 / 3.1415927f;
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
ColorMatrix cm = new ColorMatrix();
ColorMatrix tmp = new ColorMatrix();
cm.setRGB2YUV();
tmp.setRotate(0, deg);
cm.postConcat(tmp);
tmp.setYUV2RGB();
cm.postConcat(tmp);
final float[] a = cm.getArray();
int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);
return Color.argb(Color.alpha(color), pinToByte(ir),
pinToByte(ig), pinToByte(ib));
}
private static final float PI = 3.1415926f;
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - CENTER_X;
float y = event.getY() - CENTER_Y;
boolean inCenter = java.lang.Math.sqrt(x*x + y*y) <= CENTER_RADIUS;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTrackingCenter = inCenter;
if (inCenter) {
mHighlightCenter = true;
invalidate();
break;
}
case MotionEvent.ACTION_MOVE:
if (mTrackingCenter) {
if (mHighlightCenter != inCenter) {
mHighlightCenter = inCenter;
invalidate();
}
} else {
float angle = (float)java.lang.Math.atan2(y, x);
// need to turn angle [-PI ... PI] into unit [0....1]
float unit = angle/(2*PI);
if (unit < 0) {
unit += 1;
}
mCenterPaint.setColor(interpColor(mColors, unit));
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mTrackingCenter) {
if (inCenter) {
mListener.colorChanged(mCenterPaint.getColor());
}
mTrackingCenter = false; // so we draw w/o halo
invalidate();
}
break;
}
return true;
}
}
public ColorPickerDialog(Context context,
OnColorChangedListener listener,
int initialColor) {
super(context);
mListener = listener;
mInitialColor = initialColor;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnColorChangedListener l = new OnColorChangedListener() {
public void colorChanged(int color) {
mListener.colorChanged(color);
dismiss();
}
};
setContentView(new ColorPickerView(getContext(), l, mInitialColor));
setTitle("Pick a Color");
}
You have to choose the color and click the center circle to pick the color. Set the color to your paint object and use the same to draw.
Snap shot
Edit 2:
Source code can be found at https://code.google.com/p/android-color-picker/
Another ColorPickerDialog
public class ColorPickerDialog extends AlertDialog implements
ColorPickerView.OnColorChangedListener {
private ColorPickerView mColorPicker;
private ColorPanelView mOldColor;
private ColorPanelView mNewColor;
private OnColorChangedListener mListener;
public ColorPickerDialog(Context myDrawingMenuOptionEventsListener, int initialColor) {
super(myDrawingMenuOptionEventsListener);
init(initialColor);
}
private void init(int color) {
// To fight color branding.
getWindow().setFormat(PixelFormat.RGBA_8888);
setUp(color);
}
private void setUp(int color) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.dialog_color_picker, null);
layout.setBackgroundColor(Color.WHITE);
setView(layout);
setTitle("Choose a Color");
// setIcon(android.R.drawable.ic_dialog_info);
mColorPicker = (ColorPickerView) layout
.findViewById(R.id.color_picker_view);
mOldColor = (ColorPanelView) layout.findViewById(R.id.old_color_panel);
mNewColor = (ColorPanelView) layout.findViewById(R.id.new_color_panel);
((LinearLayout) mOldColor.getParent()).setPadding(Math
.round(mColorPicker.getDrawingOffset()), 0, Math
.round(mColorPicker.getDrawingOffset()), 0);
mColorPicker.setOnColorChangedListener(this);
mOldColor.setColor(color);
mColorPicker.setColor(color, true);
}
#Override
public void onColorChanged(int color) {
mNewColor.setColor(color);
if (mListener != null) {
mListener.onColorChanged(color);
}
}
public void setAlphaSliderVisible(boolean visible) {
mColorPicker.setAlphaSliderVisible(visible);
}
public int getColor() {
return mColorPicker.getColor();
}
}
Usage :
final ColorPickerDialog d= new ColorPickerDialog(ActivityName.this,0xffffffff);
d.setAlphaSliderVisible(true);
d.setButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
mPaint.setColor(d.getColor());
}
});
d.setButton2("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
d.show();
Snap shot:
In the above choose the color on the right bar. You also make a choice how dark or light the color choosen should be. Click ok to set the paint to your paint object and use the same to draw. Cancel will dismiss the color picker dialog.
Edit 3:
Only change instead of clear function i have added color picker on click of clear button.
public class MainActivity extends Activity implements ColorPickerDialog.OnColorChangedListener {
DrawingView dv ;
RelativeLayout rl;
private Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter mBlur;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(R.layout.activity_main);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
rl = (RelativeLayout) findViewById(R.id.rl);
rl.addView(dv);
Button b = (Button) findViewById(R.id.button1);
//b.setText(R.string.France);
Button b1 = (Button) findViewById(R.id.button2);
rl.setDrawingCacheEnabled(true);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
// dv.clear();
new ColorPickerDialog(MainActivity.this, MainActivity.this, mPaint.getColor()).show();
}
});
b1.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
AlertDialog.Builder editalert = new AlertDialog.Builder(MainActivity.this);
editalert.setTitle("Please Enter the name with which you want to Save");
final EditText input = new EditText(MainActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT);
input.setLayoutParams(lp);
editalert.setView(input);
editalert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
rl.setDrawingCacheEnabled(true);
String name= input.getText().toString();
Bitmap bitmap =rl.getDrawingCache();
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root + "/MyDraw");
myDir.mkdirs();
File file = new File (myDir, name+".png");
if (file.exists ()) file.delete ();
try
{
if(!file.exists())
{
file.createNewFile();
}
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(CompressFormat.PNG, 10, ostream);
// System.out.println("saving......................................................"+path);
ostream.close();
rl.invalidate();
}
catch (Exception e)
{
e.printStackTrace();
}finally
{
rl.setDrawingCacheEnabled(false);
}
}
});
editalert.show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public class DrawingView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
public DrawingView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
canvas.drawPath(circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
invalidate();
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
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;
}
}
#Override
public void colorChanged(int color) {
// TODO Auto-generated method stub
mPaint.setColor(color);
}
}
you have a google code which can help you
http://code.google.com/p/android-color-picker/
I used this link for color picker http://code.google.com/p/android-color-picker/
steps:
I've added some extra file(like:"android-mvn-push.gradle" ) you can skip that file in step 3
and also remove this "apply from: '../android-mvn-push.gradle' " from your bulid.gradle(Module:liabrary) file if your skipping step 3.
Go to the above link and download project as zip file and extract the zip.
Inside Extracted folder you'll see library folder.
Copy that folder into your android Applications main folder where it contains all its gradle n all... and also copy "android-mvn-push.gradle" file to your main project folder.
open bulid.gradle(Module:app) inside that add dependencies like this,
"compile project(':library')"
update Settings.Gradle with include ':app', ':library'
go to Tools>Android>Sync with Gradles and then it will show some error like build gradle not matching bla bla....
Click on that open file error option.
In your bulid.gradle(Module:liabrary) change this,
apply plugin: 'com.android.library'
android {
compileSdkVersion propCompileSdkVersion
buildToolsVersion propBuildToolsVersion
defaultConfig {
minSdkVersion propMinSdkVersion
targetSdkVersion propTargetSdkVersion
versionCode propVersionCode
versionName propVersionName
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
apply from: '../android-mvn-push.gradle'
To>>>>>>>>>>>
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
apply from: '../android-mvn-push.gradle'
Finally Tools>Android > Sync with gradle...
and Bingo....It Works ....!!!
I programming an android app that can drag image. Top and left of image in default mode is 0, 0. My question is how can I get top and left of visible image after drag.
Touch class:
public class Touch implements OnTouchListener {
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private static final float MIN_ZOOM = 1f;
private static final float MAX_ZOOM = 5f;
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
private PointF start = new PointF();
private PointF mid = new PointF();
private float[] values = new float[9];
private int mode = NONE;
private float dx;
private float dy;
private float matrixX = 0;
private float matrixY = 0;
private float width = 0;
private float height = 0;
private float oldDistance = 1f;
public boolean onTouch(View view, MotionEvent event) {
ImageView imageView = (ImageView)view;
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDistance = spacing(event);
if(oldDistance > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG) {
matrix.set(savedMatrix);
matrix.getValues(values);
matrixX = values[Matrix.MTRANS_X];
matrixY = values[Matrix.MTRANS_Y];
width = values[Matrix.MSCALE_X] * imageView.getDrawable().getIntrinsicWidth();
height = values[Matrix.MSCALE_Y] * imageView.getDrawable().getIntrinsicHeight();
dx = event.getX() - start.x;
dy = event.getY() - start.y;
if(matrixY + dy > 0)
while(matrixY + dy > 0)
dy--;
if(matrixX + dx + width < imageView.getWidth())
while(matrixX + dx + width < imageView.getWidth())
dx++;
if(matrixY + dy + height < imageView.getHeight())
while(matrixY + dy + height < imageView.getHeight())
dy++;
if(matrixX + dx > 0)
while(matrixX + dx > 0)
dx--;
matrix.postTranslate(dx, dy);
}
else if(mode == ZOOM) {
float newDistance = spacing(event);
if(newDistance > 10f) {
matrix.set(savedMatrix);
float scale = newDistance / oldDistance;
matrix.getValues(values);
float currentScale = values[Matrix.MSCALE_X];
if(scale * currentScale > MAX_ZOOM)
scale = MAX_ZOOM / currentScale;
else if (scale * currentScale < MIN_ZOOM)
scale = MIN_ZOOM / currentScale;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
imageView.setImageMatrix(matrix);
return true;
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
point.set((event.getX(0) + event.getX(1)) / 2, (event.getY(0) + event.getY(1)) / 2);
}
}
Activity class:
public class RepositionTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reposition_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
imageView.setOnTouchListener(new Touch());
}
}
Layout xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/reposition_test_layout"
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/android_image"
android:src="#drawable/android"
android:layout_width="300dp"
android:layout_height="300dp"
android:scaleType="matrix"
android:contentDescription="#string/android_image_description" >
</ImageView>
</LinearLayout>
Thanks for your help.
I finally solve the problem. When the user reposition the image, then we can save the matrix information in a file or db.
Activity class:
public class RepositionTestActivity extends Activity {
private ImageView imageView;
private Button button;
private SQLiteDatabase db;
private Cursor cursor;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reposition_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
button = (Button)findViewById(R.id.reposition_button);
ViewTreeObserver viewTreeObserver = imageView.getViewTreeObserver();
if(viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
db = openOrCreateDatabase("DB_DATING", MODE_PRIVATE, null);
db.execSQL("" +
"CREATE TABLE IF NOT EXISTS tbl_images (" +
" id INTEGER PRIMARY KEY AUTOINCREMENT," +
" name VARCHAR(50) UNIQUE NOT NULL," +
" scale_x FLOAT NOT NULL," +
" scale_y FLOAT NOT NULL," +
" trans_x FLOAT NOT NULL," +
" trans_y FLOAT NOT NULL" +
");");
cursor = db.query("tbl_images", new String[] {"name", "scale_x", "scale_y", "trans_x", "trans_y"}, "name = \"android\"", null, null, null, null);
imageView.setScaleType(ScaleType.MATRIX);
cursor.moveToNext();
if(cursor.getCount() > 0) {
Matrix matrix = new Matrix();
matrix.postScale(cursor.getFloat(1), cursor.getFloat(2));
matrix.postTranslate(cursor.getFloat(3), cursor.getFloat(4));
imageView.setImageMatrix(matrix);
}
imageView.setOnTouchListener(new Touch(imageView.getImageMatrix()));
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
float[] values = new float[9];
imageView.getImageMatrix().getValues(values);
ContentValues contentValues = new ContentValues();
contentValues.put("scale_x", values[Matrix.MSCALE_X]);
contentValues.put("scale_y", values[Matrix.MSCALE_Y]);
contentValues.put("trans_x", values[Matrix.MTRANS_X]);
contentValues.put("trans_y", values[Matrix.MTRANS_Y]);
if(cursor.getCount() > 0) {
long count = db.update("tbl_images", contentValues, "name = \"android\"", null);
Log.d("Update Count", "===========> " + count);
}
else {
contentValues.put("name", "android");
long count = db.insert("tbl_images", null, contentValues);
Log.d("Insert Count", "===========> " + count);
}
}
});
}
});
}
}
}