Zooming in on imageview in Android without quality loss - android

I need to pinch zoom and drag/pan an image, so i implemented a scaleimageview.
but the more i zoom in, the more blurry the image gets.
I'm searching almost 3 days now for a solution.
My code looks like this (i'm using Xamarin for Android):
using Android.Views;
using Android.Widget;
namespace test
{
public class ScaleImageViewGestureDetector : GestureDetector.SimpleOnGestureListener
{
private readonly ScaleImageView m_ScaleImageView;
public ScaleImageViewGestureDetector(ScaleImageView imageView)
{
m_ScaleImageView = imageView;
}
public override bool OnDown(MotionEvent e)
{
return true;
}
public override bool OnDoubleTap(MotionEvent e)
{
m_ScaleImageView.MaxZoomTo((int)e.GetX(), (int)e.GetY());
m_ScaleImageView.Cutting();
return true;
}
}
public class ScaleImageView : ImageView, View.IOnTouchListener
{
private Context m_Context;
private float m_MaxScale = 2.0f;
private Matrix m_Matrix;
private float[] m_MatrixValues = new float[9];
private int m_Width;
private int m_Height;
private int m_IntrinsicWidth;
private int m_IntrinsicHeight;
private float m_Scale;
private float m_MinScale;
private float m_PreviousDistance;
private int m_PreviousMoveX;
private int m_PreviousMoveY;
private bool m_IsScaling;
private GestureDetector m_GestureDetector;
public ScaleImageView(Context context, IAttributeSet attrs) :
base(context, attrs)
{
m_Context = context;
Initialize();
}
public ScaleImageView(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
m_Context = context;
Initialize();
}
public override void SetImageBitmap(Bitmap bm)
{
base.SetImageBitmap(bm);
this.Initialize();
}
public override void SetImageResource(int resId)
{
base.SetImageResource(resId);
this.Initialize();
}
private void Initialize()
{
this.SetScaleType(ScaleType.Matrix);
m_Matrix = new Matrix();
if (Drawable != null)
{
m_IntrinsicWidth = Drawable.IntrinsicWidth;
m_IntrinsicHeight = Drawable.IntrinsicHeight;
this.SetOnTouchListener(this);
}
m_GestureDetector = new GestureDetector(m_Context, new ScaleImageViewGestureDetector(this));
}
protected override bool SetFrame(int l, int t, int r, int b)//left top right bottom
{
m_Width = r - l;
m_Height = b - t;
Console.WriteLine ("test");
Console.WriteLine (m_Width.ToString ());
Console.WriteLine (m_Height.ToString ());
m_Matrix.Reset();
var r_norm = r - l;
m_Scale = (float)r_norm / (float)m_IntrinsicWidth;
ImageMatrix = m_Matrix;
double height = Resources.DisplayMetrics.HeightPixels;
double width = Resources.DisplayMetrics.WidthPixels;
float scaleFactor = (float)(height/m_IntrinsicHeight);
//don't take screen height, there's a tabbar on the bottom of the screen!
m_Matrix.PostTranslate( (float) ((width-m_IntrinsicWidth)/2.00f),(float) ((m_Height-m_IntrinsicHeight)/2.00f));
m_MinScale = scaleFactor;
ZoomTo(m_Scale, m_Width / 2, m_Height / 2);
Cutting();
return base.SetFrame(l, t, r, b);
}
private float GetValue(Matrix matrix, int whichValue)
{
matrix.GetValues(m_MatrixValues);
return m_MatrixValues[whichValue];
}
public float Scale
{
get { return this.GetValue(m_Matrix, Matrix.MscaleX); }
}
public float TranslateX
{
get { return this.GetValue(m_Matrix, Matrix.MtransX); }
}
public float TranslateY
{
get { return this.GetValue(m_Matrix, Matrix.MtransY); }
}
public void MaxZoomTo(int x, int y)
{
if (this.m_MinScale != this.Scale && (Scale - m_MinScale) > 0.1f)
{
var scale = m_MinScale / Scale;
ZoomTo(scale, x, y);
}
else
{
var scale = m_MaxScale / Scale;
ZoomTo(scale, x, y);
}
}
public void ZoomTo(float scale, int x, int y)
{
if (Scale * scale < m_MinScale)
{
scale = m_MinScale / Scale;
}
else
{
if (scale >= 1 && Scale * scale > m_MaxScale)
{
scale = m_MaxScale / Scale;
}
}
m_Matrix.PostScale(scale, scale);
//move to center
m_Matrix.PostTranslate(-(m_Width * scale - m_Width) / 2, -(m_Height * scale - m_Height) / 2);
//move x and y distance
m_Matrix.PostTranslate(-(x - (m_Width / 2)) * scale, 0);
m_Matrix.PostTranslate(0, -(y - (m_Height / 2)) * scale);
ImageMatrix = m_Matrix;
}
public void Cutting()
{
var width = (int)(m_IntrinsicWidth * Scale);
var height = (int)(m_IntrinsicHeight * Scale);
if (TranslateX < -(width - m_Width))
{
m_Matrix.PostTranslate(-(TranslateX + width - m_Width), 0);
}
if (TranslateX > 0)
{
m_Matrix.PostTranslate(-TranslateX, 0);
}
if (TranslateY < -(height - m_Height))
{
m_Matrix.PostTranslate(0, -(TranslateY + height - m_Height));
}
if (TranslateY > 0)
{
m_Matrix.PostTranslate(0, -TranslateY);
}
if (width < m_Width)
{
m_Matrix.PostTranslate((m_Width - width) / 2, 0);
}
if (height < m_Height)
{
m_Matrix.PostTranslate(0, (m_Height - height) / 2);
}
ImageMatrix = m_Matrix;
}
private float Distance(float x0, float x1, float y0, float y1)
{
var x = x0 - x1;
var y = y0 - y1;
return FloatMath.Sqrt(x * x + y * y);
}
private float DispDistance()
{
return FloatMath.Sqrt(m_Width * m_Width + m_Height * m_Height);
}
public override bool OnTouchEvent(MotionEvent e)
{
if (m_GestureDetector.OnTouchEvent(e))
{
m_PreviousMoveX = (int)e.GetX();
m_PreviousMoveY = (int)e.GetY();
return true;
}
var touchCount = e.PointerCount;
switch (e.Action)
{
case MotionEventActions.Down:
case MotionEventActions.Pointer1Down:
case MotionEventActions.Pointer2Down:
{
if (touchCount >= 2)
{
var distance = this.Distance(e.GetX(0), e.GetX(1), e.GetY(0), e.GetY(1));
m_PreviousDistance = distance;
m_IsScaling = true;
}
}
break;
case MotionEventActions.Move:
{
if (touchCount >= 2 && m_IsScaling)
{
var distance = this.Distance(e.GetX(0), e.GetX(1), e.GetY(0), e.GetY(1));
var scale = (distance - m_PreviousDistance) / this.DispDistance();
m_PreviousDistance = distance;
scale += 1;
scale = scale * scale;
this.ZoomTo(scale, m_Width / 2, m_Height / 2);
this.Cutting();
}
else if (!m_IsScaling)
{
var distanceX = m_PreviousMoveX - (int)e.GetX();
var distanceY = m_PreviousMoveY - (int)e.GetY();
m_PreviousMoveX = (int)e.GetX();
m_PreviousMoveY = (int)e.GetY();
m_Matrix.PostTranslate(-distanceX, -distanceY);
this.Cutting();
}
}
break;
case MotionEventActions.Up:
case MotionEventActions.Pointer1Up:
case MotionEventActions.Pointer2Up:
{
if (touchCount <= 1)
{
m_IsScaling = false;
}
}
break;
}
return true;
}
public bool OnTouch(View v, MotionEvent e)
{
return OnTouchEvent(e);
}
}
}

Related

How to move and scale added on canvas bitmaps with MotionEvent.MOVE

I have custom image view that can be zoomed in and out. But also I have "canvas.drawBitmap" in onDraw method:
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (isPinShouldBeDrawn) {
Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pin_raster_64);
canvas.drawBitmap(marker, lastTouchX, lastTouchY, null);
}
isPinShouldBeDrawn = false;
}
In "onTouchEvent" method I can move the image:
#SuppressWarnings("deprecation")
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
if (mDetector.onTouchEvent(event)) {
return true;
}
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
if (touchCount >= 2) {
float distance = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
mPrevDistance = distance;
isScaling = true;
} else {
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
}
case MotionEvent.ACTION_MOVE:
if (touchCount >= 2 && isScaling) {
float dist = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
float scale = (dist - mPrevDistance) / dispDistance();
mPrevDistance = dist;
scale += 1;
scale = scale * scale;
zoomTo(scale, mWidth / 2, mHeight / 2);
cutting();
} else if (!isScaling) {
int distanceX = mPrevMoveX - (int) event.getX();
int distanceY = mPrevMoveY - (int) event.getY();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
mMatrix.postTranslate(-distanceX, -distanceY);
cutting();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_2_UP:
if (event.getPointerCount() <= 1) {
isScaling = false;
}
break;
}
return true;
}
But bitmaps from onDraw not moving. If i'm trying to save new bitmap, it's saving only visible area, and after that I can't zoom it at all or move; Save method:
public void save() {
Bitmap returnedBitmap = Bitmap.createBitmap(
getWidth(),
getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
draw(canvas);
setImageBitmap(returnedBitmap);
}
How to synchronized move and scale the image and added bitmaps? Please help!
Whole class:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import androidx.appcompat.widget.AppCompatImageView;
import com.signalsense.signalsenseapp.R;
public class ScaleImageView extends AppCompatImageView implements OnTouchListener {
static final float STROKE_WIDTH = 10f;
static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
public static Path path = new Path();
public static int imageheight, imagewidth;
private static Matrix mMatrix;
final RectF dirtyRect = new RectF();
private final Context mContext;
private final float MAX_SCALE = 2f;
private final float[] mMatrixValues = new float[9];
float lastTouchX;
float lastTouchY;
Paint paint = new Paint();
String TAG = "ScaleImageView";
private boolean isPinShouldBeDrawn = false;
// display width height.
private int mWidth;
private int mHeight;
private int mIntrinsicWidth;
private int mIntrinsicHeight;
private float mScale;
private float mMinScale;
private float mPrevDistance;
private boolean isScaling;
private int mPrevMoveX;
private int mPrevMoveY;
private GestureDetector mDetector;
public ScaleImageView(Context context, AttributeSet attr) {
super(context, attr);
this.mContext = context;
initialize();
}
public ScaleImageView(Context context) {
super(context);
this.mContext = context;
initialize();
}
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);
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.initialize();
}
#Override
public void setImageResource(int resId) {
super.setImageResource(resId);
this.initialize();
}
private void initialize() {
this.setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
Drawable d = getDrawable();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(STROKE_WIDTH);
if (d != null) {
mIntrinsicWidth = d.getIntrinsicWidth();
mIntrinsicHeight = d.getIntrinsicHeight();
setOnTouchListener(this);
}
mDetector = new GestureDetector(mContext,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
maxZoomTo((int) e.getX(), (int) e.getY());
cutting();
return super.onDoubleTap(e);
}
#Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
lastTouchX = e.getX();
lastTouchY = e.getY();
isPinShouldBeDrawn = true;
invalidate();
save();
}
});
}
#Override
protected boolean setFrame(int l, int t, int r, int b) {
mWidth = r - l;
mHeight = b - t;
mMatrix.reset();
int r_norm = r - l;
mScale = (float) r_norm / (float) mIntrinsicWidth;
int paddingHeight = 0;
int paddingWidth = 0;
// scaling vertical
if (mScale * mIntrinsicHeight > mHeight) {
mScale = (float) mHeight / (float) mIntrinsicHeight;
mMatrix.postScale(mScale, mScale);
paddingWidth = (r - mWidth) / 2;
paddingHeight = 0;
// scaling horizontal
} else {
mMatrix.postScale(mScale, mScale);
paddingHeight = (b - mHeight) / 2;
paddingWidth = 0;
}
mMatrix.postTranslate(paddingWidth, paddingHeight);
setImageMatrix(mMatrix);
mMinScale = mScale;
zoomTo(mScale, mWidth / 2, mHeight / 2);
cutting();
return super.setFrame(l, t, r, b);
}
protected float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
protected float getScale() {
return getValue(mMatrix, Matrix.MSCALE_X);
}
public float getTranslateX() {
return getValue(mMatrix, Matrix.MTRANS_X);
}
protected float getTranslateY() {
return getValue(mMatrix, Matrix.MTRANS_Y);
}
protected void maxZoomTo(int x, int y) {
if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
// threshold 0.1f
float scale = mMinScale / getScale();
zoomTo(scale, x, y);
} else {
float scale = MAX_SCALE / getScale();
zoomTo(scale, x, y);
}
}
public void zoomTo(float scale, int x, int y) {
if (getScale() * scale < mMinScale) {
return;
}
if (scale >= 1 && getScale() * scale > MAX_SCALE) {
return;
}
mMatrix.postScale(scale, scale);
// move to center
mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2,
-(mHeight * scale - mHeight) / 2);
// move x and y distance
mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
setImageMatrix(mMatrix);
}
public void cutting() {
int width = (int) (mIntrinsicWidth * getScale());
int height = (int) (mIntrinsicHeight * getScale());
imagewidth = width;
imageheight = height;
if (getTranslateX() < -(width - mWidth)) {
mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
}
if (getTranslateX() > 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
}
if (getTranslateY() < -(height - mHeight)) {
mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
}
if (getTranslateY() > 0) {
mMatrix.postTranslate(0, -getTranslateY());
}
if (width < mWidth) {
mMatrix.postTranslate((mWidth - width) / 2, 0);
}
if (height < mHeight) {
mMatrix.postTranslate(0, (mHeight - height) / 2);
}
setImageMatrix(mMatrix);
}
private float distance(float x0, float x1, float y0, float y1) {
float x = x0 - x1;
float y = y0 - y1;
return (float) Math.sqrt(x * x + y * y);
}
private float dispDistance() {
return (float) Math.sqrt(mWidth * mWidth + mHeight * mHeight);
}
public void clear() {
path.reset();
invalidate();
}
public void save() {
Bitmap returnedBitmap = Bitmap.createBitmap(
getWidth(),
getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
draw(canvas);
setImageBitmap(returnedBitmap);
}
#SuppressWarnings("deprecation")
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
if (mDetector.onTouchEvent(event)) {
return true;
}
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
if (touchCount >= 2) {
float distance = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
mPrevDistance = distance;
isScaling = true;
} else {
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
}
case MotionEvent.ACTION_MOVE:
if (touchCount >= 2 && isScaling) {
float dist = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
float scale = (dist - mPrevDistance) / dispDistance();
mPrevDistance = dist;
scale += 1;
scale = scale * scale;
zoomTo(scale, mWidth / 2, mHeight / 2);
cutting();
} else if (!isScaling) {
int distanceX = mPrevMoveX - (int) event.getX();
int distanceY = mPrevMoveY - (int) event.getY();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
mMatrix.postTranslate(-distanceX, -distanceY);
cutting();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_2_UP:
if (event.getPointerCount() <= 1) {
isScaling = false;
}
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (isPinShouldBeDrawn) {
Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pin_raster_64);
canvas.drawBitmap(marker, lastTouchX, lastTouchY, null);
}
isPinShouldBeDrawn = false;
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
return super.onTouchEvent(event);
}
}
Sorry for english, it's not my native language :)
Record normalized coordinates on touch events,
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
//lastTouchX = e.getX();
//lastTouchY = e.getY();
float[] touchCoords = new float[]{ e.getX(), e.getY()}; // Touch point in screen-X,Y coords.
Matrix matrix_inverse = new Matrix();
mMatrix.invert(matrix_inverse); // XY to UV mapping matrix.
matrix_inverse.mapPoints(touchCoords); // Touch point in bitmap-U,V coords.
lastTouchX = touchCoords[0];
lastTouchY = touchCoords[1];
isPinShouldBeDrawn = true;
invalidate();
save();
}
Then, draw the overlay bitmap by applying the transform.
protected void onDraw(Canvas canvas)
{
// TODO Auto-generated method stub
super.onDraw(canvas);
if (isPinShouldBeDrawn)
{
Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pin_raster_64);
//canvas.drawBitmap(marker, lastTouchX, lastTouchY, null);
Matrix matrix_marker = new Matrix();
matrix_marker.setTranslate(lastTouchX, lastTouchY);
matrix_marker.postConcat(mMatrix);
canvas.drawBitmap(marker, matrix_marker, null);
}
isPinShouldBeDrawn = false;
}
If you'd like to bake the overlays onto the bitmap, set the invert matrix to the canvas and call draw().
public void save()
{
Bitmap returnedBitmap = Bitmap.createBitmap(
mIntrinsicWidth,
mIntrinsicHeight,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
Matrix matrix_inverse = new Matrix();
mMatrix.invert(matrix_inverse);
canvas.setMatrix(matrix_inverse);
draw(canvas);
setImageBitmap(returnedBitmap);
}

Canvas getting wrong coordinates for finger drawing after pinch to zoom

I create canvas finger drawing app like line brush which is on https://play.google.com/store/apps/details?id=jp.naver.linebrush.android, I done pinch to zoom functionality but problem is after zoom in and zoom out, not drawing properly position on canvas area. For more understanding I share my View class for canvas drawing:
public class DrawingView extends View {
// Zooming Code 16-6-2015 4.12pm
private static final int INVALID_POINTER_ID = -1;
public Bitmap mMyChracter;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
View currentView;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private float focusX;
private float focusY;
private float lastFocusX = -1;
private float lastFocusY = -1;
static final int IMG_WIDTH = 640;
static final int IMG_HEIGHT = 480;
static final int IMAGE_X_POS = 560;
static final int IMAGE_Y_POS = 20;
float sy;
float sx;
public static Context context;
// -------------------------------------
// ...................................
/*private final Bitmap bitmap;
private final int width;
private final int height;*/
private Matrix transform = new Matrix();
private Vector2D position = new Vector2D();
private float scale = 1;
private float angle = 0;
private TouchManager touchManager = new TouchManager(2);
private boolean isInitialized = false;
// Debug helpers to draw lines between the two touch points
private Vector2D vca = null;
private Vector2D vcb = null;
private Vector2D vpa = null;
private Vector2D vpb = null;
int mWidth;
int mHeight;
// ...............................
private final Paint mDefaultPaint;
private Paint mFillPaint;
float x, y;
private Canvas mLayerCanvas = new Canvas();
private Bitmap mLayerBitmap;
private Stack<DrawOp> mDrawOps = new Stack<>();
private Stack<DrawOp> mUndoOps = new Stack<>();
private SparseArray<DrawOp> mCurrentOps = new SparseArray<>(0);
// For Drag and Pan zoom Code initialization
private static float MIN_ZOOM = 1f;
private static float MAX_ZOOM = 2f;
private float scaleFactor = 1.f;
private static ScaleGestureDetector detector;
boolean mFlagDrawing;
private final Matrix mMatrix = new Matrix();
int y_old=0,y_new=0;int zoomMode=0;
float pinch_dist_old=0,pinch_dist_new=0;
int zoomControllerScale=1;//new and old pinch distance to determine Zoom scale
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
// We can be in one of these 3 states
static final int NONE = 0;
static final int PAN = 1;
static final int ZOOM = 2;
int mode = NONE;
private static final String TAG = "DebugTag";
// New Code
private Bitmap imgBitmap = null;
private int containerWidth;
private int containerHeight;
Paint background;
//Matrices will be used to move and zoom image
// Matrix matrix = new Matrix();
// Matrix savedMatrix = new Matrix();
// PointF start = new PointF();
float currentScale;
float curX;
float curY;
//We can be in one of these 3 states
// static final int NONE = 0;
// static final int DRAG = 1;
// static final int ZOOM = 2;
// int mode = NONE;
//For animating stuff
float targetX;
float targetY;
float targetScale;
float targetScaleX;
float targetScaleY;
float scaleChange;
float targetRatio;
float transitionalRatio;
float easing = 0.2f;
boolean isAnimating = false;
float scaleDampingFactor = 0.5f;
//For pinch and zoom
// float oldDist = 1f;
// PointF mid = new PointF();
private Handler mHandler = new Handler();
float minScale;
float maxScale = 8.0f;
float wpRadius = 25.0f;
float wpInnerRadius = 20.0f;
float screenDensity;
private GestureDetector gestureDetector;
public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
public static final int DEFAULT_SCALE_ORIGINAL = 1;
private int defaultScale;
private static final String EXTRA_EVENT_LIST = "event_list";
private static final String EXTRA_STATE = "instance_state";
private ArrayList<MotionEvent> eventList = new ArrayList<MotionEvent>(100);
public DrawingView(Context context) {
this(context, null, 0);
}
public DrawingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public DrawingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDefaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDefaultPaint.setStyle(Paint.Style.STROKE);
mDefaultPaint.setStrokeJoin(Paint.Join.ROUND);
mDefaultPaint.setStrokeCap(Paint.Cap.ROUND);
mDefaultPaint.setStrokeWidth(40);
mDefaultPaint.setColor(Color.GREEN);
/*mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFillPaint.setStyle(Paint.Style.STROKE);
mFillPaint.setStrokeJoin(Paint.Join.ROUND);
mFillPaint.setStrokeCap(Paint.Cap.ROUND);
mDefaultPaint.setStrokeWidth(40);
mFillPaint.setColor(Color.GREEN);*/
setFocusable(true);
setFocusableInTouchMode(true);
setBackgroundColor(Color.WHITE);
setLayerType(LAYER_TYPE_SOFTWARE, null);
setSaveEnabled(true);
// Code for Zoom start
// detector = new ScaleGestureDetector(getContext(), new ScaleListener());
// Code for Zoom finish
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
private static float getDegreesFromRadians(float angle) {
return (float)(angle * 180.0 / Math.PI);
}
// Single Touch Code
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
vca = null;
vcb = null;
vpa = null;
vpb = null;
final int pointerCount = MotionEventCompat.getPointerCount(event);
switch (MotionEventCompat.getActionMasked(event)) {
// switch(event.getAction()){
// switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// case MotionEventCompat.ACTION_POINTER_DOWN:
{
if (mFlagDrawing == true) {
/* final float xx = event.getX() / mScaleFactor;
final float yy = event.getY() / mScaleFactor;
mLastTouchX = xx;
mLastTouchY = yy;
mActivePointerId = event.getPointerId(0);*/
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
position.add(touchManager.moveDelta(0));
}
else {
if (touchManager.getPressCount() == 2) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
System.out.println("mFlagDrawing: " + mFlagDrawing);
// Code for Zoom start
// detector.onTouchEvent(event);
// mScaleDetector.onTouchEvent(event);
// Code for Zoom finish
} else if (mFlagDrawing == false) {
System.out.println("mFlagDrawing: " + mFlagDrawing);
for (int p = 0; p < pointerCount; p++) {
final int id = MotionEventCompat.getPointerId(event, p);
DrawOp current = new DrawOp(mDefaultPaint);
current.getPath().moveTo(event.getX(), event.getY());
mCurrentOps.put(id, current);
}
}
// mFlagZoom = true;
// }
}
break;
case MotionEvent.ACTION_MOVE: {
// for(int p = 0; p < pointerCount; p++){
if (mFlagDrawing == true) {
/* final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float xx = event.getX(pointerIndex) / mScaleFactor;
final float yy = event.getY(pointerIndex) / mScaleFactor;
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = xx - mLastTouchX;
final float dy = yy - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = xx;
mLastTouchY = yy;*/
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
position.add(touchManager.moveDelta(0));
// current.add(touchManager.moveDelta(0));
}
else {
if (touchManager.getPressCount() == 2) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
System.out.println("mFlagDrawing: " + mFlagDrawing);
// Code for Zoom start
// detector.onTouchEvent(event);
// mScaleDetector.onTouchEvent(event);
// Code for Zoom finish
} else if (mFlagDrawing == false) {
System.out.println("mFlagDrawing: " + mFlagDrawing);
final int id = MotionEventCompat.getPointerId(event, 0);
DrawOp current = mCurrentOps.get(id);
final int historySize = event.getHistorySize();
for (int h = 0; h < historySize; h++) {
x = event.getHistoricalX(h);
y = event.getHistoricalY(h);
current.getPath().lineTo(x, y);
}
x = MotionEventCompat.getX(event, 0);
y = MotionEventCompat.getY(event, 0);
current.getPath().lineTo(x, y);
// position.add(current.getPath());
// }
}
}
break;
case MotionEvent.ACTION_UP:
// case MotionEventCompat.ACTION_POINTER_UP:
// {
if (mFlagDrawing == true) {
// mActivePointerId = INVALID_POINTER_ID;
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
position.add(touchManager.moveDelta(0));
}
else {
if (touchManager.getPressCount() == 2) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
System.out.println("mFlagDrawing: " + mFlagDrawing);
// Code for Zoom start
// detector.onTouchEvent(event);
// mScaleDetector.onTouchEvent(event);
// Code for Zoom finish
} else if (mFlagDrawing == false) {
System.out.println("mFlagDrawing: " + mFlagDrawing);
for (int p = 0; p < pointerCount; p++) {
final int id = MotionEventCompat.getPointerId(event, p);
mDrawOps.push(mCurrentOps.get(id));
mCurrentOps.remove(id);
// }
updateLayer();
}
}
// }
break;
case MotionEvent.ACTION_CANCEL: {
if (mFlagDrawing == true) {
// mActivePointerId = INVALID_POINTER_ID;
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
position.add(touchManager.moveDelta(0));
}
else {
if (touchManager.getPressCount() == 2) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
System.out.println("mFlagDrawing: " + mFlagDrawing);
// Code for Zoom start
// detector.onTouchEvent(event);
// mScaleDetector.onTouchEvent(event);
// Code for Zoom finish
} else if (mFlagDrawing == false) {
System.out.println("mFlagDrawing: " + mFlagDrawing);
for (int p = 0; p < pointerCount; p++) {
mCurrentOps
.remove(MotionEventCompat.getPointerId(event, p));
}
}
// mFlagZoom = true;
// }
}
break;
/*case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = event.getX(newPointerIndex) / mScaleFactor;
mLastTouchY = event.getY(newPointerIndex) / mScaleFactor;
mActivePointerId = event.getPointerId(newPointerIndex);
}
break;
}*/
default:
return false;
}
invalidate();
return true;
}
#Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
/*mLayerBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mLayerCanvas.setBitmap(mLayerBitmap);*/
if(mLayerBitmap == null){
mLayerBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}else{
Bitmap temporary = Bitmap.createScaledBitmap(mLayerBitmap, w, h, true);
mLayerBitmap = temporary;
}
mLayerCanvas = new Canvas(mLayerBitmap);
// mLayerCanvas.rotate(90);
/* matrix.setTranslate(1.5f,1.5f);
matrix.postRotate(90, 2.5f, 2.5f);*/
// updateLayer();
/* //Reset the width and height. Will draw bitmap and change
containerWidth = w;
containerHeight = h;
if(mLayerBitmap != null) {
int imgHeight = mLayerBitmap.getHeight();
int imgWidth = mLayerBitmap.getWidth();
float scale;
int initX = 0;
int initY = 0;
if(defaultScale == DrawingView.DEFAULT_SCALE_FIT_INSIDE) {
if(imgWidth > containerWidth) {
scale = (float)containerWidth / imgWidth;
float newHeight = imgHeight * scale;
initY = (containerHeight - (int)newHeight)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(0, initY);
}
else {
scale = (float)containerHeight / imgHeight;
float newWidth = imgWidth * scale;
initX = (containerWidth - (int)newWidth)/2;
matrix.setScale(scale, scale);
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = scale;
minScale = scale;
}
else {
if(imgWidth > containerWidth) {
initY = (containerHeight - (int)imgHeight)/2;
matrix.postTranslate(0, initY);
}
else {
initX = (containerWidth - (int)imgWidth)/2;
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = 1.0f;
minScale = 1.0f;
}
invalidate();
}*/
}
private void updateLayer() {
mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
for (DrawOp drawOp : mDrawOps) {
if (drawOp != null) {
drawOp.draw(mLayerCanvas);
}
}
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isInEditMode()) {
return;
}
// if(mFlagDrawing == true){
if (!isInitialized) {
int w = getWidth();
int h = getHeight();
position.set(w / 2, h / 2);
isInitialized = true;
}
mWidth = mLayerBitmap.getWidth();
mHeight = mLayerBitmap.getHeight();
// Code for Zoom start
/* canvas.save();
canvas.scale(mScaleFactor, mScaleFactor, focusX, focusY);
canvas.translate(mPosX, mPosY);*/
// canvas.scale(scaleFactor, scaleFactor);
/*canvas.scale(mScaleFactor, mScaleFactor, super.getWidth() * 0.5f,
super.getHeight() * 0.5f);*/
// Code for Zoom finish
// canvas.save();
transform.reset();
transform.postTranslate(-mWidth / 2.0f, -mHeight / 2.0f);
// transform.postRotate(getDegreesFromRadians(angle));
transform.postScale(scale, scale);
transform.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(mLayerBitmap, transform, null);
// }
// else if(mFlagDrawing == false){
// canvas.drawBitmap(mLayerBitmap, 0, 0, null);
for (int i = 0; i < mCurrentOps.size(); i++) {
DrawOp current = mCurrentOps.valueAt(i);
if (current != null) {
current.draw(canvas);
}
}
// }
// Code for Zoom
// canvas.restore();
}
public void operationClear() {
mDrawOps.clear();
mUndoOps.clear();
mCurrentOps.clear();
updateLayer();
}
public void operationUndo() {
if (mDrawOps.size() > 0) {
mUndoOps.push(mDrawOps.pop());
updateLayer();
}
}
public void operationRedo() {
if (mUndoOps.size() > 0) {
mDrawOps.push(mUndoOps.pop());
updateLayer();
}
}
public void setPaintStrokeWidth(float widthPx) {
mDefaultPaint.setStrokeWidth(widthPx);
}
public void setPaintOpacity(int percent) {
int alphaValue = (int) Math.round(percent * (255.0 / 100.0));
mDefaultPaint.setColor(combineAlpha(mDefaultPaint.getColor(),
alphaValue));
}
public void setPaintColor(String color) {
mDefaultPaint.setColor(combineAlpha(Color.parseColor(color), mDefaultPaint.getAlpha()));
}
public void setPaintColor(int color) {
mDefaultPaint.setColor(combineAlpha(color, mDefaultPaint.getAlpha()));
}
public void setPaintMaskFilter(MaskFilter filter) {
mDefaultPaint.setMaskFilter(filter);
}
public void setPaintShader(BitmapShader shader) {
mDefaultPaint.setShader(shader);
}
public void setPaintColorFilter(ColorFilter colorFilter) {
mDefaultPaint.setColorFilter(colorFilter);
}
private static int combineAlpha(int color, int alpha) {
return (color & 0x00FFFFFF) | ((alpha & 0xFF) << 24);
}
private static class DrawOp {
private float x,y;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path mPath = new Path();
public DrawOp(Paint paint) {
reset(paint);
}
/*public void add(Vector2D moveDelta) {
// TODO Auto-generated method stub
this.x += moveDelta.getX();
this.y += moveDelta.getY();
}*/
void reset(Paint paint) {
mPath.reset();
update(paint);
}
void update(Paint paint) {
mPaint.set(paint);
}
void draw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
}
public Path getPath() {
return mPath;
}
/* public DrawOp add(DrawOp value) {
this.x += value.getX();
this.y += value.getY();
return this;
}*/
/*public float getX() {
return x;
}
public float getY() {
return y;
}*/
}
public void fillShapeColor(){
}
// Code for Zoom
/* private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
invalidate();
return true;
}
}*/
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// float x = detector.getFocusX();
// float y = detector.getFocusY();
lastFocusX = -1;
lastFocusY = -1;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
focusX = detector.getFocusX();
focusY = detector.getFocusY();
if (lastFocusX == -1)
lastFocusX = focusX;
if (lastFocusY == -1)
lastFocusY = focusY;
mPosX += (focusX - lastFocusX);
mPosY += (focusY - lastFocusY);
Log.v("Hi Zoom", "Factor:" + mScaleFactor);
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.5f, Math.min(mScaleFactor, 2.0f));
lastFocusX = focusX;
lastFocusY = focusY;
invalidate();
return true;
}
}
/* #Override
public Parcelable onSaveInstanceState()
{
System.out.println("save instance");
Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_STATE, super.onSaveInstanceState());
bundle.putParcelableArrayList(EXTRA_EVENT_LIST, eventList);
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state)
{
if (state instanceof Bundle)
{
Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(bundle.getParcelable(EXTRA_STATE));
eventList = bundle.getParcelableArrayList(EXTRA_EVENT_LIST);
if (eventList == null) {
eventList = new ArrayList<MotionEvent>(100);
}
for (MotionEvent event : eventList) {
// performTouchEvent(event);
}
return;
}
super.onRestoreInstanceState(state);
}*/
public void setDrawingFlag(boolean flag) {
// System.out.println("Before Set mFlag " + mFlagDrawing);
this.mFlagDrawing = flag;
// System.out.println("After Set mFlag " + mFlagDrawing);
}
public void moveCanvas(float x, float y) {
float dx = x - mLastTouchX;
float dy = y - mLastTouchY;
// Adjust for zoom factor. Otherwise, the user's finger moving 10 pixels
// at 200% zoom causes the image to slide 20 pixels instead of perfectly
// following the user's touch
dx /= (mScaleFactor * 2);
dy /= (mScaleFactor * 2);
mPosX += dx;
mPosY += dy;
// Log.v(TAG, "moving by " + dx + "," + dy + " mScaleFactor: " +
// mScaleFactor);
}
}
I found mistake and correct it by my self. Below is my code.
public void resetZoom() {
mPanX = mPanY = 0;
final Matrix m = new Matrix();
m.postScale(1f / DENSITY, 1f / DENSITY);
setZoom(m);
invalidate();
}
public void setZoomPosNoInval(float x, float y) {
mPanX = x;
mPanY = y;
}
public void setZoomPos(float x, float y) {
setZoomPosNoInval(x, y);
invalidate();
}
public void setZoomPosNoInval(float[] pos) {
setZoomPosNoInval(pos[0], pos[1]);
}
public void setZoomPos(float[] pos) {
setZoomPosNoInval(pos);
invalidate();
}
public float[] getZoomPos(float[] pos) {
if (pos == null)
pos = new float[2];
pos[0] = mPanX;
pos[1] = mPanY;
return pos;
}
public float getZoomPosX() {
return mPanX;
}
public float getZoomPosY() {
return mPanY;
}
public Matrix getZoom() {
return mZoomMatrix;
}
public Matrix getZoomInv() {
return mZoomMatrixInv;
}
public void setZoom(Matrix m) {
mZoomMatrix.set(m);
mZoomMatrix.invert(mZoomMatrixInv);
}

