0

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.

Screenshot 1

Screenshot 2

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!

Community
  • 1
  • 1
  • Did you solved this? If not try to explain better what you're doing. And it would probably make a lot more sense to set the touch listener on a view higher in the hierarchy instead of the dot view especially as you draw outside of its bounds(from what I see in the images). – user Oct 28 '13 at 14:55
  • @Luksprog No, I havn't solved this - I will edit the main question to try and make it clearer. I have registered every dot view to the touch listener so I can detect firstly which dot was pressed on on `ACTION_DOWN` and then detect which dot view the finger has been dragged over on `ACTION_MOVE` –  Oct 28 '13 at 19:43
  • @Luksprog I have updated the question majorly. I hope it makes more sense now. –  Oct 28 '13 at 21:05

1 Answers1

1

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

user
  • 86,916
  • 18
  • 197
  • 190
  • 1
    Wow!! Thank you so much for the wonderful explanation and example code Luksprog. It makes much more sense now :-). –  Oct 29 '13 at 19:52
  • 1
    I had commented with a problem, but I fixed it. :-) –  Nov 02 '13 at 19:06
  • Hi @Luksprog - I don't know whether you would know this: I would like to release my application to the Play Store which includes your example code. I have modified it slightly, and added a lot more of my own code to the activity. As far as I'm aware, all I need to do is show the licence within my app somewhere. Is this correct? –  Jan 25 '14 at 16:39
  • 1
    @RiThBo The code is released under the Apache license which from my knowledge yes, requires only to show the Apache license in the app(but of course I'm not a lawyer). From my point of view I posted the code to help, you can use the code in whatever way you want. – user Jan 25 '14 at 17:04
  • @Luksprog - I know this is an oldie, but when the line is dragged over each grid - is there a way to identify if the colors are identical ? please – yardie Jun 17 '16 at 11:03
  • @andre3wap Wow, this is an old question. If you need to find out the colors as you drag the line, the case for MotionEvent.ACTION_MOVE in my example should help you. Basically you need to do the same thing I do in the MotionEvent.ACTION_UP case: simply use the getChildForTouch() method with the current x,y values to find out the DotView in which you entered(keep in mind to test leaving the start DotView). Then compare the colors(which you'll need to expose from the DotView class) of the start DotView.mStartView with the one you find. – user Jun 17 '16 at 11:40
  • @Luksprog - haha, I know man. I have been banging my head for the past week trying to figure this out. If you have the time, do you mind writing an example of which you speak based on the class you created please? – yardie Jun 17 '16 at 12:22
  • @andre3wap I don't have time to look through the code, but this should be easy to do. Check this https://gist.github.com/luksprog/83a3a8e75f859bd39287b9e65cb1419c as a high level example. – user Jun 17 '16 at 14:49
  • @Luksprog - Hey man. Thanks alot, that helped me out. I am getting the colors fine now. I only have one more question please please. Say for example I match 2 colors, How would you suggest deleting just those 2 DotView from the screen? – yardie Jun 19 '16 at 02:20
  • 1
    @andre3wap Add a boolean field to the DotView class representing a state like *wasMatched* (initially being false). In the onDraw() method of the DotView class use the wasMatched field to either don't draw anything(if wasMatched is true) or draw the current stuff(the current code, when wasMatched is false). Then, when you match two DotViews(the start and the current one) update the wasMatched field to true for both of them. You do need to add some logic to the touch listener to ignore touches in the DotViews which have wasMatched set to true. – user Jun 19 '16 at 04:09