1/*
2 * Copyright 2012 AndroidPlot.com
3 *
4 *    Licensed under the Apache License, Version 2.0 (the "License");
5 *    you may not use this file except in compliance with the License.
6 *    You may obtain a copy of the License at
7 *
8 *        http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *    Unless required by applicable law or agreed to in writing, software
11 *    distributed under the License is distributed on an "AS IS" BASIS,
12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *    See the License for the specific language governing permissions and
14 *    limitations under the License.
15 */
16
17package com.androidplot.demos;
18
19import java.text.DecimalFormat;
20import java.util.Random;
21
22import android.app.Activity;
23import android.graphics.Color;
24import android.graphics.Paint;
25import android.graphics.PointF;
26import android.os.Bundle;
27import android.view.MotionEvent;
28import android.view.View;
29import android.view.View.OnTouchListener;
30import android.widget.Button;
31
32import com.androidplot.Plot;
33import com.androidplot.xy.BoundaryMode;
34import com.androidplot.xy.LineAndPointFormatter;
35import com.androidplot.xy.SimpleXYSeries;
36import com.androidplot.xy.XYPlot;
37
38/***********************************
39 * @author David Buezas (david.buezas at gmail.com)
40 * Feel free to copy, modify and use the source as it fits you.
41 * 09/27/2012 nfellows - updated for 0.5.1 and made a few simplifications
42 */
43public class TouchZoomExampleActivity extends Activity implements OnTouchListener {
44    private static final int SERIES_SIZE = 200;
45    private XYPlot mySimpleXYPlot;
46    private Button resetButton;
47    private SimpleXYSeries[] series = null;
48    private PointF minXY;
49    private PointF maxXY;
50
51    public void onCreate(Bundle savedInstanceState) {
52        super.onCreate(savedInstanceState);
53        setContentView(R.layout.touch_zoom_example);
54        resetButton = (Button) findViewById(R.id.resetButton);
55        resetButton.setOnClickListener(new View.OnClickListener() {
56            @Override
57            public void onClick(View view) {
58                minXY.x = series[0].getX(0).floatValue();
59                maxXY.x = series[3].getX(series[3].size() - 1).floatValue();
60                mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.FIXED);
61
62                // pre 0.5.1 users should use postRedraw() instead.
63                mySimpleXYPlot.redraw();
64            }
65        });
66        mySimpleXYPlot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
67        mySimpleXYPlot.setOnTouchListener(this);
68        mySimpleXYPlot.getGraphWidget().setTicksPerRangeLabel(2);
69        mySimpleXYPlot.getGraphWidget().setTicksPerDomainLabel(2);
70        mySimpleXYPlot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT);
71        mySimpleXYPlot.getGraphWidget().setRangeValueFormat(
72                new DecimalFormat("#####"));
73        mySimpleXYPlot.getGraphWidget().setDomainValueFormat(
74                new DecimalFormat("#####.#"));
75        mySimpleXYPlot.getGraphWidget().setRangeLabelWidth(25);
76        mySimpleXYPlot.setRangeLabel("");
77        mySimpleXYPlot.setDomainLabel("");
78
79        mySimpleXYPlot.setBorderStyle(Plot.BorderStyle.NONE, null, null);
80        //mySimpleXYPlot.disableAllMarkup();
81        series = new SimpleXYSeries[4];
82        int scale = 1;
83        for (int i = 0; i < 4; i++, scale *= 5) {
84            series[i] = new SimpleXYSeries("S" + i);
85            populateSeries(series[i], scale);
86        }
87        mySimpleXYPlot.addSeries(series[3],
88                new LineAndPointFormatter(Color.rgb(50, 0, 0), null,
89                        Color.rgb(100, 0, 0), null));
90        mySimpleXYPlot.addSeries(series[2],
91                new LineAndPointFormatter(Color.rgb(50, 50, 0), null,
92                        Color.rgb(100, 100, 0), null));
93        mySimpleXYPlot.addSeries(series[1],
94                new LineAndPointFormatter(Color.rgb(0, 50, 0), null,
95                        Color.rgb(0, 100, 0), null));
96        mySimpleXYPlot.addSeries(series[0],
97                new LineAndPointFormatter(Color.rgb(0, 0, 0), null,
98                        Color.rgb(0, 0, 150), null));
99        mySimpleXYPlot.redraw();
100        mySimpleXYPlot.calculateMinMaxVals();
101        minXY = new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),
102                mySimpleXYPlot.getCalculatedMinY().floatValue());
103        maxXY = new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),
104                mySimpleXYPlot.getCalculatedMaxY().floatValue());
105    }
106
107    private void populateSeries(SimpleXYSeries series, int max) {
108        Random r = new Random();
109        for(int i = 0; i < SERIES_SIZE; i++) {
110            series.addLast(i, r.nextInt(max));
111        }
112    }
113
114    // Definition of the touch states
115    static final int NONE = 0;
116    static final int ONE_FINGER_DRAG = 1;
117    static final int TWO_FINGERS_DRAG = 2;
118    int mode = NONE;
119
120    PointF firstFinger;
121    float distBetweenFingers;
122    boolean stopThread = false;
123
124    @Override
125    public boolean onTouch(View arg0, MotionEvent event) {
126        switch (event.getAction() & MotionEvent.ACTION_MASK) {
127            case MotionEvent.ACTION_DOWN: // Start gesture
128                firstFinger = new PointF(event.getX(), event.getY());
129                mode = ONE_FINGER_DRAG;
130                stopThread = true;
131                break;
132            case MotionEvent.ACTION_UP:
133            case MotionEvent.ACTION_POINTER_UP:
134                mode = NONE;
135                break;
136            case MotionEvent.ACTION_POINTER_DOWN: // second finger
137                distBetweenFingers = spacing(event);
138                // the distance check is done to avoid false alarms
139                if (distBetweenFingers > 5f) {
140                    mode = TWO_FINGERS_DRAG;
141                }
142                break;
143            case MotionEvent.ACTION_MOVE:
144                if (mode == ONE_FINGER_DRAG) {
145                    PointF oldFirstFinger = firstFinger;
146                    firstFinger = new PointF(event.getX(), event.getY());
147                    scroll(oldFirstFinger.x - firstFinger.x);
148                    mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,
149                            BoundaryMode.FIXED);
150                    mySimpleXYPlot.redraw();
151
152                } else if (mode == TWO_FINGERS_DRAG) {
153                    float oldDist = distBetweenFingers;
154                    distBetweenFingers = spacing(event);
155                    zoom(oldDist / distBetweenFingers);
156                    mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,
157                            BoundaryMode.FIXED);
158                    mySimpleXYPlot.redraw();
159                }
160                break;
161        }
162        return true;
163    }
164
165    private void zoom(float scale) {
166        float domainSpan = maxXY.x - minXY.x;
167        float domainMidPoint = maxXY.x - domainSpan / 2.0f;
168        float offset = domainSpan * scale / 2.0f;
169
170        minXY.x = domainMidPoint - offset;
171        maxXY.x = domainMidPoint + offset;
172
173        minXY.x = Math.min(minXY.x, series[3].getX(series[3].size() - 3)
174                .floatValue());
175        maxXY.x = Math.max(maxXY.x, series[0].getX(1).floatValue());
176        clampToDomainBounds(domainSpan);
177    }
178
179    private void scroll(float pan) {
180        float domainSpan = maxXY.x - minXY.x;
181        float step = domainSpan / mySimpleXYPlot.getWidth();
182        float offset = pan * step;
183        minXY.x = minXY.x + offset;
184        maxXY.x = maxXY.x + offset;
185        clampToDomainBounds(domainSpan);
186    }
187
188    private void clampToDomainBounds(float domainSpan) {
189        float leftBoundary = series[0].getX(0).floatValue();
190        float rightBoundary = series[3].getX(series[3].size() - 1).floatValue();
191        // enforce left scroll boundary:
192        if (minXY.x < leftBoundary) {
193            minXY.x = leftBoundary;
194            maxXY.x = leftBoundary + domainSpan;
195        } else if (maxXY.x > series[3].getX(series[3].size() - 1).floatValue()) {
196            maxXY.x = rightBoundary;
197            minXY.x = rightBoundary - domainSpan;
198        }
199    }
200
201    private float spacing(MotionEvent event) {
202        float x = event.getX(0) - event.getX(1);
203        float y = event.getY(0) - event.getY(1);
204        return (float) Math.hypot(x, y);
205    }
206}
207
208