Android: Dragging Multiple Views - android

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);

Related

What is causing the item(View) in the Container (ViewGroup) class to move quickly and randomly after it has been rotated 90 degrees?

Description:
I have a class called Container which can hold items. In the code below I added only one item to the container. The user can move this item by dragging it and rotating it by 90 degrees by double-clicking on it.
Before double-clicking on it (before applying the rotation), it moves as expected when the user drags the item.
If the user double-clicks the item (applies 90 degrees rotation) and then tries to move it by dragging it, it unexpectedly moves so quickly out of the window and in a random direction.
Demo:
Code:
Container.java
public class Container extends ViewGroup {
public Container(Context context) {
super(context);
}
public Container(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Container(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//Layout all children used fixed position
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i<count; i++) {
View child = getChildAt(i);
int left = child.getLeft();
int top = child.getTop();
int right = child.getRight();
int bottom = child.getBottom();
child.layout(left, top, right, bottom);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i<count; i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Item.java
public class Item extends View {
// default width and height for this item
private static final int WIDTH = 200;
private static final int HEIGHT = 80;
private final Paint mPaint;
private final static int OFFSET = 1;
//Store the click position
private float mStartX = 0;
private float mStartY = 0;
//Used to detect double-click on this item
private long mStartTime = 0;
private static final int DOUBLE_TAP_THRESHOLD = 200;
public Item(Context context, int x, int y) {
super(context);
// Set the size of the item,
// No need to override onMeasure()
setLeft(x);
setTop(y);
setRight(x + WIDTH);
setBottom(y + HEIGHT);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
canvas.drawRect(OFFSET, OFFSET, width-OFFSET, height-OFFSET, mPaint);
}
// Rotate the item if the user double click on it
// And move the item if the user drag it
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
long currentTime = System.currentTimeMillis();
if(currentTime - mStartTime < DOUBLE_TAP_THRESHOLD) {
//Double click detected
// Apply rotation
setRotation(getRotation() + 90); // rotation that cause the problem
return true;
}
mStartTime = currentTime;
mStartX = event.getX();
mStartY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
// compute the new position for the item
float deltaX = event.getX() - mStartX;
float deltaY = event.getY() - mStartY;
float newX = getX() + deltaX;
float newY = getY() + deltaY;
//move the item
setX(newX);
setY(newY);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Container container = findViewById(R.id.container);
container.addView(new Item(this, 100, 200));
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.package.name.Container
android:id="#+id/container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Question: Why does the item move so quickly when rotating it? and how to fix this issue.
Your problem is different frames of reference. X and Y are relative to the view's upper left hand corner. When you rotate and move the view, you're changing the relative coordinates of the view, which is going to lead to some funnky math.
There's 2 solutions to this. The one that changes your code the least is to convert all touch coordienates to a fixed coordinate system first. For example, the parent view's coordinate system, or the screens. view.getX is relative to its parent already.
The second and probably better way to do it- don't have the individual views control that. Do it all in a touch handler of the parent. The parent knows where all its children are and is responsible for laying them out, so this not only is more aligned with how Android works, but it avoids the problem of changing coordinate systems. All you have to do to make that work is make the views ignore all touches (their default behavior) and touches on them will fall through to their parent.
Here is how I fixed this issue. You need to take into account the rotation angle by using the rotation matrix:
x' = x*cos(theta) - y*sin(theta)
y' = x*sin(theta) - y*cos(theta)
There are two ways to implement this:
By using the direct formula
double rotation = Math.toRadians(getRotation());
float newX = (float) (getX() + deltaX*Math.cos(rotation) - deltaY*Math.sin(rotation));
float newY = (float) (getY() + deltaX*Math.sin(rotation) + deltaY*Math.cos(rotation));
//move the item
setX(newX);
setY(newY);
By using the Matrix class
float[] point = new float[] {deltaX, deltaY};
mapPoint(point, getRotation());
float newX = getX() + point[0];
float newY = getY() + point[1];
//move the item
setX(newX);
setY(newY);
Here is the helper method mapPoint:
private void mapPoint(float[] point, float angle) {
Matrix matrix = new Matrix();
matrix.setRotate(angle);
matrix.mapPoints(point);
}

Draw on canvas wherever view is dragged

I am trying to create a simple paint app using canvas. The end product would be for the user to select a tool of choice (pen, marker, eraser, etc) and draw or erase accordingly anywhere the tool is dragged on the canvas.
As of now, I only have a pen and I am trying to draw a line wherever the pen is dragged in the canvas. However, I am finding this task challenging. I can drag the pen and I can draw lines, but I can't draw while I am dragging the pen. I am reaching out to the SO community to bridge these two features.
Below is what I have:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ViewGroup rootLayout;
private int _xDelta;
private int _yDelta;
private RelativeLayout pl;
private ImageView w1;
private boolean clicked1 = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CanvasView canvasView = new CanvasView(MainActivity.this);
w1 = (ImageView) findViewById(R.id.wand1);
pl = (RelativeLayout) findViewById(R.id.coordAct);
RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.coordAct);
rootLayout.addView(canvasView);
RelativeLayout.LayoutParams layoutParams1w2 = new RelativeLayout.LayoutParams(getScreenWidth(), getScreenHeight() / 2);
layoutParams1w2.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
canvasView.setLayoutParams(layoutParams1w2);
w1.setOnTouchListener(new ChoiceTouchListener());
}
public static int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
public static int getScreenHeight() {
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
public class ChoiceTouchListener implements View.OnTouchListener {
public boolean onTouch(View view, MotionEvent event) {
if (!clicked1){
rootLayout = (ViewGroup) w1.getParent();
if (rootLayout != null) {
// detach the child from parent
rootLayout.removeView(w1);
}
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(300, 300);
pl.addView(w1);
w1.setLayoutParams(layoutParams1);
clicked1 = true;
}
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
rootLayout.invalidate();
return true;
}
}
CanvasView.java
public class CanvasView extends View{
Context context;
int width, height;
Bitmap bitmap;
Path path;
Canvas canvas;
Paint paint;
float mX, mY;
static final float TOLERANCE=4;
public CanvasView(Context context) {
super(context);
this.context=context;
path=new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(50);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
bitmap=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
canvas=new Canvas(bitmap);
}
public void startTouch(float x, float y) {
path.moveTo(x, y);
mX = x;
mY = y;
}
public void moveTouch(float x, float y) {
float dx = Math.abs(x-mX);
float dy = Math.abs(y-mY);
if(dx>=TOLERANCE || dy>= TOLERANCE) {
path.quadTo(mX, mY, (x+mX)/2, (y+mY)/2);
mX=x;
mY=y;
}
}
//To clear canvas
public void clearCanvas() {
path.reset();
invalidate();
}
public void upTouch() {
path.lineTo(mX,mY);
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
startTouch(x,y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
default:
return false;
}
invalidate();
return true;
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/coordAct"
tools:context="com.simplepaintapp.MainActivity">
<RelativeLayout
android:id="#+id/parLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3"
android:background="#0000ff">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="28dp"
android:background="#BA9DF7"
android:gravity="bottom"
android:orientation="vertical">
<HorizontalScrollView
android:id="#+id/backgd"
android:layout_width="match_parent"
android:layout_height="100dp"
android:weightSum="1">
<LinearLayout
android:id="#+id/parentLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:weightSum="5">
<ImageView
android:id="#+id/wand1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:src="#drawable/pen" />
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
</RelativeLayout>
Current bug after implementing the dispatchTouchEvent() method inside onTouch()
An update to MainActivity and CanvasView is required. See the new source code below:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ViewGroup rootLayout;
private int _xDelta;
private int _yDelta;
private RelativeLayout pl;
private ImageView w1;
private boolean clicked1 = false;
CanvasView canvasView;
public static int X;
public static int Y;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
canvasView = new CanvasView(MainActivity.this);
w1 = (ImageView) findViewById(R.id.wand1);
pl = (RelativeLayout) findViewById(R.id.coordAct);
RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.coordAct);
rootLayout.addView(canvasView);
RelativeLayout.LayoutParams layoutParams1w2 = new RelativeLayout.LayoutParams(getScreenWidth(), getScreenHeight() / 2);
layoutParams1w2.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
canvasView.setLayoutParams(layoutParams1w2);
w1.setOnTouchListener(new ChoiceTouchListener());
}
public static int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
public static int getScreenHeight() {
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
public class ChoiceTouchListener implements View.OnTouchListener {
public boolean onTouch(View view, MotionEvent event) {
if (!clicked1){
rootLayout = (ViewGroup) w1.getParent();
if (rootLayout != null) {
// detach the child from parent
rootLayout.removeView(w1);
}
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(300, 300);
pl.addView(w1);
w1.setLayoutParams(layoutParams1);
clicked1 = true;
}
X = (int) event.getRawX();
Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
canvasView.dispatchTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
canvasView.dispatchTouchEvent(event);
break;
}
rootLayout.invalidate();
return true;
}
}
CanvasView.java:
public class CanvasView extends View{
Context context;
int width, height;
Bitmap bitmap;
Path path;
public Canvas canvas;
Paint paint;
float mX, mY;
static final float TOLERANCE=4;
public static float x;
public static float y;
public CanvasView(Context context) {
super(context);
this.context=context;
path=new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(50);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
bitmap=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
canvas=new Canvas(bitmap);
}
public void startTouch(float x, float y) {
path.moveTo(x, y);
mX = x;
mY = y;
}
public void moveTouch(float x, float y) {
float dx = Math.abs(x-mX);
float dy = Math.abs(y-mY);
if(dx>=TOLERANCE || dy>= TOLERANCE) {
path.quadTo(mX, mY, (x+mX)/2, (y+mY)/2);
mX=x;
mY=y;
}
}
//To clear canvas
public void clearCanvas() {
path.reset();
invalidate();
}
public void upTouch() {
path.lineTo(mX,mY);
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
startTouch(MainActivity.X,MainActivity.Y-380);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(MainActivity.X,MainActivity.Y-380);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
default:
return false;
}
invalidate();
return true;
}

Draw on the custom image view in android

I am working on a drawing app. And I already create a draw view that allow user to draw on the view.
The problem is , when I draw on it , it exceed the area of the image (please refer to the picture, the yellow line is exceeed the actual photo area), how can I stick the canvas size to the actual image ?
And how the imageview be zoomable? That means when user click on pen button, it draw, when user click again then it become zoom function.
Thanks. The zoom function can be tackle later and the problem of exceed area need to fix first
Here is the custom draw view in xml
<com.example.tool.DrawView
android:id="#+id/draw"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Here is the java
public class DrawView extends ImageView {
private int color = Color.BLACK;
private float width = 4f;
private List<Holder> holderList = new ArrayList<Holder>();
private class Holder {
Path path;
Paint paint;
Holder(int color, float width) {
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
}
public DrawView(Context context) {
super(context);
init();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
holderList.add(new Holder(color, width));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Holder holder : holderList) {
canvas.drawPath(holder.path, holder.paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
holderList.add(new Holder(color,width));
holderList.get(holderList.size() - 1).path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
holderList.get(holderList.size() - 1).path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
public void resetPaths() {
for (Holder holder : holderList) {
holder.path.reset();
}
invalidate();
}
public void setBrushColor(int color) {
this.color = color;
}
public void setWidth(float width) {
this.width = width;
}
}
And I call it by:
DrawView pic = findViewById(R.id.draw);
pic.setImageBitmap(bitmap);
Thanks a lot for helping
Calculate the displayed image size first and then clamp the eventX and eventY to the bound of the displayed image. Code as below:
int imageViewH=imageView.getMeasuredHeight();//height of imageView
int imageViewW =imageView.getMeasuredWidth();//width of imageView
int drawableH =imageView.getDrawable().getIntrinsicHeight();//original height of underlying image
int drawableW=imageView.getDrawable().getIntrinsicWidth();//original width of underlying image
int displayH, displayW; // the shown height and width of the picture.
int leftX, rightX, topY, bottomY; // the shown edges of the picture.
if (imageViewH/drawableH <= imageViewW/drawableW){
displayW = drawableW*imageViewH/drawableH;//rescaled width of image within ImageView
displayH = imageViewH;
leftX = (imageViewW - displayW)/2; // left edge of the displayed image.
rightX = leftX + displayW; // right edge of the displayed image.
topY = 0; // top edg
bottomY = displayH; // bottom edge.
}else{
displayH = drawableH*imageViewW/drawableH;//rescaled height of image within ImageView
displayW = imageViewW;
leftX = 0; // left edge of the displayed image.
rightX = displayW; // right edge of the displayed image.
topY = (imageViewH - displayH)/2; // top edg
bottomY = topY + displayH; // bottom edge.
}
//TODO: clamp the eventX and eventY to the bound of leftX, rightX, topY, bottomY
Note: the code should be used only after your ImageView has its measured height and a drawable.
You can set a width of your DrawView programmatically to ImageView's width, if in ImageView's properties there's a true attribute adjustViewBounds.

Android: Is this not how drawing a canvas works?

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;
}
});
}
}

Zoom and Pinch RelativeLayout

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

Categories

Resources