getting android. view.inflateException when I use zoomView for pageCurlView

please tell me how can i remove the exception. or give me idea to implement zoom view with curlview.....
my xml file is like this,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/relat">
<com.example.image.ZoomView
>
<com.example.image.PageCurlView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/dcgpagecurlPageCurlView1"
>
</com.example.image.PageCurlView>
my java file is
package com.example.image;
public class StandaloneExample extends Activity {
Button bt;LinearLayout lr;
/** Decoded bitmap image */
private Bitmap mBitmap;
private ZoomView zoomview;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.standalone_example);
View v1 = ((LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.standalone_example, null, false);
v1.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
zoomview.addView(v1);
lr = (LinearLayout) findViewById(R.id.relat);
lr.addView(zoomview);
}
#Override
public void onDestroy(){
super.onDestroy();
System.gc();
finish();
}
public void lockOrientationLandscape() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
public void lockOrientationPortrait() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public void lockOrientation( int orientation ) {
setRequestedOrientation(orientation);
}
}
i have implemented zoomView.java as described in a link
pagecurlview.java class is
public class PageCurlView extends View {
private final static String TAG = "PageCurlView";
private Paint mTextPaint;
private TextPaint mTextPaintShadow;
private int mCurlSpeed;
private int mUpdateRate;
private int mInitialEdgeOffset;
private int mCurlMode;
public static final int CURLMODE_SIMPLE = 0;
public static final int CURLMODE_DYNAMIC = 1;
private boolean bEnableDebugMode = false;
private WeakReference<Context> mContext;
private FlipAnimationHandler mAnimationHandler;
private float mFlipRadius;
private Vector2D mMovement;
private Vector2D mFinger;
private Vector2D mOldMovement;
private Paint mCurlEdgePaint;
private Vector2D mA, mB, mC, mD, mE, mF, mOldF, mOrigin;
private int mCurrentLeft, mCurrentTop;
private boolean bViewDrawn;
private boolean bFlipRight;
private boolean bFlipping;
private boolean bUserMoves;
private boolean bBlockTouchInput = false;
private boolean bEnableInputAfterDraw = false;
private Bitmap mForeground;
private Bitmap mBackground;
private ArrayList<Bitmap> mPages;
private int mIndex = 0;
private class Vector2D
{
public float x,y;
public Vector2D(float x, float y)
{
this.x = x;
this.y = y;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "("+this.x+","+this.y+")";
}
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
public float lengthSquared() {
return (x * x) + (y * y);
}
public boolean equals(Object o) {
if (o instanceof Vector2D) {
Vector2D p = (Vector2D) o;
return p.x == x && p.y == y;
}
return false;
}
public Vector2D reverse() {
return new Vector2D(-x,-y);
}
public Vector2D sum(Vector2D b) {
return new Vector2D(x+b.x,y+b.y);
}
public Vector2D sub(Vector2D b) {
return new Vector2D(x-b.x,y-b.y);
}
public float dot(Vector2D vec) {
return (x * vec.x) + (y * vec.y);
}
public float cross(Vector2D a, Vector2D b) {
return a.cross(b);
}
public float cross(Vector2D vec) {
return x * vec.y - y * vec.x;
}
public float distanceSquared(Vector2D other) {
float dx = other.x - x;
float dy = other.y - y;
return (dx * dx) + (dy * dy);
}
public float distance(Vector2D other) {
return (float) Math.sqrt(distanceSquared(other));
}
public float dotProduct(Vector2D other) {
return other.x * x + other.y * y;
}
public Vector2D normalize() {
float magnitude = (float) Math.sqrt(dotProduct(this));
return new Vector2D(x / magnitude, y / magnitude);
}
public Vector2D mult(float scalar) {
return new Vector2D(x*scalar,y*scalar);
}
}
class FlipAnimationHandler extends Handler {
#Override
public void handleMessage(Message msg) {
PageCurlView.this.FlipAnimationStep();
}
public void sleep(long millis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), millis);
}
}
public PageCurlView(Context context) {
super(context);
init(context);
ResetClipEdge();
}
public PageCurlView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
// Get the data from the XML AttributeSet
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageCurlView);
// Get data
bEnableDebugMode = a.getBoolean(R.styleable.PageCurlView_enableDebugMode, bEnableDebugMode);
mCurlSpeed = a.getInt(R.styleable.PageCurlView_curlSpeed, mCurlSpeed);
mUpdateRate = a.getInt(R.styleable.PageCurlView_updateRate, mUpdateRate);
mInitialEdgeOffset = a.getInt(R.styleable.PageCurlView_initialEdgeOffset, mInitialEdgeOffset);
mCurlMode = a.getInt(R.styleable.PageCurlView_curlMode, mCurlMode);
Log.i(TAG, "mCurlSpeed: " + mCurlSpeed);
Log.i(TAG, "mUpdateRate: " + mUpdateRate);
Log.i(TAG, "mInitialEdgeOffset: " + mInitialEdgeOffset);
Log.i(TAG, "mCurlMode: " + mCurlMode);
// recycle object (so it can be used by others)
a.recycle();
}
ResetClipEdge();
}
private final void init(Context context) {
// Foreground text paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
// The shadow
mTextPaintShadow = new TextPaint();
mTextPaintShadow.setAntiAlias(true);
mTextPaintShadow.setTextSize(16);
mTextPaintShadow.setColor(0x00000000);
// Cache the context
mContext = new WeakReference<Context>(context);
// Base padding
setPadding(3, 3, 3, 3);
// The focus flags are needed
setFocusable(true);
setFocusableInTouchMode(true);
mMovement = new Vector2D(0,0);
mFinger = new Vector2D(0,0);
mOldMovement = new Vector2D(0,0);
// Create our curl animation handler
mAnimationHandler = new FlipAnimationHandler();
// Create our edge paint
mCurlEdgePaint = new Paint();
mCurlEdgePaint.setColor(Color.WHITE);
mCurlEdgePaint.setAntiAlias(true);
mCurlEdgePaint.setStyle(Paint.Style.FILL);
mCurlEdgePaint.setShadowLayer(10, -5, 5, 0x99000000);
// Set the default props, those come from an XML :D
mCurlSpeed = 30;
mUpdateRate = 33;
mInitialEdgeOffset = 20;
mCurlMode = 1;
// LEGACY PAGE HANDLING!
// Create pages
mPages = new ArrayList<Bitmap>();
mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.princess));
mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.temp));
// Create some sample images
mForeground = mPages.get(0);
mBackground = mPages.get(1);
}
public void ResetClipEdge()
{
// Set our base movement
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
mOldMovement.x = 0;
mOldMovement.y = 0;
// Now set the points
// TODO: OK, those points MUST come from our measures and
// the actual bounds of the view!
mA = new Vector2D(mInitialEdgeOffset, 0);
mB = new Vector2D(this.getWidth(), this.getHeight());
mC = new Vector2D(this.getWidth(), 0);
mD = new Vector2D(0, 0);
mE = new Vector2D(0, 0);
mF = new Vector2D(0, 0);
mOldF = new Vector2D(0, 0);
// The movement origin point
mOrigin = new Vector2D(this.getWidth(), 0);
}
private Context GetContext() {
return mContext.get();
}
public boolean IsCurlModeDynamic()
{
return mCurlMode == CURLMODE_DYNAMIC;
}
public void SetCurlSpeed(int curlSpeed)
{
if ( curlSpeed < 1 )
throw new IllegalArgumentException("curlSpeed must be greated than 0");
mCurlSpeed = curlSpeed;
}
public int GetCurlSpeed()
{
return mCurlSpeed;
}
public void SetUpdateRate(int updateRate)
{
if ( updateRate < 1 )
throw new IllegalArgumentException("updateRate must be greated than 0");
mUpdateRate = updateRate;
}
public int GetUpdateRate()
{
return mUpdateRate;
}
public void SetInitialEdgeOffset(int initialEdgeOffset)
{
if ( initialEdgeOffset < 0 )
throw new IllegalArgumentException("initialEdgeOffset can not negative");
mInitialEdgeOffset = initialEdgeOffset;
}
public int GetInitialEdgeOffset()
{
return mInitialEdgeOffset;
}
public void SetCurlMode(int curlMode)
{
if ( curlMode != CURLMODE_SIMPLE &&
curlMode != CURLMODE_DYNAMIC )
throw new IllegalArgumentException("Invalid curlMode");
mCurlMode = curlMode;
}
public int GetCurlMode()
{
return mCurlMode;
}
public void SetEnableDebugMode(boolean bFlag)
{
bEnableDebugMode = bFlag;
}
public boolean IsDebugModeEnabled()
{
return bEnableDebugMode;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int finalWidth, finalHeight;
finalWidth = measureWidth(widthMeasureSpec);
finalHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(finalWidth, finalHeight);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = specSize;
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = specSize;
}
return result;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!bBlockTouchInput) {
// Get our finger position
mFinger.x = event.getX();
mFinger.y = event.getY();
int width = getWidth();
// Depending on the action do what we need to
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// If we moved over the half of the display flip to next
if (mOldMovement.x > (width >> 1)) {
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
// Set the right movement flag
bFlipRight = true;
} else {
// Set the left movement flag
bFlipRight = false;
// go to next previous page
previousView();
// Set new movement
mMovement.x = IsCurlModeDynamic()?width<<1:width;
mMovement.y = mInitialEdgeOffset;
}
break;
case MotionEvent.ACTION_UP:
bUserMoves=false;
bFlipping=true;
FlipAnimationStep();
break;
case MotionEvent.ACTION_MOVE:
bUserMoves=true;
// Get movement
mMovement.x -= mFinger.x - mOldMovement.x;
mMovement.y -= mFinger.y - mOldMovement.y;
mMovement = CapMovement(mMovement, true);
// Make sure the y value get's locked at a nice level
if ( mMovement.y <= 1 )
mMovement.y = 1;
// Get movement direction
if (mFinger.x < mOldMovement.x ) {
bFlipRight = true;
} else {
bFlipRight = false;
}
// Save old movement values
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// Force a new draw call
DoPageCurl();
this.invalidate();
break;
}
}
// TODO: Only consume event if we need to.
return true;
}
private Vector2D CapMovement(Vector2D point, boolean bMaintainMoveDir)
{
// Make sure we never ever move too much
if (point.distance(mOrigin) > mFlipRadius)
{
if ( bMaintainMoveDir )
{
// Maintain the direction
point = mOrigin.sum(point.sub(mOrigin).normalize().mult(mFlipRadius));
}
else
{
// Change direction
if ( point.x > (mOrigin.x+mFlipRadius))
point.x = (mOrigin.x+mFlipRadius);
else if ( point.x < (mOrigin.x-mFlipRadius) )
point.x = (mOrigin.x-mFlipRadius);
point.y = (float) (Math.sin(Math.acos(Math.abs(point.x-mOrigin.x)/mFlipRadius))*mFlipRadius);
}
}
return point;
}
public void FlipAnimationStep() {
if ( !bFlipping )
return;
int width = getWidth();
// No input when flipping
bBlockTouchInput = true;
// Handle speed
float curlSpeed = mCurlSpeed;
if ( !bFlipRight )
curlSpeed *= -1;
// Move us
mMovement.x += curlSpeed;
mMovement = CapMovement(mMovement, false);
// Create values
DoPageCurl();
// Check for endings :D
if (mA.x < 1 || mA.x > width - 1) {
bFlipping = false;
if (bFlipRight) {
//SwapViews();
nextView();
}
ResetClipEdge();
// Create values
DoPageCurl();
// Enable touch input after the next draw event
bEnableInputAfterDraw = true;
}
else
{
mAnimationHandler.sleep(mUpdateRate);
}
// Force a new draw call
this.invalidate();
}
private void DoPageCurl()
{
if(bFlipping){
if ( IsCurlModeDynamic() )
doDynamicCurl();
else
doSimpleCurl();
} else {
if ( IsCurlModeDynamic() )
doDynamicCurl();
else
doSimpleCurl();
}
}
private void doSimpleCurl() {
int width = getWidth();
int height = getHeight();
// Calculate point A
mA.x = width - mMovement.x;
mA.y = height;
// Calculate point D
mD.x = 0;
mD.y = 0;
if (mA.x > width / 2) {
mD.x = width;
mD.y = height - (width - mA.x) * height / mA.x;
} else {
mD.x = 2 * mA.x;
mD.y = 0;
}
double angle = Math.atan((height - mD.y) / (mD.x + mMovement.x - width));
double _cos = Math.cos(2 * angle);
double _sin = Math.sin(2 * angle);
mF.x = (float) (width - mMovement.x + _cos * mMovement.x);
mF.y = (float) (height - _sin * mMovement.x);
// If the x position of A is above half of the page we are still not
// folding the upper-right edge and so E and D are equal.
if (mA.x > width / 2) {
mE.x = mD.x;
mE.y = mD.y;
}
else
{
// So get E
mE.x = (float) (mD.x + _cos * (width - mD.x));
mE.y = (float) -(_sin * (width - mD.x));
}
}
private void doDynamicCurl() {
int width = getWidth();
int height = getHeight();
mF.x = width - mMovement.x+0.1f;
mF.y = height - mMovement.y+0.1f;
if(mA.x==0) {
mF.x= Math.min(mF.x, mOldF.x);
mF.y= Math.max(mF.y, mOldF.y);
}
// Get diffs
float deltaX = width-mF.x;
float deltaY = height-mF.y;
float BH = (float) (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2);
double tangAlpha = deltaY / deltaX;
double alpha = Math.atan(deltaY / deltaX);
double _cos = Math.cos(alpha);
double _sin = Math.sin(alpha);
mA.x = (float) (width - (BH / _cos));
mA.y = height;
mD.y = (float) (height - (BH / _sin));
mD.x = width;
mA.x = Math.max(0,mA.x);
if(mA.x==0) {
mOldF.x = mF.x;
mOldF.y = mF.y;
}
// Get W
mE.x = mD.x;
mE.y = mD.y;
// Correct
if (mD.y < 0) {
mD.x = width + (float) (tangAlpha * mD.y);
mE.y = 0;
mE.x = width + (float) (Math.tan(2 * alpha) * mD.y);
}
}
#Deprecated
private void SwapViews() {
Bitmap temp = mForeground;
mForeground = mBackground;
mBackground = temp;
}
private void nextView() {
int foreIndex = mIndex + 1;
if(foreIndex >= mPages.size()) {
foreIndex = 0;
}
int backIndex = foreIndex + 1;
if(backIndex >= mPages.size()) {
backIndex = 0;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
private void previousView() {
int backIndex = mIndex;
int foreIndex = backIndex - 1;
if(foreIndex < 0) {
foreIndex = mPages.size()-1;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
private void setViews(int foreground, int background) {
mForeground = mPages.get(foreground);
mBackground = mPages.get(background);
}
#Override
protected void onDraw(Canvas canvas) {
mCurrentLeft = getLeft();
mCurrentTop = getTop();
if ( !bViewDrawn ) {
bViewDrawn = true;
onFirstDrawEvent(canvas);
}
canvas.drawColor(Color.WHITE);
Rect rect = new Rect();
rect.left = 0;
rect.top = 0;
rect.bottom = getHeight();
rect.right = getWidth();
// First Page render
Paint paint = new Paint();
// Draw our elements
drawForeground(canvas, rect, paint);
drawBackground(canvas, rect, paint);
drawCurlEdge(canvas);
// Draw any debug info once we are done
if ( bEnableDebugMode )
drawDebug(canvas);
// Check if we can re-enable input
if ( bEnableInputAfterDraw )
{
bBlockTouchInput = false;
bEnableInputAfterDraw = false;
}
// Restore canvas
//canvas.restore();
}
protected void onFirstDrawEvent(Canvas canvas) {
mFlipRadius = getWidth();
ResetClipEdge();
DoPageCurl();
}
private void drawForeground( Canvas canvas, Rect rect, Paint paint ) {
canvas.drawBitmap(mForeground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
}
private Path createBackgroundPath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mB.x, mB.y);
path.lineTo(mC.x, mC.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mA.x, mA.y);
return path;
}
private void drawBackground( Canvas canvas, Rect rect, Paint paint ) {
Path mask = createBackgroundPath();
// Save current canvas so we do not mess it up
canvas.save();
canvas.clipPath(mask);
canvas.drawBitmap(mBackground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
canvas.restore();
}
private Path createCurlEdgePath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mE.x, mE.y);
path.lineTo(mF.x, mF.y);
path.lineTo(mA.x, mA.y);
return path;
}
private void drawCurlEdge( Canvas canvas )
{
Path path = createCurlEdgePath();
canvas.drawPath(path, mCurlEdgePaint);
}
private void drawPageNum(Canvas canvas, int pageNum)
{
mTextPaint.setColor(Color.WHITE);
String pageNumText = "- "+pageNum+" -";
drawCentered(canvas, pageNumText,canvas.getHeight()-mTextPaint.getTextSize()-5,mTextPaint,mTextPaintShadow);
}
public static void drawTextShadowed(Canvas canvas, String text, float x, float y, Paint textPain, Paint shadowPaint) {
canvas.drawText(text, x-1, y, shadowPaint);
canvas.drawText(text, x, y+1, shadowPaint);
canvas.drawText(text, x+1, y, shadowPaint);
canvas.drawText(text, x, y-1, shadowPaint);
canvas.drawText(text, x, y, textPain);
}
public static void drawCentered(Canvas canvas, String text, float y, Paint textPain, Paint shadowPaint)
{
float posx = (canvas.getWidth() - textPain.measureText(text))/2;
drawTextShadowed(canvas, text, posx, y, textPain, shadowPaint);
}
private void drawDebug(Canvas canvas)
{
float posX = 10;
float posY = 20;
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLACK);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
posY = debugDrawPoint(canvas,"A",mA,Color.RED,posX,posY);
posY = debugDrawPoint(canvas,"B",mB,Color.GREEN,posX,posY);
posY = debugDrawPoint(canvas,"C",mC,Color.BLUE,posX,posY);
posY = debugDrawPoint(canvas,"D",mD,Color.CYAN,posX,posY);
posY = debugDrawPoint(canvas,"E",mE,Color.YELLOW,posX,posY);
posY = debugDrawPoint(canvas,"F",mF,Color.LTGRAY,posX,posY);
posY = debugDrawPoint(canvas,"Mov",mMovement,Color.DKGRAY,posX,posY);
posY = debugDrawPoint(canvas,"Origin",mOrigin,Color.MAGENTA,posX,posY);
posY = debugDrawPoint(canvas,"Finger",mFinger,Color.GREEN,posX,posY);
}
private float debugDrawPoint(Canvas canvas, String name, Vector2D point, int color, float posX, float posY) {
return debugDrawPoint(canvas,name+" "+point.toString(),point.x, point.y, color, posX, posY);
}
private float debugDrawPoint(Canvas canvas, String name, float X, float Y, int color, float posX, float posY) {
mTextPaint.setColor(color);
drawTextShadowed(canvas,name,posX , posY, mTextPaint,mTextPaintShadow);
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(color);
canvas.drawPoint(X, Y, paint);
return posY+15;
}
}

