2

I'm considered a newbie in android programming, this is only my second application and i'm learning things as they come. So if there are obvious oversights, bear with me.

As a part of a larger application I'm trying to implement a graph page using AndroidPlot. For now I'm using manually input values for the graph as i'm only testing. I used the tutorial on the AndroidPlot website in order to implements zoom and pan in my graph page. However anytime a touch event occurs, on the emulator and on the actual phone.

Here is the code for the graph page:

public class Graph extends Activity implements OnTouchListener{

Campaign_Db myCamp = new Campaign_Db(this);
private XYPlot myXY;
private PointF minXY;
private PointF maxXY;
ArrayList<String> array_date;
ArrayList<Integer> array_cap;
Array arr_date;
Integer[] arr_cap;
float x,y;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.graph);

    myXY = (XYPlot) findViewById(R.id.myXYplot);
    myXY.setOnTouchListener(this);

    getdata();

    // Create a couple arrays of y-values to plot:
    Number[] series1Numbers = {978307200,  // 2001
            1009843200, // 2002
            1041379200, // 2003
            1072915200, // 2004
            1104537600  // 2005
            };
    Number[] series2 = {1,2,3,4,5};



    // Turn the above arrays into XYSeries':
    XYSeries series1 = new SimpleXYSeries(
            Arrays.asList(series1Numbers),
            Arrays.asList(series2),
            "cups of water");



    // Create a formatter to use for drawing a series using LineAndPointRenderer:
    @SuppressWarnings("deprecation")
    LineAndPointFormatter series1Format = new LineAndPointFormatter(
            Color.rgb(0, 200, 0),                   // line color
            Color.rgb(0, 100, 0),                   // point color
            null);                                  // fill color (none)

    // add a new series' to the xyplot:
    myXY.addSeries(series1, series1Format);


    // reduce the number of range labels
    myXY.setTicksPerRangeLabel(1);
    myXY.setTicksPerDomainLabel(2);

    // get rid of decimal points in our range labels:
    myXY.setRangeValueFormat(new DecimalFormat("0"));

    // customize our domain/range labels
    myXY.setDomainLabel("Date");
    myXY.setRangeLabel("# of cups");

    myXY.setDomainValueFormat(new Format() {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        // create a simple date format that draws on the year portion of our timestamp.
        // see http://download.oracle.com/javase/1.4.2/docs/api/java/text/SimpleDateFormat.html
        // for a full description of SimpleDateFormat.
        private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");

        @Override
        public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {

            // because our timestamps are in seconds and SimpleDateFormat expects milliseconds
            // we multiply our timestamp by 1000:
            long timestamp = ((Number) obj).longValue() * 1000;
            Date date = new Date(timestamp);
            return dateFormat.format(date, toAppendTo, pos);
        }


        @Override
        public Object parseObject(String string, ParsePosition position) {
            // TODO Auto-generated method stub
            return null;
        }


    });


    // by default, AndroidPlot displays developer guides to aid in laying out your plot.
    // To get rid of them call disableAllMarkup():
    myXY.disableAllMarkup();

    myXY.redraw();  
  //Set of internal variables for keeping track of the boundaries
        myXY.calculateMinMaxVals();
        minXY=new PointF(myXY.getCalculatedMinX().floatValue(),myXY.getCalculatedMinY().floatValue());
        maxXY=new PointF(myXY.getCalculatedMaxX().floatValue(),myXY.getCalculatedMaxY().floatValue());

}
// Definition of the touch states
    static final int NONE = 0;
    static final int ONE_FINGER_DRAG = 1;
    static final int TWO_FINGERS_DRAG = 2;
    int mode = NONE;

    PointF firstFinger;
    float lastScrolling;
    float distBetweenFingers;
    float lastZooming;

    public boolean onTouch(View view, MotionEvent event) {

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: // Start gesture
            firstFinger = new PointF(event.getX(), event.getY());
            mode = ONE_FINGER_DRAG;
            break;
        case MotionEvent.ACTION_UP: 
        case MotionEvent.ACTION_POINTER_UP:
            //When the gesture ends, a thread is created to give inertia to the scrolling and zoom 
            Timer t = new Timer();
                t.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        while(Math.abs(lastScrolling)>1f || Math.abs(lastZooming-1)<1.01){ 
                        lastScrolling*=.8;
                        scroll(lastScrolling);
                        lastZooming+=(1-lastZooming)*.2;
                        zoom(lastZooming);
                        myXY.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
                            myXY.redraw();

                    }
                    }
                }, 0);


        case MotionEvent.ACTION_POINTER_DOWN: // second finger
            distBetweenFingers = spacing(event);
            // the distance check is done to avoid false alarms
            if (distBetweenFingers > 5f) {
                mode = TWO_FINGERS_DRAG;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (mode == ONE_FINGER_DRAG) {
                PointF oldFirstFinger=firstFinger;
                firstFinger=new PointF(event.getX(), event.getY());
                lastScrolling=oldFirstFinger.x-firstFinger.x;
                scroll(lastScrolling);
                lastZooming=(firstFinger.y-oldFirstFinger.y)/myXY.getHeight();
                if (lastZooming<0)
                    lastZooming=1/(1-lastZooming);
                else
                    lastZooming+=1;
                zoom(lastZooming);
                myXY.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
                myXY.redraw();

            } else if (mode == TWO_FINGERS_DRAG) {
                float oldDist =distBetweenFingers; 
                distBetweenFingers=spacing(event);
                lastZooming=oldDist/distBetweenFingers;
                zoom(lastZooming);
                myXY.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
                myXY.redraw();
            }
            break;
        }
        return true;
    }




    private void zoom(float scale) {
        float domainSpan = maxXY.x  - minXY.x;
        float domainMidPoint = maxXY.x  - domainSpan / 2.0f;
        float offset = domainSpan * scale / 2.0f;
        minXY.x=domainMidPoint- offset;
        maxXY.x=domainMidPoint+offset;
    }

    private void scroll(float pan) {
        float domainSpan = maxXY.x  - minXY.x;
        float step = domainSpan / myXY.getWidth();
        float offset = pan * step;
        minXY.x+= offset;
        maxXY.x+= offset;
    }

    private float spacing(MotionEvent event) {

        x = event.getX(0) - event.getX(1);
        y = event.getY(0) - event.getY(1);


        return FloatMath.sqrt(x * x + y * y);
    }

     public boolean onInterceptTouchEvent(MotionEvent ev) {
            try {
                return super.onTouchEvent(ev);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                return false;
            } catch (ArrayIndexOutOfBoundsException e) {
                e.printStackTrace();
                return false;
            }
        }

   }

