I have a table-top style game board consisting of 10x10 squares. Each square is a PNG image. On top of these squares I want to place tiles which can be drag and dropped on top of the squares.
What would be my best approach concerning Views?
Is it possible to have two layers where layer one is a grid of ImageView's which represents the board. Would it then be possible to let the tile be an ImageView also which could be "stacked" on top of the ImageView's which represents the board?
Any good design ideas are welcome!
Thanks.
/ Henrik
Override onTouchEvent in your View:
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!mEnable) {
return false;
}
int action = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
onTouchActionDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
onTouchActionMove(x, y);
break;
case MotionEvent.ACTION_UP:
onTouchActionUp(x, y);
break;
}
return true;
}
Then in either onTouchActionUp/Down just use the co-ordinates and redraw your image resource, i.e. invalidate() your view.
Override onDraw to draw the dragged graphic:
#Override
protected void onDraw(Canvas canvas)
drawMyDragGfx();
}
I did a override on onDraw and onTouchEvent in my View. I.e. I did the drag and drop drawing all by myself.
Worked like a charm :-)
Related
I am creating a POC on how to resize an imageView in android. I want to put 4 squares in the corner of the ImageView so I can drag the squares to resize. What should I use to get this result?
Get the coordinate of the corners
Draw rectangles at those coordinates with method like Canvas.drawRect(float left, float top, float right, float bottom, Paint paint)
Get the coordinates of the touch point from onTouch()
Resize the imageview with the new size
Get Coordinates of Touch Point
The class (suppose it is an Activity) should implements OnTouchListener, and register the listener to itself
public class MyActivity extends Activity implements OnTouchListener {
...
setOnTouchListener(MyActivity.this);
...
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
// Get the coordinates
int x = (int)event.getRawX();
int y = (int)event.getRawY();
// Resize the imageView
resize();
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
Resize the ImageView
You can use ImageView.setMaxHeight(), ImageView.setMaxWidth() to resize the imageview
By the way, does the imageView you want to resize is in fact an image? In that case, using drawable might be a better idea
Maybe an overlay is what you need.
Create a 9-patch drawable with the squares and corners and put that layer on top of your imageview.
I'm working on an app that plots nodes on a map, and each node has edges that are represented by a line between them. I've drawn the edges using Canvas and drawLine(), but it would be useful if the lines themselves could be clickable. By that I mean a method of allowing the user to touch the line or think they're touching the line and an event can trigger. (like display edge info, etc...)
I can't rightly attach a touch event to a line I've drawn with Canvas, so I was thinking of placing ImageViews inbetween the ends of each edge line that's drawn. The ImageView could be a dot so it's clear where the touch event triggers.
Does anyone have any other suggestions? I'm mainly looking for ideas that I've missed. Maybe there's something in the Android API that can help with this that I'm unaware of.
Thanks in advance for any tips!
Use a path to draw the line:
Path linePath;
Paint p;
RectF rectF;
float point1X, point1Y, point2X, point2Y;
// initialize components
// draw the line
linePath.moveTo(point1X, point1Y);
linePath.lineTo(point2X, point2Y);
canvas.drawPath(linePath, p);
linePath.computeBounds(rectF, true);
Override onTouchEvent(MotionEvent):
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (rectF.contains(touchX, touchY)) {
// line has been clicked
}
break;
}
return true;
}
I have created my own component which extends RelativeLayout, inside it I have a field of View type which is being positioned with margins:
Blue quad represents my parent view, and a red circle is a child.
Now I want to allow user to move this red circle while moving a finger on a screen.
This is my onTouchEvent() method:
#Override
public boolean onTouchEvent(MotionEvent event) {
int eventAction = event.getAction();
switch (eventAction) {
case MotionEvent.ACTION_DOWN:
// finger touches the screen
break;
case MotionEvent.ACTION_MOVE:
marginX = (int)event.getX();
marginY = (int)event.getY();
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)pointer.getLayoutParams(); // pointer is this red circle
lp.setMargins(marginX, marginY, 0, 0);
break;
case MotionEvent.ACTION_UP:
// finger leaves the screen
break;
}
return true;
}
But I quickly found out that it doesn't refresh it's position while user is moving his finger on a screen, but instead it does it only when user releases his finger from the screen. So basically, when I move my finger, circle stays in a same position, but when I stop moving, circle is drawn in new position. I tried using invalidate() method on both, parent and child views, but it didn't help. I assume that it may have something to do with UI thread, but I don't have access to runOnUiThread()' method as it's not anActivity`.
EDIT:
I passed an activity to my class, so that I can use runOnUiThread(). My case: ACTION_MOVE now looks like this:
case MotionEvent.ACTION_MOVE:
marginX = (int)event.getX();
marginY = (int)event.getY();
activity.runOnUiThread(new Runnable() {
public void run() {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)pointer.getLayoutParams();
lp.setMargins(marginX, marginY, 0, 0);
pointer.invalidate();
}
});
break;
If it's simple pointer you can draw it on Canvas as Drawable:
#Override
protected void onDraw(Canvas canvas) {
pointer.setBounds(marginX, marginY, marginX + pointerWidth, marginY + pointerHeight);
pointer.draw(canvas);
super.onDraw(canvas);
}
And then call invalidate() on pointer in onTouchEvent() method.
I think you need drag and drop function
Refer http://www.vogella.com/articles/AndroidDragAndDrop/article.html
I have a view, and I want to move it with finger. I wanted to get the xDelta and yDelta and just translate the matrix of the view (not image view, any view, RelativeLayout for example).
I am wondering how to do that:
Overriding RelativeLayout's onDraw and apply translation matrix there. Is that possible?
I am currently doing it with applying animation on each onTouch event. The animation is with duration 0 and the start/end of x/y is the same (setFillAfter = true). It just puts the view where I want. But isn't that too expensive?
Can't I manipulate the matrix of any view directly through onDraw? I tried something like this:
#Override
protected void onDraw(Canvas canvas) {
if (getDx() != 0 && getDy() != 0) {
m = canvas.getMatrix();
m.postTranslate(dx, dy);
canvas.setMatrix(m);
}
super.onDraw(canvas);
}
But it seems that I am doing it wrong.
If you want to implement drag gesture then you can use onTouchListener and in its MotionEvent.ACTION_MOVE set padding using setPadding to that view.
Here is the sample code:
case MotionEvent.ACTION_MOVE:
yourView.setPadding((int)event.getRawX(), (int)event.getRawY(), 0, 0);
Sample for onTouchListener :-
OnTouchListener drag= new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_MOVE:
img.setPadding((int)event.getRawX(), (int)event.getRawY(), 0, 0);
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
} return true;
}
};
You need to create the dragging image at run time and have to adjust its position. In this sample code I have not adjusted the position so the dragged image is not created at the position of touch. You also need to consider the height and width of the image to be dragged.
Hope it helps !
I hate to be the one to tell you that your problem is even easier than you thought, but here's what it looks like:
#Override
protected void onDraw(Canvas canvas) {
if (getDx() != 0 && getDy() != 0) {
canvas.translate(getDx(), getDy());
}
super.onDraw(canvas);
}
Hope this helps!
If you want to Move your layout permanently (In case of temporary, we can use animation ) then Use Drag And Drop API from android Official Page . Hope this will help you
Couple of things that would help in achieving it:
Make sure that your view originally has enough space in the direction you want to use. Like 'match_parent' etc
Use GestureDetector.onSimpleGestureListener and override onDraw ( return true;) , onScroll -- connect it using onTouchEvent and you will directly get the distance , or you can calculate yourself by using the event1 and event2
Store this information in variable(s) and call invalidate()
in your onDraw method - use this information by canvas.translate(..use those variables..); super.onDraw(canvas);
and your will achieve the result
PS: The drawing starts by taking left-top as 0,0
In Android, I need to be able to draw simple graphics (points, lines, circles, text) on a large scrollable canvas (i.e the phone's screen is a viewport in a much larger area). I have hunted for tutorials on how to do this without success.
"World Map FREE" on Android market is a good example of the sort of effect I need to achieve.
This must be a common problem, so I'm surprised I can't find examples.
I've had to implement something fairly recently of this sort. Basically I had coordinates in my custom view that would shift where objects would be drawn, via some auxiliarry functions:
public class BoardView extends View {
//coordinates to shift the view by
public float shiftX=0f;
public float shiftY=0f;
//used in the dragging code
public float lastX=-1f;
public float lastY=-1f;
/*...*/
//functions that take into account the shifted x and y values
//pretty straightforward
final public void drawLine(Canvas c,float x1,float y1,float x2,float y2,Paint p){
c.drawLine(x1+shiftX, y1+shiftY, x2+shiftX, y2+shiftY, p);
}
final public void drawText(Canvas c,String s,int x,int y,Paint p){
c.drawText(s, x+shiftX, y+shiftY, p);
}
/*etc*/
To actually implement the dragging, I wrote a custom onTouchEvent method to implement dragging:
public boolean onTouchEvent(MotionEvent event){
int eventaction=event.getAction();
float x=event.getX();
float y=event.getY();
switch(eventaction){
case MotionEvent.ACTION_DOWN:
time=System.currentTimeMillis();//used in the ACTION_UP case
break;
case MotionEvent.ACTION_MOVE:
if(lastX==-1){ lastX=x;lastY=y;}//initializing X, Y movement
else{//moving by the delta
if(Math.abs(x-lastX)>1 || Math.abs(y-lastY)>1){//this prevents jittery movement I experienced
shiftX+=(x-lastX);//moves the shifting variables
shiftY+=(y-lastY);//in the direction of the finger movement
lastX=x;//used to calculate the movement delta
lastY=y;
invalidate();//DON'T FORGET TO CALL THIS! this redraws the view
}
}
break;
case MotionEvent.ACTION_UP: //this segment is to see whether a press is a selection click(quick press)
//or a drag(long press)
lastX=-1;
if(System.currentTimeMillis()-time)<100)
try {
onClickEvent(x,y);//custom function to deal with selections
} catch (Exception e) {
e.printStackTrace();
}
break;
}
return true;
}
Look into tile engines, there's a lot to find on Google about them.