package com.example.demo;
import android.content.Context;
import android.graphics.Canvas;
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.RelativeLayout;
public class MyLayout extends RelativeLayout {
private static final int INVALID_POINTER_ID = -1;
private Drawable mIcon;
private float mPosX;
private float mPosY;
MyLayout temp;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyLayout(Context context) {
this(context, null, 0);
temp = this;
}
public MyLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
temp = this;
}
public MyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources()
.getDrawable(R.drawable.ic_launcher);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(),
mIcon.getIntrinsicHeight());
temp = this;
mScaleDetector = new ScaleGestureDetector(context,
new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev
.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a
// gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.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 = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
Log.d("onLayout", "" + count);
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) child
.getLayoutParams();
child.layout((int) (params.leftMargin * mScaleFactor),
(int) (params.topMargin * mScaleFactor),
(int) ((params.leftMargin + child
.getMeasuredWidth()) * mScaleFactor),
(int) ((params.topMargin + child
.getMeasuredHeight()) * mScaleFactor));
child.setLayoutParams(params);
}
}
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// canvas.translate(mLastTouchX, mLastTouchY);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
System.out.println("onDraw");
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.draw(canvas);
Log.d("onDraw", "" + mScaleFactor);
}
}
canvas.restore();
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
System.out.println("dispatchDraw");
System.out.println("x :: " + mPosX + " :: X2 :: " + mLastTouchX);
System.out.println("y :: " + mPosY + " :: Y2 :: " + mLastTouchY);
System.out.println("Scale factor :: " + mScaleFactor);
canvas.translate(
mPosX
- ((mLastTouchX * mScaleFactor) - mLastTouchX),
mPosY
- ((mLastTouchY * mScaleFactor) - mLastTouchY));
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math
.max(1.0f, Math.min(mScaleFactor, 5.0f));
// Log.d("onScale", ""+mScaleFactor);
temp.invalidate();
invalidate();
return true;
}
}
}
Above code is for custom relative layout for pinch zoom and move with finger on screen.
But, above code is working properly. but problem is that I unable to implement zoom from where I pinch on screen.
Please help me for this.
I took a references from below link.
Zoom and Pinch RelativeLayout
Related
I need zoom in/out on whole content in RecyclerView.
In fact I need to resize all child views by width, and text inside each item.
public class ViewHolder {
Button btn;
...
}
When User make zoom in, the text of Button should be decrease down to 8sp and disappear if user continue zoom in.
So, now I have dilemma : what I need to do? Resize items in Adapter, or resize them in LayoutManager, and update whole adapter.
Both of scenarios seems affect an performance of visual effect, so I need help. Maybe you give me a better solution.
For state of 21/11/2016 I can't found any box-solution for this case.
So I decide write my own view and do remeasurement of visible views in each zoom scale factor change.
Which adapter do you use?
I've tried Horizontal LinearLayoutManager and do resize items inside adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
final ViewGroup.LayoutParams layoutParams = viewHolder.rootView.getLayoutParams();
layoutParams.width = getItemWidth(position); //width calculation using current zoom level
layoutParams.height = mItemLayoutHeight;
viewHolder.rootView.setLayoutParams(layoutParams);
...
and setZoom(..) method of the activity/fragment looks like:
public void setZoom(float newValue) {
int dScroll = (int) ((ADAPTER_ZOOM - newValue) * getResources().getDisplayMetrics().widthPixels/2f); //assume you Recycler view uses whole display
ADAPTER_ZOOM = newValue;
resetBoundaries();
//
epgAdapter.notifyDataSetChanged();
//
epgRecyclerView.scrollBy(dScroll, 0);
}
But with my custom LayoutManager performance is bad, seems there is a list of optimisations inside the LinearLayoutManager
for complete implementation refer to :
github.com/mosayeb-masoumi/zoom_whole_recyclerview
user this code. it's working fine for zooming all content of recyclerview.
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
/**
#author yogesh
* */
public class PinchRecyclerView extends RecyclerView {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private float maxWidth = 0.0f;
private float maxHeight = 0.0f;
private float mLastTouchX;
private float mLastTouchY;
private float mPosX;
private float mPosY;
private float width;
private float height;
public PinchRecyclerView(Context context) {
super(context);
if (!isInEditMode())
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public PinchRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode())
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public PinchRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode())
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent ev) {
super.onTouchEvent(ev);
final int action = ev.getAction();
mScaleDetector.onTouchEvent(ev);
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
/* this line is replaced because here came below isssue
java.lang.IllegalArgumentException: pointerIndex out of range
ref http://stackoverflow.com/questions/6919292/pointerindex-out-of-range-android-multitouch
*/
//final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
if (mPosX > 0.0f)
mPosX = 0.0f;
else if (mPosX < maxWidth)
mPosX = maxWidth;
if (mPosY > 0.0f)
mPosY = 0.0f;
else if (mPosY < maxHeight)
mPosY = maxHeight;
mLastTouchX = x;
mLastTouchY = y;
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.restore();
}
#Override
protected void dispatchDraw(#NonNull Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
if (mScaleFactor == 1.0f) {
mPosX = 0.0f;
mPosY = 0.0f;
}
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
invalidate();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 3.0f));
maxWidth = width - (width * mScaleFactor);
maxHeight = height - (height * mScaleFactor);
invalidate();
return true;
}
}
}
Code taken from this StackOverflow answer: Android change recycler view column no. on pinch zoom
I tried to create a zoom view with an image that is always scaled about its center. However, if I change the initial position of the image and pinch the image moves to the lower right corner of the window. As a reference I used the pinch zoom view of the Android Developer and changed some parameters. You have any ideas to fix that?
package android.oli.com.fitnessapp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class ZoomView extends View{
private Drawable mIcon;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private static final int INVALID_POINTER_ID = -1;
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
Context context;
public ZoomView(Context context) {
super(context);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public ZoomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public void init(){
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX - ((mIcon.getIntrinsicWidth()) * mScaleFactor) / 2, mPosY - ((mIcon.getIntrinsicWidth()) * mScaleFactor) / 2);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
//rectMid.set(0, 0, canvas.getWidth(), canvas.getHeight()/2);
//canvas.drawRect(rectMid, blue);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.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 = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
XML:
<android.oli.com.fitnessapp.ZoomView
android:layout_width="match_parent"
android:layout_height="match_parent" />
Ok i got it :D
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class ZoomView extends View{
private Drawable mIcon;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private static final int INVALID_POINTER_ID = -1;
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
Context context;
int height, width;
public ZoomView(Context context) {
super(context);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public ZoomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mIcon = context.getResources().getDrawable(R.drawable.image_1);
this.context = context;
init();
}
public void init(){
DisplayMetrics displaymetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
mIcon.setBounds(0, 0, 0 + mIcon.getIntrinsicWidth(), 0 + mIcon.getIntrinsicHeight());
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(width/2 + mPosX - (mIcon.getIntrinsicWidth() * mScaleFactor) / 2, height/2 + mPosY - (mIcon.getIntrinsicWidth() * mScaleFactor) / 2);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
//rectMid.set(0, 0, canvas.getWidth(), canvas.getHeight()/2);
//canvas.drawRect(rectMid, blue);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.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 = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
I'm trying a simple CustomView with canvas. This custom view simply change the position of an icon picture on the screen randomly when is invoked updatePosition(), and in my intention should move the image on drag, scale on pinch... and when I touch a position in the screen should appear in the clicked position.
My try is the follow
public class CustomView extends View {
Bitmap mBmp;
Random mRnd;
Paint mPaint;
private static final int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
int w, h, bw, bh;
int px = -1, py = -1;
public CustomView(Context context) {
this(context, null, 0);
mBmp = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mBmp = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
mPaint = new Paint();
mPaint.setColor(Color.CYAN);
mPaint.setAntiAlias(true);
mRnd = new Random();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
Log.d("DEBUG", "X: " + mPosX + " Y: " + mPosY);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
if (px == -1 && py == -1) {
px = w / 2 - bw / 2;
py = h / 2 - bh / 2;
}
canvas.drawCircle(px + (bw / 2), py + (bh / 2), 70, mPaint);
canvas.drawBitmap(mBmp, px, py, null);
canvas.restore();
}
public void updatePosition() {
px = mRnd.nextInt(w - bw);
py = mRnd.nextInt(h - bh);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
updatePosition() listener works while onTouchListener cause crash of the application with a NullPointerException if I try to drag the image or touch any position of the screen
Could someone help me? Thanks
It looks like mScaleDetector is only initialized in one of the constructor. It should be initialized in all constructors. A common pattern is to create an init() method and place all you initializing in there then call that method from all constructors.
Note: It's always helpful to post a StackTrace... You should be able to see which constructor got called.
I have tried t create a simple canvas but the image does't appear in the touched position moves only when I drag, I want also that teleports in the touched position if the user clicks a point of the screen
My code
public class CustomView extends View {
Bitmap mBmp;
Random mRnd;
Paint mPaint;
private static final int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
int w, h, bw, bh;
int px = -1, py = -1;
public CustomView(Context context) {
this(context, null, 0);
mBmp = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mBmp = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
mPaint = new Paint();
mPaint.setColor(Color.CYAN);
mPaint.setAntiAlias(true);
mRnd = new Random();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
Log.d("DEBUG", "X: " + mPosX + " Y: " + mPosY);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
if (px == -1 && py == -1) {
px = w / 2 - bw / 2;
py = h / 2 - bh / 2;
}
canvas.drawCircle(px + (bw / 2), py + (bh / 2), 70, mPaint);
canvas.drawBitmap(mBmp, px, py, null);
canvas.restore();
}
public void updatePosition() {
px = mRnd.nextInt(w - bw);
py = mRnd.nextInt(h - bh);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
Could someone help me? Thanks
Don't forget to call invalidate(); so your canvas redraws in your ACTION_DOWN case statement. Also it looks like you should be setting the following in ACTION_DOWN.
mPosX = mLastTouchX;
mPosY = mLastTouchY;
I am new to android development, sorry if I ask some stupid question please try to help me. I am trying to implement the Zoom and pinch in RelativeLayout. I want to make my own map view in which I'll get the image of floor map and draw the pins(ImageVIew) on it. I've tried it out but i am currently unable to click on the pins. I've done the code with the help of these posts
Extending RelativeLayout, and overriding dispatchDraw() to create a zoomable ViewGroup
and
Android - zoom in/out RelativeLayout with spread/pinch
My Code is
public class TempView extends RelativeLayout {
private static final int INVALID_POINTER_ID = -1;
private Drawable mIcon;
private float mPosX;
private float mPosY;
TempView temp;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public TempView(Context context) {
this(context, null, 0);
temp = this;
}
public TempView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
temp = this;
}
public TempView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources().getDrawable(R.drawable.ic_launcher);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());
temp = this;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.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 = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int count = getChildCount();
Log.d("onLayout", ""+count);
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility()!=GONE ){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
child.layout(
(int)(params.leftMargin * mScaleFactor),
(int)(params.topMargin * mScaleFactor),
(int)((params.leftMargin + child.getMeasuredWidth()) * mScaleFactor),
(int)((params.topMargin + child.getMeasuredHeight()) * mScaleFactor)
);
child.setLayoutParams(params);
}
}
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
Log.d("onDraw", ""+mScaleFactor);
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility()!=GONE){
child.draw(canvas);
Log.d("onDraw", ""+mScaleFactor);
}
}
canvas.restore();
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));
// Log.d("onScale", ""+mScaleFactor);
temp.invalidate();
invalidate();
return true;
}
}
}
Questions:
How to make Only Floor Map Image to zoom?
Keep Pins non-zoom but Move Relatively?
Get Accurate Click Event when clicked on a pin
Any Suggestions and Answers will be very much appriciated!
Thanks in Advance,
Qamar
An implementation of an HTML map like element in an Android View:
Supports images as drawable or bitmap in layout
Allows for a list of area tags in xml
Enables use of cut and paste HTML area tags to a resource xml (ie, the ability to take an HTML map - and image and use it with minimal editing)
Supports panning if the image is larger than the device screen
Supports pinch-zoom
Supports callbacks when an area is tapped.
Supports showing annotations as bubble text and provide callback if the bubble is tapped
see this link you will got your solution https://github.com/catchthecows/AndroidImageMap