Android pinch zoom imageview inside gallery

I'm creating lazy loaded photo gallery (like facebook)
i'm using cusotme gallery overriding Fling to the make single image paging
and using custume image view to make the pinch zoom
the result is that the image is working fine (zoom,move etc..) but i can't switch images
my code
SlowGallery:
public class SlowGallery extends Gallery {
public SlowGallery(Context context) {
super(context);
}
public SlowGallery(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlowGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2){
return e2.getX() > e1.getX();
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY){
Log.d("SlowGallery::onFling","onFinlg");
int kEvent;
if(isScrollingLeft(e1, e2)){ //Check if scrolling left
kEvent = KeyEvent.KEYCODE_DPAD_LEFT;
}
else{ //Otherwise scrolling right
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;
}
onKeyDown(kEvent, null);
return true;
}
}
ScaleImageView
public class ScaleImageView extends ImageView implements OnTouchListener {
private float MAX_SCALE = 2f;
private int DOUBLE_TAP_SECOND = 400;
private Matrix mMatrix;
private final float[] mMatrixValues = new float[9];
// display width height.
private int mWidth;
private int mHeight;
private int mIntrinsicWidth;
private int mIntrinsicHeight;
private float mScale;
private float mMinScale;
// double tap for determining
private long mLastTime = 0;
private boolean isDoubleTap;
private int mDoubleTapX;
private int mDoubleTapY;
private float mPrevDistance;
private boolean isScaling;
private int mPrevMoveX;
private int mPrevMoveY;
String TAG = "ScaleImageView";
public ScaleImageView(Context context, AttributeSet attr) {
super(context, attr);
initialize();
}
public ScaleImageView(Context context) {
super(context);
initialize();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.initialize();
}
private void initialize() {
this.setScaleType(ScaleType.MATRIX);
this.mMatrix = new Matrix();
Drawable d = getDrawable();
if (d != null) {
mIntrinsicWidth = d.getIntrinsicWidth();
mIntrinsicHeight = d.getIntrinsicHeight();
setOnTouchListener(this);
}
}
#Override
protected boolean setFrame(int l, int t, int r, int b) {
mWidth = r-l;
mHeight = b-t;
mMatrix.reset();
mScale = (float) r / (float) mIntrinsicWidth;
int paddingHeight = 0;
int paddingWidth = 0;
// scaling vertical
if (mScale * mIntrinsicHeight > mHeight) {
mScale = (float) mHeight / (float) mIntrinsicHeight;
mMatrix.postScale(mScale, mScale);
paddingWidth = (r - mWidth) / 2;
paddingHeight = 0;
// scaling horizontal
} else {
mMatrix.postScale(mScale, mScale);
paddingHeight = (b - mHeight) / 2;
paddingWidth = 0;
}
mMatrix.postTranslate(paddingWidth, paddingHeight);
setImageMatrix(mMatrix);
mMinScale = mScale;
zoomTo(mScale, mWidth / 2, mHeight / 2);
cutting();
return super.setFrame(l, t, r, b);
}
protected float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
protected float getScale() {
return getValue(mMatrix, Matrix.MSCALE_X);
}
protected float getTranslateX() {
return getValue(mMatrix, Matrix.MTRANS_X);
}
protected float getTranslateY() {
return getValue(mMatrix, Matrix.MTRANS_Y);
}
protected void maxZoomTo(int x, int y) {
if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
// threshold 0.1f
float scale = mMinScale / getScale();
zoomTo(scale, x, y);
} else {
float scale = MAX_SCALE / getScale();
zoomTo(scale, x, y);
}
}
protected void zoomTo(float scale, int x, int y) {
if (getScale() * scale < mMinScale) {
return;
}
if (scale >= 1 && getScale() * scale > MAX_SCALE) {
return;
}
mMatrix.postScale(scale, scale);
// move to center
mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2,
-(mHeight * scale - mHeight) / 2);
// move x and y distance
mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
setImageMatrix(mMatrix);
}
public void cutting() {
int width = (int) (mIntrinsicWidth * getScale());
int height = (int) (mIntrinsicHeight * getScale());
if (getTranslateX() < -(width - mWidth)) {
mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
}
if (getTranslateX() > 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
}
if (getTranslateY() < -(height - mHeight)) {
mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
}
if (getTranslateY() > 0) {
mMatrix.postTranslate(0, -getTranslateY());
}
if (width < mWidth) {
mMatrix.postTranslate((mWidth - width) / 2, 0);
}
if (height < mHeight) {
mMatrix.postTranslate(0, (mHeight - height) / 2);
}
setImageMatrix(mMatrix);
}
private float distance(float x0, float x1, float y0, float y1) {
float x = x0 - x1;
float y = y0 - y1;
return FloatMath.sqrt(x * x + y * y);
}
private float dispDistance() {
return FloatMath.sqrt(mWidth * mWidth + mHeight
* mHeight);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
if (touchCount >= 2) {
float distance = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
mPrevDistance = distance;
isScaling = true;
} else {
if (System.currentTimeMillis() <= mLastTime + DOUBLE_TAP_SECOND) {
if (30 > Math.abs(mPrevMoveX - event.getX())
+ Math.abs(mPrevMoveY - event.getY())) {
isDoubleTap = true;
mDoubleTapX = (int) event.getX();
mDoubleTapY = (int) event.getY();
}
}
mLastTime = System.currentTimeMillis();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
}
break;
case MotionEvent.ACTION_MOVE:
if (touchCount >= 2 && isScaling) {
float dist = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
float scale = (dist - mPrevDistance) / dispDistance();
mPrevDistance = dist;
scale += 1;
scale = scale * scale;
zoomTo(scale, mWidth / 2, mHeight / 2);
cutting();
} else if (!isScaling) {
int distanceX = mPrevMoveX - (int) event.getX();
int distanceY = mPrevMoveY - (int) event.getY();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
mMatrix.postTranslate(-distanceX, -distanceY);
cutting();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_2_UP:
if (event.getPointerCount() <= 1) {
isScaling = false;
if (isDoubleTap) {
if (30 > Math.abs(mDoubleTapX - event.getX())
+ Math.abs(mDoubleTapY - event.getY())) {
maxZoomTo(mDoubleTapX, mDoubleTapY);
cutting();
}
}
}
isDoubleTap = false;
break;
}
return true;
}
public boolean onTouch(View v, MotionEvent event) {
return super.onTouchEvent(event);
}
}
what am i missing ? thanks
Use ViewPager with you ImageView with Universal Image loader, I've already did and used your code for scaling image ;)

