I'm using a code for zooming my EditText and it works 100%, but the problem is when zooming the text losses its quality like zooming an image.
I need it to zoom the font in the text like making it's size bigger (size up)
This is my code for xml:
<ScrollView
android:layout_width="fill_parent"
android:layout_height="250dp"
android:visibility="invisible"
android:background="#null"
android:id="#+id/SResult">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="250dp">
<TextView
android:layout_width="fill_parent"
android:layout_height="1000dp"
android:visibility="invisible"
android:id="#+id/backtxt"
android:background="#android:color/white"
/>
<com.calcupital.calcupital.ZoomView
android:id="#+id/zoomView3"
android:layout_width="fill_parent"
android:layout_height="1000dp"
>
<EditText
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/Result"
android:gravity="left"
android:padding="5dp"
android:visibility="invisible"
android:textSize="16dp"
android:textColor="#android:color/black"
android:background="#null"
android:inputType="none" />
</com.calcupital.calcupital.ZoomView>
</RelativeLayout>
</ScrollView>
and java:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class ZoomView extends FrameLayout {
public ZoomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public ZoomView(final Context context) {
super(context);
}
/**
* Zooming view listener interface.
*
* #author karooolek
*
*/
public interface ZoomViewListener {
void onZoomStarted(float zoom, float zoomx, float zoomy);
void onZooming(float zoom, float zoomx, float zoomy);
void onZoomEnded(float zoom, float zoomx, float zoomy);
}
// zooming
float zoom = 1.0f;
float maxZoom = 2.0f;
float smoothZoom = 1.0f;
float zoomX, zoomY;
float smoothZoomX, smoothZoomY;
private boolean scrolling; // NOPMD by karooolek on 29.06.11 11:45
// minimap variables
private boolean showMinimap = false;
private int miniMapColor = Color.WHITE;
private int miniMapHeight = -1;
private String miniMapCaption;
private float miniMapCaptionSize = 10.0f;
private int miniMapCaptionColor = Color.WHITE;
// touching variables
private long lastTapTime;
private float touchStartX, touchStartY;
private float touchLastX, touchLastY;
private float startd;
private boolean pinching;
private float lastd;
private float lastdx1, lastdy1;
private float lastdx2, lastdy2;
// drawing
private final Matrix m = new Matrix();
private final Paint p = new Paint();
// listener
ZoomViewListener listener;
private Bitmap ch;
public float getZoom() {
return zoom;
}
public float getMaxZoom() {
return maxZoom;
}
public void setMaxZoom(final float maxZoom) {
if (maxZoom < 1.0f) {
return;
}
this.maxZoom = maxZoom;
}
public void setMiniMapEnabled(final boolean showMiniMap) {
this.showMinimap = showMiniMap;
}
public boolean isMiniMapEnabled() {
return showMinimap;
}
public void setMiniMapHeight(final int miniMapHeight) {
if (miniMapHeight < 0) {
return;
}
this.miniMapHeight = miniMapHeight;
}
public int getMiniMapHeight() {
return miniMapHeight;
}
public void setMiniMapColor(final int color) {
miniMapColor = color;
}
public int getMiniMapColor() {
return miniMapColor;
}
public String getMiniMapCaption() {
return miniMapCaption;
}
public void setMiniMapCaption(final String miniMapCaption) {
this.miniMapCaption = miniMapCaption;
}
public float getMiniMapCaptionSize() {
return miniMapCaptionSize;
}
public void setMiniMapCaptionSize(final float size) {
miniMapCaptionSize = size;
}
public int getMiniMapCaptionColor() {
return miniMapCaptionColor;
}
public void setMiniMapCaptionColor(final int color) {
miniMapCaptionColor = color;
}
public void zoomTo(final float zoom, final float x, final float y) {
this.zoom = Math.min(zoom, maxZoom);
zoomX = x;
zoomY = y;
smoothZoomTo(this.zoom, x, y);
}
public void smoothZoomTo(final float zoom, final float x, final float y) {
smoothZoom = clamp(1.0f, zoom, maxZoom);
smoothZoomX = x;
smoothZoomY = y;
if (listener != null) {
listener.onZoomStarted(smoothZoom, x, y);
}
}
public ZoomViewListener getListener() {
return listener;
}
public void setListner(final ZoomViewListener listener) {
this.listener = listener;
}
public float getZoomFocusX() {
return zoomX * zoom;
}
public float getZoomFocusY() {
return zoomY * zoom;
}
#Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
// single touch
if (ev.getPointerCount() == 1) {
processSingleTouchEvent(ev);
}
// // double touch
if (ev.getPointerCount() == 2) {
processDoubleTouchEvent(ev);
}
// redraw
getRootView().invalidate();
invalidate();
return true;
}
private void processSingleTouchEvent(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
final boolean touchingMiniMap = x >= 10.0f && x <= 10.0f + w
&& y >= 10.0f && y <= 10.0f + h;
if (showMinimap && smoothZoom > 1.0f && touchingMiniMap) {
processSingleTouchOnMinimap(ev);
} else {
processSingleTouchOutsideMinimap(ev);
}
}
private void processSingleTouchOnMinimap(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
final float zx = (x - 10.0f) / w * getWidth();
final float zy = (y - 10.0f) / h * getHeight();
smoothZoomTo(smoothZoom, zx, zy);
}
private void processSingleTouchOutsideMinimap(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
float lx = x - touchStartX;
float ly = y - touchStartY;
final float l = (float) Math.hypot(lx, ly);
float dx = x - touchLastX;
float dy = y - touchLastY;
touchLastX = x;
touchLastY = y;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStartX = x;
touchStartY = y;
touchLastX = x;
touchLastY = y;
dx = 0;
dy = 0;
lx = 0;
ly = 0;
scrolling = false;
break;
case MotionEvent.ACTION_MOVE:
if (scrolling || (smoothZoom > 1.0f && l > 30.0f)) {
if (!scrolling) {
scrolling = true;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
smoothZoomX -= dx / zoom;
smoothZoomY -= dy / zoom;
return;
}
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
// tap
if (l < 30.0f) {
// check double tap
if (System.currentTimeMillis() - lastTapTime < 500) {
if (smoothZoom == 1.0f) {
smoothZoomTo(maxZoom, x, y);
} else {
smoothZoomTo(1.0f, getWidth() / 2.0f,
getHeight() / 2.0f);
}
lastTapTime = 0;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
return;
}
lastTapTime = System.currentTimeMillis();
performClick();
}
break;
default:
break;
}
ev.setLocation(zoomX + (x - 0.5f * getWidth()) / zoom, zoomY
+ (y - 0.5f * getHeight()) / zoom);
ev.getX();
ev.getY();
super.dispatchTouchEvent(ev);
}
private void processDoubleTouchEvent(final MotionEvent ev) {
final float x1 = ev.getX(0);
final float dx1 = x1 - lastdx1;
lastdx1 = x1;
final float y1 = ev.getY(0);
final float dy1 = y1 - lastdy1;
lastdy1 = y1;
final float x2 = ev.getX(1);
final float dx2 = x2 - lastdx2;
lastdx2 = x2;
final float y2 = ev.getY(1);
final float dy2 = y2 - lastdy2;
lastdy2 = y2;
// pointers distance
final float d = (float) Math.hypot(x2 - x1, y2 - y1);
final float dd = d - lastd;
lastd = d;
final float ld = Math.abs(d - startd);
Math.atan2(y2 - y1, x2 - x1);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startd = d;
pinching = false;
break;
case MotionEvent.ACTION_MOVE:
if (pinching || ld > 30.0f) {
pinching = true;
final float dxk = 0.5f * (dx1 + dx2);
final float dyk = 0.5f * (dy1 + dy2);
smoothZoomTo(Math.max(1.0f, zoom * d / (d - dd)), zoomX - dxk
/ zoom, zoomY - dyk / zoom);
}
break;
case MotionEvent.ACTION_UP:
default:
pinching = false;
break;
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
private float clamp(final float min, final float value, final float max) {
return Math.max(min, Math.min(value, max));
}
private float lerp(final float a, final float b, final float k) {
return a + (b - a) * k;
}
private float bias(final float a, final float b, final float k) {
return Math.abs(b - a) >= k ? a + k * Math.signum(b - a) : b;
}
#Override
protected void dispatchDraw(final Canvas canvas) {
// do zoom
zoom = lerp(bias(zoom, smoothZoom, 0.05f), smoothZoom, 0.2f);
smoothZoomX = clamp(0.5f * getWidth() / smoothZoom, smoothZoomX,
getWidth() - 0.5f * getWidth() / smoothZoom);
smoothZoomY = clamp(0.5f * getHeight() / smoothZoom, smoothZoomY,
getHeight() - 0.5f * getHeight() / smoothZoom);
zoomX = lerp(bias(zoomX, smoothZoomX, 0.1f), smoothZoomX, 0.35f);
zoomY = lerp(bias(zoomY, smoothZoomY, 0.1f), smoothZoomY, 0.35f);
if (zoom != smoothZoom && listener != null) {
listener.onZooming(zoom, zoomX, zoomY);
}
final boolean animating = Math.abs(zoom - smoothZoom) > 0.0000001f
|| Math.abs(zoomX - smoothZoomX) > 0.0000001f
|| Math.abs(zoomY - smoothZoomY) > 0.0000001f;
// nothing to draw
if (getChildCount() == 0) {
return;
}
// prepare matrix
m.setTranslate(0.5f * getWidth(), 0.5f * getHeight());
m.preScale(zoom, zoom);
m.preTranslate(
-clamp(0.5f * getWidth() / zoom, zoomX, getWidth() - 0.5f
* getWidth() / zoom),
-clamp(0.5f * getHeight() / zoom, zoomY, getHeight() - 0.5f
* getHeight() / zoom));
// get view
final View v = getChildAt(0);
m.preTranslate(v.getLeft(), v.getTop());
// get drawing cache if available
if (animating && ch == null && isAnimationCacheEnabled()) {
v.setDrawingCacheEnabled(true);
ch = v.getDrawingCache();
}
// draw using cache while animating
if (animating && isAnimationCacheEnabled() && ch != null) {
p.setColor(0xffffffff);
canvas.drawBitmap(ch, m, p);
} else { // zoomed or cache unavailable
ch = null;
canvas.save();
canvas.concat(m);
v.draw(canvas);
canvas.restore();
}
// draw minimap
if (showMinimap) {
if (miniMapHeight < 0) {
miniMapHeight = getHeight() / 4;
}
canvas.translate(10.0f, 10.0f);
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
canvas.drawRect(0.0f, 0.0f, w, h, p);
if (miniMapCaption != null && miniMapCaption.length() > 0) {
p.setTextSize(miniMapCaptionSize);
p.setColor(miniMapCaptionColor);
p.setAntiAlias(true);
canvas.drawText(miniMapCaption, 10.0f,
10.0f + miniMapCaptionSize, p);
p.setAntiAlias(false);
}
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
final float dx = w * zoomX / getWidth();
final float dy = h * zoomY / getHeight();
canvas.drawRect(dx - 0.5f * w / zoom, dy - 0.5f * h / zoom, dx
+ 0.5f * w / zoom, dy + 0.5f * h / zoom, p);
canvas.translate(-10.0f, -10.0f);
}
// redraw
// if (animating) {
getRootView().invalidate();
invalidate();
// }
}
}
and this is my java code for main activity:
final static float STEP = 200;
TextView mtxtRatio1, mtxtRatio2, mtxtRatio3, mtxtRatio4;
float mRatio = 1.0f;
int mBaseDist;
float mBaseRatio;
float fontsize = 13;
So, how can I resolve this issue and make zooming for the text without loosing the quality?
set a custom selector as background for your EditText with only colors (noImage). It "MAY" work, i didn't tested it.tell me if it works
EDIT:2
View
<Button
android:id="#+id/bButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/bookbutton_selector"
android:text="Button" />
drawables/bookbutton_selector
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="false" android:drawable="#drawable/bookbutton_normal"/>
<item android:state_pressed="true" android:drawable="#drawable/bookbutton_pressed"/>
</selector>
drawables/bookbutton_normal
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="5dp" />
<gradient
android:startColor="#color/bookmany"
android:endColor="#color/bookmany_dark"
/>
</shape>
drawables/bookbutton_pressed
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="5dp" />
<gradient
android:endColor="#333"
android:startColor="#333" />
</shape>
This is a case of button, and this is also applicable for EditText with small changes in selector.
As you can see i set the selector as the background for the Button, this will help you the button to have custom style when get Clicked and Released.
Try it out. Let me know if you need more help
Related
Here is My code, I am trying to zoom at the specific area but my code is not working properly. I want my code to work as Autocad Canvas zoom in out can anyone know to zoom canvas just like AutoCAD canvas. I want to make the CAD application work the same as AutoCAD work please help me.
I want My code to work as zoom in-out canvas drag move canvas.
public class Custom_Canvas extends View implements View.OnTouchListener {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
Paint paint = new Paint();
// See onTouchEvent!
private float initX = 0f;
private float initY = 0f;
// x and y coord of canvas center!
private float canvasX = 0f;
private float canvasY = 0f;
private boolean drag = false;
private boolean firstdraw = true;
ScaleGestureDetector scaleGestureDetector;
private float scalefactor = 1f;
private float scalePointX, scalePointY;
float min_zoom = 0.1f;
float max_zoom = 10f;
private float lastfocusX = -1;
private float lastfocusY = -1;
#SuppressLint("ClickableViewAccessibility")
public Custom_Canvas(Context context, AttributeSet attrs){
super(context, attrs);
this.setOnTouchListener(this);
scaleGestureDetector = new ScaleGestureDetector(context,new Scalelistener());
paint.setColor(Color.WHITE);
}
#SuppressLint("ClickableViewAccessibility")
public Custom_Canvas(Context context) {
super(context);
this.setOnTouchListener(this);
paint.setColor(Color.WHITE);
scaleGestureDetector = new ScaleGestureDetector(context,new Scalelistener());
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
if(firstdraw){
canvasX = getWidth()/2f;
canvasY = getHeight()/2f;
firstdraw = false;
}
canvas.scale(scalefactor,scalefactor,scalePointX,scalePointY);
canvas.translate(canvasX,canvasY);
canvas.drawCircle(0,0,20f,paint);
// canvas.drawCircle(100,100,20f,paint);
canvas.restore();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
scaleGestureDetector.onTouchEvent(event);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
float x = event.getX()/scalefactor;
float y = event.getY()/scalefactor;
// drag = true;
initX = x;
initY = y;
mActivePointerId = event.getPointerId(0);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
// case MotionEvent.ACTION_POINTER_DOWN:
final int p = event.findPointerIndex(mActivePointerId);
final float x1 = event.getX(p) / scalefactor;
final float y1 = event.getY(p) / scalefactor;
if (!scaleGestureDetector.isInProgress()) {
// if (drag) {
float dx = x1 - initX;
float dy = y1 - initY;
canvasX += dx ;
canvasY += dy ;
invalidate();
// }
}
initX = x1;
initY = y1;
break;
case MotionEvent.ACTION_POINTER_UP:
// drag = false;
final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
initX = event.getX(newPointerIndex)/scalefactor;
initY = event.getY(newPointerIndex)/scalefactor;
mActivePointerId = event.getPointerId(newPointerIndex);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// drag = false;
mActivePointerId = INVALID_POINTER_ID;
break;
}
return true;
}
private class Scalelistener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
#Override
public boolean onScale(ScaleGestureDetector detector) {
scalefactor *= detector.getScaleFactor();
scalePointX = detector.getFocusX();
scalePointY = detector.getFocusY();
Toast.makeText(getContext(), " "+scalePointX+" "+scalePointY, Toast.LENGTH_SHORT).show();
if(lastfocusX == -1){
lastfocusX = scalePointX;
}
if(lastfocusY == -1){
lastfocusY = scalePointY;
}
canvasX += (scalePointX - lastfocusX);
canvasY += (scalePointY - lastfocusY);
scalefactor = Math.max(min_zoom,Math.min(scalefactor,max_zoom));
lastfocusX = scalePointX;
lastfocusY = scalePointY;
invalidate();
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastfocusX = -1;
lastfocusY = -1;
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) { }
}
I'm building an app to generate circles on a bitmap when the user double taps the screen. When the user pinches in or pinches out, the image zooms in and zooms out, and when they use one finger the image can be dragged around.
I wrote code to draw a circle on bitmap when the user double-taps, and it works well EXCEPT that the image gets zoomed WAY OUT after it draws the circle...is something wrong with the gesturedetector class I wrote (the class is at the bottom of the following code:)
public class ZoomInZoomOut extends ImageView {
private static final String TAG = "ZoomableImageView";
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;
Context context;
private GestureDetector gestureDetector;
public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
public static final int DEFAULT_SCALE_ORIGINAL = 1;
private int defaultScale;
public int getDefaultScale() {
return defaultScale;
}
public void setDefaultScale(int defaultScale) {
this.defaultScale = defaultScale;
}
public ZoomInZoomOut(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
screenDensity = context.getResources().getDisplayMetrics().density;
initPaints();
gestureDetector = new GestureDetector(new MyGestureDetector());
this.context=context;
}
public ZoomInZoomOut(Context context, AttributeSet attrs) {
super(context, attrs);
screenDensity = context.getResources().getDisplayMetrics().density;
initPaints();
gestureDetector = new GestureDetector(new MyGestureDetector());
defaultScale = ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE;
}
private void initPaints() {
background = new Paint();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
//Reset the width and height. Will draw bitmap and change
containerWidth = width;
containerHeight = height;
if(imgBitmap != null) {
int imgHeight = imgBitmap.getHeight();
int imgWidth = imgBitmap.getWidth();
float scale;
int initX = 0;
int initY = 0;
if(defaultScale == ZoomInZoomOut.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();
}
}
#Override
protected void onDraw(Canvas canvas) {
if(imgBitmap != null && canvas != null)
{
canvas.drawBitmap(imgBitmap, matrix, background);
}
}
//Checks and sets the target image x and y co-ordinates if out of bounds
private void checkImageConstraints() {
if(imgBitmap == null) {
return;
}
float[] mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
if(currentScale < minScale) {
float deltaScale = minScale / currentScale;
float px = containerWidth/2;
float py = containerHeight/2;
matrix.postScale(deltaScale, deltaScale, px, py);
invalidate();
}
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
int rangeLimitX = containerWidth - (int)(imgBitmap.getWidth() * currentScale);
int rangeLimitY = containerHeight - (int)(imgBitmap.getHeight() * currentScale);
boolean toMoveX = false;
boolean toMoveY = false;
if(rangeLimitX < 0) {
if(curX > 0) {
targetX = 0;
toMoveX = true;
}
else if(curX < rangeLimitX) {
targetX = rangeLimitX;
toMoveX = true;
}
}
else {
targetX = rangeLimitX / 2;
toMoveX = true;
}
if(rangeLimitY < 0) {
if(curY > 0) {
targetY = 0;
toMoveY = true;
}
else if(curY < rangeLimitY) {
targetY = rangeLimitY;
toMoveY = true;
}
}
else {
targetY = rangeLimitY / 2;
toMoveY = true;
}
if(toMoveX == true || toMoveY == true) {
if(toMoveY == false) {
targetY = curY;
}
if(toMoveX == false) {
targetX = curX;
}
//Disable touch event actions
isAnimating = true;
//Initialize timer
mHandler.removeCallbacks(mUpdateImagePositionTask);
mHandler.postDelayed(mUpdateImagePositionTask, 100);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float oldX = 0, newX = 0, sens = 5;
if(gestureDetector.onTouchEvent(event)) {
return true;
}
if(isAnimating == true) {
return true;
}
//Handle touch events here
float[] mvals = new float[9];
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if(isAnimating == false) {
savedMatrix.set(matrix);
oldX = event.getX();
start.set(event.getX(), event.getY());
mode = DRAG;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if(oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
newX = event.getX();
if (Math.abs(oldX - newX) < sens) {
Toast.makeText(context, "Hello", Toast.LENGTH_LONG).show();
return true;
}
oldX = 0;
newX = 0;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
if(isAnimating == false) {
checkImageConstraints();
}
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG && isAnimating == false) {
matrix.set(savedMatrix);
float diffX = event.getX() - start.x;
float diffY = event.getY() - start.y;
matrix.postTranslate(diffX, diffY);
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
}
else if(mode == ZOOM && isAnimating == false) {
float newDist = spacing(event);
if(newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.getValues(mvals);
currentScale = mvals[0];
if(currentScale * scale <= minScale) {
matrix.postScale(minScale/currentScale, minScale/currentScale, mid.x, mid.y);
}
else if(currentScale * scale >= maxScale) {
matrix.postScale(maxScale/currentScale, maxScale/currentScale, mid.x, mid.y);
}
else {
matrix.postScale(scale, scale, mid.x, mid.y);
}
matrix.getValues(mvals);
curX = mvals[2];
curY = mvals[5];
currentScale = mvals[0];
}
}
break;
}
//Calculate the transformations and then invalidate
invalidate();
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) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x/2, y/2);
}
public void setImageBitmap(Bitmap b) {
if(b != null) {
imgBitmap = b;
containerWidth = getWidth();
containerHeight = getHeight();
int imgHeight = imgBitmap.getHeight();
int imgWidth = imgBitmap.getWidth();
float scale;
int initX = 0;
int initY = 0;
matrix.reset();
if(defaultScale == ZoomInZoomOut.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) {
initX = 0;
if(imgHeight > containerHeight) {
initY = 0;
}
else {
initY = (containerHeight - (int)imgHeight)/2;
}
matrix.postTranslate(0, initY);
}
else {
initX = (containerWidth - (int)imgWidth)/2;
if(imgHeight > containerHeight) {
initY = 0;
}
else {
initY = (containerHeight - (int)imgHeight)/2;
}
matrix.postTranslate(initX, 0);
}
curX = initX;
curY = initY;
currentScale = 1.0f;
minScale = 1.0f;
}
invalidate();
}
else {
Log.d(TAG, "bitmap is null");
}
}
public Bitmap getPhotoBitmap() {
return imgBitmap;
}
private Runnable mUpdateImagePositionTask = new Runnable() {
public void run() {
float[] mvals;
if(Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) {
isAnimating = false;
mHandler.removeCallbacks(mUpdateImagePositionTask);
mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
//Set the image parameters and invalidate display
float diffX = (targetX - curX);
float diffY = (targetY - curY);
matrix.postTranslate(diffX, diffY);
}
else {
isAnimating = true;
mvals = new float[9];
matrix.getValues(mvals);
currentScale = mvals[0];
curX = mvals[2];
curY = mvals[5];
//Set the image parameters and invalidate display
float diffX = (targetX - curX) * 0.3f;
float diffY = (targetY - curY) * 0.3f;
matrix.postTranslate(diffX, diffY);
mHandler.postDelayed(this, 25);
}
invalidate();
}
};
private Runnable mUpdateImageScale = new Runnable() {
public void run() {
float transitionalRatio = targetScale / currentScale;
float dx;
if(Math.abs(transitionalRatio - 1) > 0.05) {
isAnimating = true;
if(targetScale > currentScale) {
dx = transitionalRatio - 1;
scaleChange = 1 + dx * 0.2f;
currentScale *= scaleChange;
if(currentScale > targetScale) {
currentScale = currentScale / scaleChange;
scaleChange = 1;
}
}
else {
dx = 1 - transitionalRatio;
scaleChange = 1 - dx * 0.5f;
currentScale *= scaleChange;
if(currentScale < targetScale) {
currentScale = currentScale / scaleChange;
scaleChange = 1;
}
}
if(scaleChange != 1) {
matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);
mHandler.postDelayed(mUpdateImageScale, 15);
invalidate();
}
else {
isAnimating = false;
scaleChange = 1;
matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
currentScale = targetScale;
mHandler.removeCallbacks(mUpdateImageScale);
invalidate();
checkImageConstraints();
}
}
else {
isAnimating = false;
scaleChange = 1;
matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
currentScale = targetScale;
mHandler.removeCallbacks(mUpdateImageScale);
invalidate();
checkImageConstraints();
}
}
};
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onDoubleTap(MotionEvent event) {
Bitmap bmOverlay = Bitmap.createBitmap(imgBitmap.getWidth(),
imgBitmap.getHeight(),
imgBitmap.getConfig());
Canvas canvas = new Canvas(bmOverlay);
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(Color.RED);
p.setStrokeWidth(2);
p.setStyle(Paint.Style.STROKE);
canvas.drawBitmap(imgBitmap, matrix, null);
canvas.drawCircle((int)event.getX(),(int)event.getY(),
100, p);
setImageBitmap(bmOverlay);
return true;
}
#Override
public boolean onDown(MotionEvent e) {
return false;
}
}
}
You're setting the image bitmap. setImageBitmap call matrix.reset. So when you do that, you're losing your zoom factor. You need to fiddle with your code/architecture to prevent that call.
I used the following code to add pinch zoom and pan
public class Touch extends Activity implements OnTouchListener {
private static final String TAG = "Touch";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 5.0f;
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// 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;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
ImageView image2;
image2 = (ImageView)this.findViewById(R.id.imageView2);
image2.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) image2;
// Handle touch events here...
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:
oldDist = spacing(event);
if (oldDist > 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.postTranslate(event.getX() - start.x, event.getY() - start.y);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
}
/** Determine the space between the first two fingers */
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);
}
/** Calculate the mid point of the first two fingers */
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
When tested with an image of 800kb this worked smoothly.
I wanted to do some drawing and to get the onDraw method, I moved this code into a MyImageView - subclassed from ImageView, added this custom item to layout via xml. It loads but the performance is very very slow. I tried other codes also, but when made inside a subclass, the performance is very slow. Can someone please tell me:
What should I do to make the subclass work equally smooth?
Can I access the onDraw method of the ImageView from the Activity without having to go for a custom class? I am talking about this:
image2 = (ImageView)this.findViewById(R.id.imageView2);
Create Custom ImageView and apply that custom imageview in xml. This will be applicable and smooth pinch in zoom and out.
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix;
// 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;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
public float getSaveScale() {
return saveScale;
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
public float getMaxScale() {
return maxScale;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
//Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
replace your imageview in xml as:
<package name.TouchImageView
android:id="#+id/imageView2"
............
/>
Hope this works..
TouchImageView image2 = (TouchImageView)this.findViewById(R.id.imageView2);
I have an ImageView zoom application ran on my mobile, when I first run it, the image work and the zooming as well, after that, I made a link button to the image view, with the same previous application, the image appears but no zooming for it and it doesn't move at all
Can someone help?
mapslocations.xml
<Button
android:id="#+id/pslook1"
android:layout_width="fill_parent"
android:layout_height="45dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_weight="1"
android:gravity="center"
android:text="Map"
android:textSize="10sp"
android:textStyle="italic" />
Sites.java
package com.f.fa;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
public class Sites extends Activity {
ImageView imageDetail1;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
PointF startPoint = new PointF();
PointF midPoint = new PointF();
float oldDist = 1f;
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapslocations);
imageDetail1 = (ImageView) findViewById(R.id.pslook1);
/**
* set on touch listner on image
*/
imageDetail1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
System.out.println("matrix=" + savedMatrix.toString());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
startPoint.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(midPoint, 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.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, midPoint.x, midPoint.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
#SuppressLint("FloatMath")
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) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
});
}
}
imageview1.xml
<ImageView
android:id="#+id/pslook1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:src="#drawable/powerstationwallmap" />
PagePowerstation.java
package com.f.fa;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
public class PagePowerstation extends Activity {
Button imageview1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.imageview1);
}
}
Try this custom TouchImageView:
public class TouchImageView extends ImageView {
Matrix matrix;
// 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;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
/* */
private void stopInterceptEvent()
{
getParent().requestDisallowInterceptTouchEvent(true);
}
private void startInterceptEvent()
{
getParent().requestDisallowInterceptTouchEvent(false);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
stopInterceptEvent();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
float transX = m[Matrix.MTRANS_X];
if((int) (getFixTrans(transX, viewWidth, origWidth * saveScale) + fixTransX) == 0)
startInterceptEvent();
else
stopInterceptEvent();
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
startInterceptEvent();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
//Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
Use in XML:
<your_package_name.TouchImageView
android:id="#+id/dialogImageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
I'm trying to implement a touch responsive image view - like the one in the Gallery application. I've managed to do that, and it works well, except that it is VERY laggy compared to the stock Gallery.
I'm looking for ways to make it smoother.
Here's what I have now:
#SuppressWarnings("unused")
public class ImageDisplayView extends ImageView {
private static final String TAG = "ImageDisplayView";
private static final int MODE_NONE = 0;
private static final int MODE_DRAG = 1;
private static final int MODE_ZOOM = 2;
// Zoom Bounds
private static final float SCALE_MIN = 0.8f;
private static final float SCALE_BOTTOM = 1.0f;
private static final float SCALE_TOP = 10.0f;
private static final float SCALE_MAX = 12.0f;
// Transformation
private Matrix mMatrix = new Matrix();
private Matrix mPreMatrix = new Matrix();
private PointF mPivot = new PointF();
private PointF mNewPivot = new PointF();
private float mDist;
// State
private boolean mInitialScaleDone = false;
private boolean mEnabled = true;
private RectF mBounds = new RectF();
private Float mScale = null;
private float mMode = MODE_NONE;
public ImageDisplayView(Context context) {
super(context);
initialScale();
}
public ImageDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
initialScale();
}
public ImageDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialScale();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initialScale();
}
private void initialScale() {
if (!mInitialScaleDone && getDrawable() != null) {
mInitialScaleDone = true;
final Runnable r = new Runnable() {
#Override
public void run() {
// Fit to screen
float scale = calculateFitScreenScale();
mMatrix.reset();
mMatrix.postScale(scale, scale);
setImageMatrix(mMatrix);
// Center
float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
.height()) / 2;
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
mScale = 1.0f;
}
};
if (getWidth() == 0) {
getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
r.run();
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
} else {
r.run();
}
}
}
#Override
public void setImageMatrix(Matrix matrix) {
super.setImageMatrix(matrix);
mBounds.set(getDrawable().getBounds());
matrix.mapRect(mBounds);
}
#Override
public void setEnabled(boolean enabled) {
mEnabled = enabled;
if (!enabled && mMode != MODE_NONE) {
if (mMode == MODE_DRAG) {
checkLimits(null);
} else {
mPivot.set(getWidth() / 2, getHeight() / 2);
updateScale();
mPreMatrix.set(mMatrix);
checkLimits(null);
}
mMode = MODE_NONE;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float scale, dx, dy;
if (mEnabled) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 1) { // Start mode: drag only.
mMode = MODE_DRAG;
mPivot.set(event.getX(), event.getY());
} else { // Start mode: zoom and drag.
mMode = MODE_ZOOM;
mDist = distance(event);
midPoint(mPivot, event);
}
mPreMatrix.set(mMatrix);
return true;
case MotionEvent.ACTION_UP:
mMode = MODE_NONE;
checkLimits(event);
break;
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerCount() == 2) {
mMode = MODE_DRAG;
mPivot.set(event.getX(), event.getY());
updateScale();
mPreMatrix.set(mMatrix);
}
return true;
case MotionEvent.ACTION_MOVE:
mMatrix.set(mPreMatrix);
if (mMode == MODE_DRAG) {
dx = event.getX() - mPivot.x;
dy = event.getY() - mPivot.y;
mMatrix.postTranslate(dx, dy);
} else if (mMode == MODE_ZOOM) {
scale = distance(event) / mDist;
midPoint(mNewPivot, event);
dx = mNewPivot.x - mPivot.x;
dy = mNewPivot.y - mPivot.y;
mMatrix.postTranslate(dx, dy);
float postScale = mScale * scale;
if (postScale < SCALE_MIN) {
scale = SCALE_MIN / mScale;
} else if (postScale > SCALE_MAX) {
scale = SCALE_MAX / mScale;
}
mMatrix.postScale(scale, scale, mNewPivot.x, mNewPivot.y);
}
setImageMatrix(mMatrix);
return true;
}
}
return super.onTouchEvent(event);
}
private void updateScale() {
mScale = (mBounds.width() / getDrawable().getIntrinsicWidth())
/ calculateFitScreenScale();
}
#IntendedCaller("onTouchEvent(MotionEvent)")
private void checkLimits(MotionEvent event) {
float scale, dx, dy;
if (mScale < SCALE_BOTTOM) {
scale = SCALE_BOTTOM;
} else if (mScale > SCALE_TOP) {
scale = SCALE_TOP;
} else {
scale = mScale;
}
RectF scaleBounds = new RectF(mBounds); // Use the scale of the image
// to determine drag
// threshold.
scaleBounds.offset(getWidth() / 2 - scaleBounds.centerX(), getHeight()
/ 2 - scaleBounds.centerY());
dx = Math.min(0.0f, Math.max(0.0f, scaleBounds.left) - mBounds.left)
+ Math.max(0.0f, Math.min(getWidth(), scaleBounds.right)
- mBounds.right);
dy = Math.min(0.0f, Math.max(0.0f, scaleBounds.top) - mBounds.top)
+ Math.max(0.0f, Math.min(getHeight(), scaleBounds.bottom)
- mBounds.bottom);
animateTransformation(event, 500, null, scale, dx, dy);
}
public void animateTransformation(MotionEvent event, long duration,
final Runnable callback, final float targetScale, final float dx,
final float dy) {
mPreMatrix.set(mMatrix);
final float scale = targetScale / mScale;
final float px, py;
if (event != null) {
PointF pivot = new PointF();
midPoint(pivot, event);
px = scale > 1.0f ? mBounds.centerX() : dx > 0 ? mBounds.right
: dx < 0 ? mBounds.left : pivot.x;
py = scale > 1.0f ? mBounds.centerY() : dy > 0 ? mBounds.bottom
: dy < 0 ? mBounds.top : pivot.y;
} else {
px = mBounds.centerX();
py = mBounds.centerY();
}
Utils.animate(this, new Animator() {
#Override
public void onAnimationEnd() {
updateScale();
if (callback != null) {
callback.run();
}
}
#Override
public void makeStep(float percent) {
mMatrix.set(mPreMatrix);
float s = 1.0f + percent * (scale - 1.0f);
float tempx = dx * percent, tempy = dy * percent;
mMatrix.postTranslate(tempx, tempy);
mMatrix.postScale(s, s, px + tempx, py + tempy);
setImageMatrix(mMatrix);
}
}, duration);
}
public RectF getBounds() {
return mBounds;
}
public float getScale() {
return mScale;
}
public void scale(float scale, float px, float py) {
mMatrix.set(getImageMatrix());
mMatrix.postScale(scale, scale, px, py);
setImageMatrix(mMatrix);
mScale = scale;
}
public void translate(float dx, float dy) {
mMatrix.set(getImageMatrix());
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
}
public void resetAndCenter() {
mMatrix.set(getImageMatrix());
mMatrix.postScale(1 / mScale, 1 / mScale, mBounds.centerX(),
mBounds.centerY());
setImageMatrix(mMatrix);
// Center
float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
.height()) / 2;
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
updateScale();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mInitialScaleDone = false;
initialScale();
}
private float distance(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 p, MotionEvent event) {
if (event.getPointerCount() == 1) {
p.set(event.getX(), event.getY());
} else {
p.set((event.getX(1) + event.getX(0)) / 2,
(event.getY(1) + event.getY(0)) / 2);
}
}
private float calculateFitScreenScale() {
RectF r = new RectF(getDrawable().getBounds());
float w = getWidth(), h = getHeight();
if (r.width() > r.height()) {
return w / r.width();
} else if (r.width() == r.height()) {
if (w < h) {
return w / r.width();
} else {
return h / r.height();
}
} else {
return h / r.height();
}
}
}
What can I do in order to make this less laggy? Any help much appreciated.
Have you tried it on an actual device? The emulator in notoriously slow.
Here's something that might help:
Check out the Batching section at the Android Dev Resource page:
http://developer.android.com/reference/android/view/MotionEvent.html
I've noticed that how a device batches together historical points in a continuous motion can differ considerably. This may also differ considerably between devices.
In your code, you only use getX and getY, which takes the most recent event, ignoring all the historical points.
If your device happens to be batching large amounts of historical events together, using only the most recent ones (via getX and getY) might cause lag.
The link suggests how one might consume all the historical points.
You could implement a solution that checks whether too many historical points have been batched together (event.getHistorySize() > x). If the number of historical points are above a threshold of your choosing, make intermediate frame updates based on, say, historySize / 2. Something like that.
Please review this question here
How can I get zoom functionality for images?
theres answer by Mike Ortiz down, implementing Mutli-Touch ImageView