And here is the logcat for the error

06-11 14:59:31.764: E/AndroidRuntime(2203): FATAL EXCEPTION: main
06-11 14:59:31.764: E/AndroidRuntime(2203): java.lang.IllegalArgumentException: pointerIndex out of range
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.MotionEvent.nativeGetAxisValue(Native Method)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.MotionEvent.getX(MotionEvent.java:1974)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.example.damavand.Graph.spacing(Graph.java:231)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.example.damavand.Graph.onTouch(Graph.java:176)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.View.dispatchTouchEvent(View.java:5559)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2058)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1827)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2058)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1827)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2058)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1827)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2058)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1827)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2058)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1827)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1931)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1390)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.app.Activity.dispatchTouchEvent(Activity.java:2364)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1879)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.View.dispatchPointerEvent(View.java:5766)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:2890)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2466)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:845)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2475)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.os.Handler.dispatchMessage(Handler.java:99)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.os.Looper.loop(Looper.java:137)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at android.app.ActivityThread.main(ActivityThread.java:4441)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at java.lang.reflect.Method.invokeNative(Native Method)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at java.lang.reflect.Method.invoke(Method.java:511)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
06-11 14:59:31.764: E/AndroidRuntime(2203):     at dalvik.system.NativeStart.main(Native Method)

