Am I misunderstanding how translating and scaling a canvas should work? I created a custom view that I can drag and zoom, but it's inside the bounds I set. I thought translating the canvas would redraw the bounds? I want to be able to drag and zoom the image on the whole screen, not in a viewport! I assumed translating the canvas would adjust the viewport.
Am I thinking about this the wrong way?
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
canvas.restore();
}
This is how I created the view. So basically it is stuck in this 250 x 250 box. I need the view to actually drag around.. I get the feeling I'm doing something stupid but I can't produce the behavior I'm looking for.
MultiTouchImageView view = new MultiTouchImageView(this);
view.setLayoutParams(new RelativeLayout.LayoutParams(250, 250));
You can set margins to move view in layout. I've created test project to show the idea, it looks like that:
public class DragView extends View {
private float mLastTouchX;
private float mLastTouchY;
private float mDeltaX;
private float mDeltaY;
public DragView(Context context) {
super(context);
init();
}
public DragView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
mLastTouchX = event.getRawX();
mLastTouchY = event.getRawY();
switch (action) {
case MotionEvent.ACTION_DOWN: {
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) getLayoutParams();
mDeltaX = mLastTouchX - lParams.leftMargin;
mDeltaY = mLastTouchY - lParams.topMargin;
break;
}
case MotionEvent.ACTION_MOVE: {
mLastTouchX = event.getRawX();
mLastTouchY = event.getRawY();
final RelativeLayout.LayoutParams params = (LayoutParams) getLayoutParams();
params.leftMargin = (int) (mLastTouchX - mDeltaX);
params.topMargin = (int) (mLastTouchY - mDeltaY);
setLayoutParams(params);
break;
}
}
return true;
}
});
}
}
Related
I am working on a pinch-to-zoom RecyclerView and have the pinch-to-zoom functionality working, but once zoomed in, the onItemClickListeners are in the wrong locations. Below is the code for the modified RecyclerView. I also made a simple demo that is hosted on GitHub. When trying the demo, when you click on an item, you should get a Toast that says the number of the icon you click on. Try it without zooming, then zoom in and try again to see the issue. I believe that I need to notify the RecyclerView that the onItemClickListener locations need to be updated, but cannot find the correct method to override. Thanks for any help!
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 = View.MeasureSpec.getSize(widthMeasureSpec);
height = View.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: {
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;
}
}
}
No Zoom, Clicked #34, Working
Zoomed In, Clicked #34, Not Working
Update
I thought better of spreading the modifications throughout several of your classes. Here is a link to just PinchRecyclerView.java with all the changes that are needed. I will leave the entire project linked below in case there is some other use for it.
You are changing just the display on the canvas and not the underlying views as azizbekian stated in his comment. If you don't want to go with a custom GridLayoutManager and continue with the approach you have embarked upon, you will need to map screen touches back to your underlying views.
Here is your updated project on GitHub that does the mapping. Here are the basic changes:
Add a touch listener to the PinchRecyclerView.
Translate touch coordinates on the PinchRecyclerView to the corresponding position within your underlying layout.
Identify through searching the intended item view and do your click processing.
If you try this updated app and, as you can see in the video, the touch listener is also triggered on a pinch, so that is a bug.
Here is an architecture of how the recyclerview works
Adapter supplies data to > ViewHolder
ViewHolder stores the data items findviewbyid's in memory/saved location to prevent finding views every time you scroll for performance issues. Next...its
ViewHolder > LayoutManager
Layout manager stores the position of each item and does the job of updating their positions on pinch to zoom, scroll etc.
I think the way to solve this issue is to try and call
adapter.notifyDataSetChanged();
Inside a method that returns true when zoom activity is detected. Do not do this in adaper class do it in zoom activity class.
This should update the LayoutManager/ViewHolder and reassign ids.
Or
adapter.notifyItemRangeChanged(position, list.size());
To tell that item range has been changed
You can see more on this
How to update RecyclerView Adapter Data?
How do I make RecyclerView update its layout?
Both Answered
Why not just add the click listeners in your RecyclerView.ViewHolder:
itemView.setOnClickListener(...);
And as azizbekian already stated, use your own LayoutManager for the layout.
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 am arranging my views inside HorizontalScrollView and my each view(DrawView.java) contains rectangle. When the activity only contain this view, pinch zoom functionality is working fine(See Code 1 and respective image 1) but when I am arranging multiple of these views in HorizontalScrollView, it's not working properly because touch events of this view are messing up with touch events of HorizontalScrollView(Code 2 and Image 2).
Someone please help me to fix this bug.
This is a code for activity in which pinch zoom is working fine.
public class MainActivity extends Activity {
DrawView drawView;
public class DrawView extends View {
Paint paint = new Paint();
public int recog=-1; // this variable will tell if onDraw is called for first time or is called by listener usinf invalidate()
boolean listener=false;
int data[] = new int[] {200,200,200,200,200,200,200,200,200,200,200,200};
private float MIN_ZOOM = 0.1f;
private float MAX_ZOOM = 10f;
private float mPosX;
private float mPosY;
boolean zoom=false;
private ScaleGestureDetector detector;
float width=200;
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;
public DrawView(Context context) {
this(context, null);
}
public DrawView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.GREEN);
canvas.save();
if(listener ==true)
mScaleFactor+=0.4;
Log.e("Ronak","Listener"+listener);
Log.e("Ronak","ScaleFactor"+mScaleFactor);
canvas.scale(mScaleFactor, 1);
canvas.drawRect(0.0f, 0.0f, 300.0f, 100.0f, paint);
makeLinesinRange(0,100,300,100,200, 5, canvas,100);
width=mScaleFactor*300;
canvas.restore();
requestLayout();
}
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 onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
// int width = MeasureSpec.getSize(widthMeasureSpec);
//int height = (int)(width * 0.5f);
setMeasuredDimension((int)width, 350);
}
private void makeLinesinRange(int x1, int y1, int x2, int y2, int size, int divisions, Canvas canvas,int width)
{
paint.setStrokeWidth(6);
paint.setColor(Color.DKGRAY);
float kk=(x2-x1)/(divisions-1);
for(int i=0;i<divisions;i++)
{
canvas.drawLine(x1+i*kk, y1, x1+i*kk, y2+size, paint);
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
Log.e("Ronak","scale "+mScaleFactor);
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.3f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawView = new DrawView(this);
drawView.setBackgroundColor(Color.WHITE);
setContentView(drawView);
}
}
DrawView.java. This is a code of single view. I am arranging multiple of these views in HorizontalScrollView
public class DrawView extends View {
Paint paint = new Paint();
public int recog=-1; // this variable will tell if onDraw is called for first time or is called by listener usinf invalidate()
boolean listener=false;
int view_number=0;
int data[] = new int[] {200,200,200,200,200,200,200,200,200,200,200,200};
int lines[] = new int[] {4,5,7,8,2,6,6,3,2,1,1,6};
String[] type=new String[]{"Song","Music","Magazine","Audible","Videos","Apps","Other"};
private static float MIN_ZOOM = 0.1f;
private static float MAX_ZOOM = 10f;
private float mPosX;
private float mPosY;
boolean zoom=false;
private ScaleGestureDetector detector;
float width=700;
float height=500;
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;
public DrawView(Context context) {
this(context, null);
}
public DrawView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.BLUE);
canvas.save();
if(listener ==true)
mScaleFactor+=0.4;
Log.e("Ronak","Listener"+listener);
Log.e("Ronak","ScaleFactor"+mScaleFactor);
canvas.scale(mScaleFactor, 1);
canvas.drawRect(0.0f, 0.0f, 300.0f, 70.0f, paint);
makeLinesinRange(0,70,300,70,200, lines[view_number], canvas,100);
width=mScaleFactor*300;
height=mScaleFactor*100;
/*int start=0;
int temp=(int)width/lines[view_number];
for(int i=0;i<lines[view_number];i++)
{
canvas.drawText(type[i], 50, 100, paint);
start=temp;
temp+=temp;
}*/
canvas.restore();
requestLayout();
}
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;
}
public boolean onInterceptTouchEvent(MotionEvent event) {
/* switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mIsBeingDragged = false;
break;
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
float xDelta = Math.abs(x - mLastX);
float yDelta = Math.abs(y - mLastY);
float yDeltaTotal = y - mStartY;
if (yDelta > xDelta && Math.abs(yDeltaTotal) > mTouchSlop) {
mIsBeingDragged = true;
mStartY = y;
return true;
}
break;
}*/
Log.e("Ronak","Here");
return false;
}
#Override protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
// int width = MeasureSpec.getSize(widthMeasureSpec);
//int height = (int)(width * 0.5f);
setMeasuredDimension((int)width, 350);
}
private void makeLinesinRange(int x1, int y1, int x2, int y2, int size, int total_events, Canvas canvas,int width)
{
paint.setStrokeWidth(6);
paint.setColor(Color.MAGENTA);
if(total_events<1)
{
return;
}
if(total_events==1)
{
canvas.drawLine(((x1+x2)/2), y1, (x1+x2)/2, y2+size, paint);
}
else if(total_events==2)
{
canvas.drawLine(((x1+x2)/3), y1, (x1+x2)/3, y2+size, paint);
canvas.drawLine((2*(x1+x2)/3), y1, 2*(x1+x2)/3, y2+size, paint);
}
else
{
paint.setStrokeWidth(6);
paint.setColor(Color.MAGENTA);
float kk=(x2-x1)/(total_events-1);
for(int i=0;i<total_events;i++)
{
canvas.drawLine(x1+i*kk, y1, x1+i*kk, y2+size, paint);
}
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
Log.e("Ronak","scale "+mScaleFactor);
// 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 also have a class which is extending HorizontalScrollView and is calling the above class(DrawView.java) for fitting multiple of these view in horizontal scrolling. Is there ant need to override the onTouch function of this class?
First: YIKES! Don't call requestLayout() from onDraw() -- or you'll get 100% load, all it's ever going to do is relayout and redraw itself.
Second: Remove onInterceptTouchEvent() from DrawView. Since it extends View -- and Views don't have this method -- it'll not get called as part of the touch dispatch process anyway.
Third: which one's not working -- scrolling or scaling?
Read this thread, it's got some insights on how to handle multiple gesture-consuming components
I have a problem on dragging multiple Views in android. I have a two circle created using a Canvas. The problem is that I can only drag one Circle and I can't drag the other Circle. It seems that the first Circle covers the entire screen, and when I try to drag the 2nd Circle still the 1st Circle is moving.
Here is my code.
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}}
DragSource.java
public class DragSource extends View {
private Paint viewPaint;
private float startX;
private float startY;
private float touchOffsetX;
private float touchOffsetY;
private float x = 30;
private float y = 30;
private static final float RADIUS = 30;
//needed for finding drop target:
//the constructor:
public DragSource(Context context, AttributeSet attrs) {
super(context, attrs);
viewPaint = new Paint();
viewPaint.setColor(Color.RED);
viewPaint.setAntiAlias(true);
}
public boolean onTouchEvent(MotionEvent mEvent) {
int eventAction = mEvent.getAction();
switch(eventAction)
{
case MotionEvent.ACTION_DOWN:
startX = x;
startY = y;
touchOffsetX = mEvent.getX();
touchOffsetY = mEvent.getY();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
x = startX + mEvent.getX() - touchOffsetX;
y = startY + mEvent.getY() - touchOffsetY;
break;
}
return true;
}
public void draw(Canvas c) {
int w = c.getWidth();
int h = c.getHeight();
c.drawCircle(x, y, RADIUS, viewPaint);
this.invalidate();
}}
my activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" ><com.example.trialsdrag.DragSource
android:layout_width="wrap_content"
android:layout_height="wrap_content"/><com.example.trialsdrag.DragSource
android:layout_width="wrap_content"
android:layout_height="wrap_content"/></RelativeLayout>
What's happening is your views' size is the actually the whole screen size, and in onTouchEvent, your simply changing the drawing on them (moving the circle).
What you need to do is set android:layout_height and android:layout_width for the DragSource in your main_activity.xml, and then change the layout margins dynamically in onTouchEvent using:
RelativeLayout.LayoutParams params = (LayoutParams) getLayoutParams();
params.setMargins(x+params.leftMargin, y+params.topMargin, 0, 0);
setLayoutParams(params);
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