Speedometer Animation Android

I have 8 images which are placed circularly on the dial which resembles a speedometer with first image placed at the position which is similar to 0 and the last image on the position similar to 200 or the last value of the speedometer.
Now i want to rotate the needle which is fixed at the base and moves from the first image to last.While the needle rotates,as it moves over the images,the images changes.
The needle moves from first image to the last and moves back to first image.!
Have removed the sensor's code as i didnt wanted and remove the background and major chunk added a new method called reverse() for reversing the movement not able to find where i am going wrong. it just execute the loop once and comes out.
public final class Thermometer extends View
{
private static final String TAG = Thermometer.class.getSimpleName();
private Handler handler;
// drawing tools
private Paint scalePaint;
private RectF scaleRect;
private Paint logoPaint;
private Bitmap logo;
private Matrix logoMatrix;
private float logoScale;
private Paint handPaint;
private Path handPath;
private Paint handScrewPaint;
private Paint backgroundPaint;
// end drawing tools
private Bitmap background; // holds the cached static part
// scale configuration
private static final int totalNicks = 100;
private static final float degreesPerNick = 360.0f / totalNicks;
private static final int centerDegree = 40; // the one in the top center (12
// o'clock)
private static final int minDegrees = -30;
private static final int maxDegrees = 110;
// hand dynamics -- all are angular expressed in F degrees
private boolean handInitialized = false;
private float handPosition = minDegrees;
private float handTarget = minDegrees;
private float handVelocity = 0.0f;
private float handAcceleration = 0.0f;
private long lastHandMoveTime = -1L;
public Thermometer(Context context)
{
super(context);
init();
}
public Thermometer(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public Thermometer(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
#Override
protected void onRestoreInstanceState(Parcelable state)
{
Bundle bundle = (Bundle) state;
Parcelable superState = bundle.getParcelable("superState");
super.onRestoreInstanceState(superState);
handInitialized = bundle.getBoolean("handInitialized");
handPosition = bundle.getFloat("handPosition");
handTarget = bundle.getFloat("handTarget");
handVelocity = bundle.getFloat("handVelocity");
handAcceleration = bundle.getFloat("handAcceleration");
lastHandMoveTime = bundle.getLong("lastHandMoveTime");
}
#Override
protected Parcelable onSaveInstanceState()
{
Parcelable superState = super.onSaveInstanceState();
Bundle state = new Bundle();
state.putParcelable("superState", superState);
state.putBoolean("handInitialized", handInitialized);
state.putFloat("handPosition", handPosition);
state.putFloat("handTarget", handTarget);
state.putFloat("handVelocity", handVelocity);
state.putFloat("handAcceleration", handAcceleration);
state.putLong("lastHandMoveTime", lastHandMoveTime);
return state;
}
private void init()
{
handler = new Handler();
initDrawingTools();
}
private void initDrawingTools()
{
// the linear gradient is a bit skewed for realism
logoPaint = new Paint();
logoPaint.setFilterBitmap(true);
logo = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.logo);
logoMatrix = new Matrix();
logoScale = (1.0f / logo.getWidth()) * 0.3f;
logoMatrix.setScale(logoScale, logoScale);
handPaint = new Paint();
handPaint.setAntiAlias(true);
handPaint.setColor(0xff392f2c);
handPaint.setShadowLayer(0.01f, -0.005f, -0.005f, 0x7f000000);
handPaint.setStyle(Paint.Style.FILL);
handPath = new Path();
handPath.moveTo(0.5f, 0.5f + 0.2f);
handPath.lineTo(0.5f - 0.010f, 0.5f + 0.2f - 0.007f);
handPath.lineTo(0.5f - 0.002f, 0.5f - 0.32f);
handPath.lineTo(0.5f + 0.002f, 0.5f - 0.32f);
handPath.lineTo(0.5f + 0.010f, 0.5f + 0.2f - 0.007f);
handPath.lineTo(0.5f, 0.5f + 0.2f);
handPath.addCircle(0.5f, 0.5f, 0.025f, Path.Direction.CW);
handScrewPaint = new Paint();
handScrewPaint.setAntiAlias(true);
handScrewPaint.setColor(0xff493f3c);
handScrewPaint.setStyle(Paint.Style.FILL);
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size)
{
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY)
{
return size;
}
else
{ // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize()
{
return 300;
}
private void drawScale(Canvas canvas)
{
canvas.drawOval(scaleRect, scalePaint);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
for (int i = 0; i < totalNicks; ++i)
{
float y1 = scaleRect.top;
float y2 = y1 - 0.020f;
canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);
if (i % 5 == 0)
{
int value = nickToDegree(i);
if (value >= minDegrees && value <= maxDegrees)
{
String valueString = Integer.toString(value);
canvas.drawText(valueString, 0.5f, y2 - 0.015f, scalePaint);
}
}
canvas.rotate(degreesPerNick, 0.5f, 0.5f);
}
canvas.restore();
}
private int nickToDegree(int nick)
{
int rawDegree = ((nick < totalNicks / 2) ? nick : (nick - totalNicks)) * 2;
int shiftedDegree = rawDegree + centerDegree;
return shiftedDegree;
}
private float degreeToAngle(float degree)
{
return (degree - centerDegree) / 2.0f * degreesPerNick;
}
private void drawLogo(Canvas canvas)
{
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(0.5f - logo.getWidth() * logoScale / 2.0f,0.5f - logo.getHeight() * logoScale / 2.0f);
int color = 0x00000000;
float position = getRelativeTemperaturePosition();
if (position < 0)
{
color |= (int) ((0xf0) * -position); // blue
}
else
{
color |= ((int) ((0xf0) * position)) << 16; // red
}
Log.d(TAG, "*** " + Integer.toHexString(color));
LightingColorFilter logoFilter = new LightingColorFilter(0xff338822,color);
logoPaint.setColorFilter(logoFilter);
canvas.drawBitmap(logo, logoMatrix, logoPaint);
canvas.restore();
}
private void drawHand(Canvas canvas)
{
if (handInitialized)
{
float handAngle = degreeToAngle(handPosition);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(handAngle, 0.5f, 0.5f);
canvas.drawPath(handPath, handPaint);
canvas.restore();
canvas.drawCircle(0.5f, 0.5f, 0.01f, handScrewPaint);
}
}
private void drawBackground(Canvas canvas)
{
if (background == null)
{
Log.w(TAG, "Background not created");
}
else
{
canvas.drawBitmap(background, 0, 0, backgroundPaint);
}
}
#Override
protected void onDraw(Canvas canvas)
{
drawBackground(canvas);
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawLogo(canvas);
drawHand(canvas);
canvas.restore();
moveHand();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
Log.d(TAG, "Size changed to " + w + "x" + h);
regenerateBackground();
}
private void regenerateBackground()
{
// free the old bitmap
if (background != null)
{
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getHeight(),Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
float scale = (float) getWidth();
backgroundCanvas.scale(scale, scale);
}
private boolean handNeedsToMove()
{
return Math.abs(handPosition - handTarget) > 0.01f;
}
private void moveHand()
{
setHandTarget(maxDegrees);
if (!handNeedsToMove())
{
return;
}
if (lastHandMoveTime != -1L)
{
long currentTime = System.currentTimeMillis();
float delta = (currentTime - lastHandMoveTime) / 1500.0f;
float direction = Math.signum(handVelocity);
if (Math.abs(handVelocity) < 90.0f)
{
handAcceleration = 5.0f * (handTarget - handPosition);
}
else
{
handAcceleration = 0.0f;
}
handPosition += handVelocity * delta;
handVelocity += handAcceleration * delta;
if ((handTarget - handPosition) * direction < 0.01f * direction)
{
handPosition = handTarget;
handVelocity = 0.0f;
handAcceleration = 0.0f;
lastHandMoveTime = -1L;
}
else
{
Log.i("inside ","direction else loop");
lastHandMoveTime = System.currentTimeMillis();
}
invalidate();
}
else
{
Log.i("inside ","direction first final else loop");
lastHandMoveTime = System.currentTimeMillis();
moveHand();
}
if(handPosition==maxDegrees)
{
reverse();
}
}
public void reverse()
{
handAcceleration=1.0f;
Log.i("Hand Velocity",Float.toString(handVelocity));
Log.i("Inside","next loop");
setHandTarget(minDegrees);
if (!handNeedsToMove())
{
return;
}
if (lastHandMoveTime != -1L)
{
long currentTime = System.currentTimeMillis();
float delta = (currentTime -lastHandMoveTime) / 1500.0f;
float direction = Math.signum(handVelocity);
if (Math.abs(handVelocity) <90.0f)
{
handAcceleration = 5.0f * (handPosition+handTarget);
Log.i("Hand Acceleration",Float.toString(handAcceleration));
}
else
{
handAcceleration = 0.0f;
}
handPosition -= handVelocity * delta;
handVelocity -= handAcceleration *delta;
if ((handPosition + handTarget) * direction < 0.01f * direction)
{
handPosition = handTarget;
handVelocity = 0.0f;
handAcceleration = 0.0f;
lastHandMoveTime =-1L;
}
else
{
lastHandMoveTime = System.currentTimeMillis();
}
invalidate();
}
else
{
lastHandMoveTime = System.currentTimeMillis();
reverse();
}
}
private float getRelativeTemperaturePosition()
{
if (handPosition < centerDegree)
{
return -(centerDegree - handPosition)/ (float) (centerDegree - minDegrees);
}
else
{
return (handPosition - centerDegree)/ (float) (maxDegrees - centerDegree);
}
}
private void setHandTarget(float temperature)
{
if (temperature < minDegrees)
{
temperature = minDegrees;
}
else if (temperature > maxDegrees)
{
temperature = maxDegrees;
}
handTarget = temperature;
handInitialized = true;
invalidate();
}
}
Just initialize the hand position to max degree and in the moveHand() method setHandTarget(minDegrees) for anticlockwise rotation It worked for me.
For clockwise rotation just do the opposite.
I have solved hand showing and moving inserting this in main activity:
View termometro = (View)findViewById(R.id.termometro);
termometro.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
It has to do with hardware acceleration

Categories

Resources