I have been reading around and nothing have been helpful. This might be because I am horrible when it comes to motion events and because of that I might have missed on probable solutions. Thanks in advance for any help or guidance.

  • `IllegalArgumentException: pointerIndex out of range` in your code `at com.example.damavand.Graph.spacing(Graph.java:231)`. set a breakpoint at that line and you should see the problem. – David M Jun 11 '13 at 10:56
  • @DavidM After reading up, I found the problem to be regarding the pointerindex values being incorrect. However I have absolutely no idea how I'd be able to solve this issue. – Nami Alejandro Salimi Jun 11 '13 at 11:00
  • 1
    check out AlexBcn's answer below.... – David M Jun 11 '13 at 11:01
  • @DavidM That seems to be the way to go, as I had seen it elsewhere. In both cases I am confused on how to use the event.getPointerCount and where to place it – Nami Alejandro Salimi Jun 11 '13 at 11:05
  • http://www.vogella.com/articles/AndroidTouch/article.html is where i started... – David M Jun 11 '13 at 11:08
  • Yes I have seen that piece before. I get that using the pointer I could get the index for x and y but where would I place the index, instead of (0) or (1) and what would I do for the other? @DavidM – Nami Alejandro Salimi Jun 11 '13 at 11:18

3 Answers3

3

Alright I decided to go about this another way and use a custom-built zoom and pan function made by Marcin Lepicki who had been kind enough to share his work. His way of approching the zoom function is much easier to follow and also tidier as long as coding is considering.

Here is the link to his code for anyone else who might be facing the same issue or looking to work with AndroidPlot

AndroidPlot multitouch zoom & scroll

Hope this has been helpful.

  • This link has been removed. Can you please share the solution. Thanks. – dn_c Oct 21 '15 at 08:45
  • @DivyaNagrath It has been more than two years since I posted this, I no longer remember nor have access to the code that was used here but here is the docs for androidPlot, hopefully it can help you out in some way http://androidplot.com/docs/ – Nami Alejandro Salimi Oct 21 '15 at 09:39
  • I referred that. but I'm getting the same crash issue on touch event. :( – dn_c Oct 21 '15 at 12:05
  • @DivyaNagrath well in that case, I suggest you create another question (since this question wasn't originally meant for AndroidPlot) and post your code and logcat there. You can then leave the link to that question here and I'll try to help out as well. – Nami Alejandro Salimi Oct 21 '15 at 13:01
2

You are accessing to a wrong index event.getX(1) and getY(1) when there are not two

private float spacing(MotionEvent event) {

    x = event.getX(0) - event.getX(1);
    y = event.getY(0) - event.getY(1);


    return FloatMath.sqrt(x * x + y * y);
}

Use event.getPointerCount before

MotionEvent was extended in Android 2.0 (Eclair) to report data about multiple pointers and new actions were added to describe multitouch events. MotionEvent.getPointerCount() returns the number of active pointers. getX and getY now accept an index to specify which pointer’s data to retrieve.

Take a look at Making Sense of Multitouch

AlexBcn
  • 2,450
  • 2
  • 17
  • 28
  • Thank you for the prompt respose. Sorry if i'm being very slow, but where exactly would I place the event.getPointerCount in my code? – Nami Alejandro Salimi Jun 11 '13 at 11:02
  • You are assuming that the indexes are always going to be 0 or 1 and that is not correct on the link I posted you will see he gets the X and Y using code such as `final int pointerIndex = ev.findPointerIndex(mActivePointerId);``final float x = ev.getX(pointerIndex);``final float y = ev.getY(pointerIndex);` – AlexBcn Jun 11 '13 at 11:39
  • Yes I can see how that makes sense. However in the example there, there is no getPointerCount() it confuses me further. How do I differentiate the two pointers and their indices? – Nami Alejandro Salimi Jun 11 '13 at 11:46
0

The exception in spacing() occures because there is a break missing in the switch case block. So the follwing case MotionEvent.ACTION_POINTER_DOWN: for the second finger is executed even you use only one finger.

    case MotionEvent.ACTION_POINTER_UP:
        //When the gesture ends, a thread is created to give inertia to the scrolling and zoom 
        Timer t = new Timer();
            t.schedule(new TimerTask() {
                @Override
                public void run() {
                    while(Math.abs(lastScrolling)>1f || Math.abs(lastZooming-1)<1.01){ 
                    lastScrolling*=.8;
                    scroll(lastScrolling);
                    lastZooming+=(1-lastZooming)*.2;
                    zoom(lastZooming);
                    myXY.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
                        myXY.redraw();

                }
                }
            }, 0);
            break;   // <== the MISSING break!

    case MotionEvent.ACTION_POINTER_DOWN: // second finger
        distBetweenFingers = spacing(event);