How much priority does scrollBy have over canvas onDraw? - android

This is a basic code for detecting touch + drawing a custom view :
public boolean onTouchEvent(MotionEvent event) { //Basic onTouch code for scrolling along the Y axis
super.onTouchEvent(event);
if(event.getActionMasked()==MotionEvent.ACTION_DOWN){
mPrevious = event.getY();
}
if(event.getActionMasked()==MotionEvent.ACTION_MOVE){
float distance = mPrevious - event.getY();
scrollBy(0,Math.round(distance));
mPrevious = event.getY();
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Basic drawing of a circle at (200,200)
canvas.drawCircle(200f,200f,50f, defaultPaint);
//canvas.setMatrix(new Matrix());
Log.d("CANVAS", "("+canvas.getMatrix().toString()+")");
}
I'm working with a custom view, I'm trying to implement user scrolling functionality using scrollBy, but it looks like I'll have to create the logic myself.
I still want to understand how this function works. When I print log the canvas matrix in the last line, it correctly displays the new coordinates, and onDraw is called every time the touch moves.
But if I uncomment canvas.setMatrix, suprisingly nothing changes functionality-wise. The only difference now is that the console log shows that the canvas matrix is always equal to identity mx, even if it's correctly being scrolled. Why? How can scrollBy completely overwrite canvas drawing?

Related

Slow response onTouchEvent in Android View with background present

all!
I'm trying to figure out have why I have a slow response in Android View in onTouchEvent method. It's a simple TicTacToe game , i removed all the logic, and only things that concern the problem are left.
So far The onTouchEvent method gets coordinates and then calls onDraw method which simply draws a circle. The onDraw method also draws the background, which is 960x1280 jpg file with the size of 366 kB. It works but i have a delay between touching the screen and getting the circle drawn. It's a few seconds on emulator and about 0.5 seconds on my Samsung. I noticed that without background it works perfectly. But at the same time the problem seems to be not in drawing the background. I mean, that if i call invalidate() several times in a row drawing a few more figures, they are all drawn instantly, we have delay only before the first figure. So as I understand somehow onTouchEvent method reacts differently depending if there is a background.
I've been searching but the only thing i found was to add a 16 ms delay (it is in the code, commented) but it didn't help.
If to take a smaller pic , i have a better response, but i need this big picture to suit different phones.
So in conclusion , the problem is that response time depends on the size of background pic, but at the same time it's not a problem of drawing this big picture, only for onTouchEvent to respose. I am confused -) How can that be?
Please, help me if you can.
Here is the code:
public class GameViewBug extends View {
private Paint mCirclePaint;
private Bitmap bitmap;
private float touchX;
private float touchY;
public GameViewBug(Context context) {
super(context);
init();
}
public void init() {
mCirclePaint = new Paint();
mCirclePaint.setColor(Color.RED);
mCirclePaint.setStrokeWidth(10);
mCirclePaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.kletka3);
canvas.drawBitmap(bitmap, 0, 0, null);
//canvas.drawColor(Color.WHITE);
canvas.drawCircle(touchX, touchY, 50, mCirclePaint);
}
public boolean onTouchEvent(MotionEvent event) {
// try {
// Thread.sleep(16);
// }catch (InterruptedException e) {}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touchX = event.getX();
touchY = event.getY();
}
invalidate();
return true;
}
}
The problem is, that you perform heavy computational operation on main thread in method, which is called very often:
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.kletka3)
You should load that resource once (suggest using Glide library for displaying images), and do all computations on background thread.

Drawing a line following finger - motionevent.getX() and getY() incorrect UPDATE

UPDATE: please read entire question again :-)
Background:
I have created a grid of dots by creating a custom view and then adding these views to a TableLayout. The aim is for me to be able to draw a line from one of these dots to another dot in a way so that when a dot is pressed, a line is started from the center of the pressed dot to the point where the finger is currently touching. When the finger is dragged over another dot, the line then finishes at the center of that dot. The result being a line drawn from the center of the pressed dot to the center of the dot which was dragged over by the finger.
To create a line going over the TableLayout, I created a new custom view which just created a line between points with the canvas.drawLine() method. I then created a FrameLayout in which I put the TableLayout and the LineView. This meant the line view would be able to be drawn on top of the TableLayout.
Current Situation:
I have added touch listener in the DotView class so that it can have its own “touch feedback”. There is also another touch listener in the activity which I am trying to use to get the points needed to draw the line. The touch listeners are working fine as far as I can tell - they are not interfering with each other.
The problem is getting the correct coordinates to plot the line. I have tried a number of ways to get the coordinates of the center of the dot that was pressed (the starting point of the line), but I still have not managed it. This question, has more information: Get the coordinates of the center of a view . The second part is getting the correct end point of the line. For the purposes of this question, I would just like the end of the line to follow the position of the finger. I thought it was as easy as motionevent.getX() / getY() but this hasn’t worked. What happened instead was that there was a mix up between the coordinates on a scale relative to the layout of the dot and the coordinates relative to the whole layout/screen.
Simply put: the getX() and getY() values are incorrect, and this is what I am trying to solve here.
As shown in the screenshots, when I press down on a dot, the start point of the line is roughly in the right place, but the end point is way off. I can’t really explain why.
I have tried getRawX() and getRawY() and they return more much more accurate values, but they are still incorrect by the amount of padding (or something like that - I don’t 100% understand).
This shows my code
In my Activity :
LineView test;
FrameLayout fl;
float startPointX = 0;
float startPointY = 0;
// Removed
#Override
public boolean onTouch(View view, MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
int[] loc = new int[2];
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (view instanceof DotView) {
DotView touchedDv = (DotView) view;
int dotColor = touchedDv.getColor();
test.setColor(dotColor);
float[] f = touchedDv.getDotCenterLocationOnScreen();
startPointX = f[0];
startPointY = f[1];
test.setPoints(startPointX, startPointY, eventX, eventY);
fl.addView(test);
}
vib.vibrate(35);
return false;
case MotionEvent.ACTION_MOVE:
test.setPoints(startPointX, startPointY, eventX, eventY);
break;
case MotionEvent.ACTION_UP:
fl.removeView(test);
return false;
default:
return false;
}
return true;
}
And finally the LineView:
public class LineView extends View {
public static final int LINE_WIDTH = 10;
Paint paint = new Paint();
float startingX, startingY, endingX, endingY;
public LineView(Context context) {
super(context);
paint.setColor(Color.MAGENTA);
paint.setStrokeWidth(LINE_WIDTH);
}
public void setPoints(float startX, float startY, float endX, float endY) {
startingX = startX;
startingY = startY;
endingX = endX;
endingY = endY;
invalidate();
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawLine(startingX, startingY, endingX, endingY, paint);
}
NOTES:
I am calling the top left hand dot "dot 1".
The screenshots may not be entirely accurate as my finger moves
slightly when I take the screenshot, but the behaviour I described is
happening.
If any other information/code is wanted, I will happily provide it.
Thanks for taking the time to read this - sorry it is so long!
I have added touch listener in the DotView class so that it can have
its own “touch feedback”. There is also another touch listener in the
activity which I am trying to use to get the points needed to draw the
line. The touch listeners are working fine as far as I can tell - they
are not interfering with each other.
I don't quite see why you need both touch listener. To draw the line the touch listener on the TableLayout should be more than enough.
The problem with your code(or at least from what I've seen) is that you use the getLocationOnScreen(coordsArray) method without translating the returned values back to the coordinates system of the LineView. For example, you get the coordinates of a DotView which will be x and y. You then use this values in the LineView. But, the LineView when it will do its drawings will use its own(standard) coordinates system which places the top-left of the view at (0,0) and the coordinates will not match. Here's an example: suppose you touch the very first DotView which has a height of 50 and the y returned by the getLocationOnScreen() method is 100. You calculate the center of the DotView wich will come be at 125(100 + 50 / 2). Using this value in the LineView will be way off the normal position as the actual drawing will be done at 125, which in screen coordinates will visually translate to a y of 225 (100 returned by getLocationOnScreen() + 125).
Anyway I've made a small example using the getLocationOnScreen() method to do what you're trying to do(which you can find here).

Pin several images at touch points - Android

Am trying to place pins wherever the user touches on an imageView. Assume a map (like Google Maps) & the user touches a point, say point A, and a pin is drawn at that point. Then, the user touches point B, then another pin (not the same previous pin relocated!) needs to be drawn at point B and so on. Right now, am able to draw a pin at the point where the user touches on the screen like this :
#Override
public void onDraw(Canvas canvas) {
....
Bitmap marker = BitmapFactory.decodeResource(getResources(),
R.drawable.icon_locationmarker);
canvas.drawBitmap(marker, mLastTouchX, mLastTouchY, null);
....
canvas.restore();
}
However, I don't want to relocate one pin across the screen wherever the user touches (which is what the above code is doing). I want to put several pins at all points wherever user touches. Am new to Android. Please help.
Eluvatar is right, you needs to create a list to store all your mark. Here's the sample of code. Remember, do add list when only motionEvent is either Action_UP or Action_DOWN only. Otherwise, there will be full of point.
public ArrayList<Coordinate> pointsList;
#Override
public void onDraw(Canvas canvas) {
....
Bitmap marker = BitmapFactory.decodeResource(getResources(),
R.drawable.icon_locationmarker);
for(Coordinate coor : pointsList){
canvas.drawBitmap(marker, coor.x, coor.y, null);
}
....
canvas.restore();
}
public View.OnTouchListener mListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
pointsList.add(new Coordinate(event.getX(), event.getY()));
}
return false;
}
};
class Coordinate{
float x;
float y;
public Coordinate(float x, float y){
this.x = x;
this.y = y;
}
}
edit: change int x,y to float x,y
you need to create a list of "touch points". then on touch add a new touch point to the list, then onDraw you iterate through that list and draw a marker on each point.
you'll also need to make sure you save the list of touch points on save instance state otherwise you'll lose them on rotate and on activity pause.

Technique to make a canvas drawLine() clickable?

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

android: how to add button or text box on canvas

I am making a canvas and setting its background which is an image
I am adding text on it by canvas. Drawtext method which works perfectly alright
now I want these text to be clickable but i couldn't find any method
The other method I could think of was to add text box on canvas add write on click event of these text box but could not find any example related to this can anybody suggest what to do.
Canvas is a space where you can just draw some graphics, thus the only way to do what you want is detecting when the user click the surface the canvas is drawn on (e.g. a SurfaceView), and using the coordenates you just fire an event. Of course, you need to verify whether the click was done on the specific part you want (e.g. the area where you drew a button or something).
Use the onTouchEvent method. Here is an example I used for finding out if the user's click coordinates are in a List of rectangles (aka buttons):
#Override
public boolean onTouchEvent( MotionEvent event) {
super.onTouchEvent(event);
int x = (int)event.getX();
int y = (int)event.getY();
xStored = x; yStored=y;
if (event.getAction()==MotionEvent.ACTION_UP){
}else if(event.getAction()==MotionEvent.ACTION_DOWN){
System.out.println("Touching down!");
for(Rect rect : rectangles){
if(rect.contains(x,y)){
System.out.println("Touched Rectangle, start activity."+x+","+y);
invalidate();
}else{
}
}
}else if(event.getAction()==MotionEvent.ACTION_MOVE){
}
this.postInvalidate();
return true;
}

Categories

Resources