/* | |
* Copyright 2012 AndroidPlot.com | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.androidplot.demos; | |
import java.text.DecimalFormat; | |
import java.util.Random; | |
import android.app.Activity; | |
import android.graphics.Color; | |
import android.graphics.Paint; | |
import android.graphics.PointF; | |
import android.os.Bundle; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.View.OnTouchListener; | |
import android.widget.Button; | |
import com.androidplot.Plot; | |
import com.androidplot.xy.BoundaryMode; | |
import com.androidplot.xy.LineAndPointFormatter; | |
import com.androidplot.xy.SimpleXYSeries; | |
import com.androidplot.xy.XYPlot; | |
/*********************************** | |
* @author David Buezas (david.buezas at gmail.com) | |
* Feel free to copy, modify and use the source as it fits you. | |
* 09/27/2012 nfellows - updated for 0.5.1 and made a few simplifications | |
*/ | |
public class TouchZoomExampleActivity extends Activity implements OnTouchListener { | |
private static final int SERIES_SIZE = 200; | |
private XYPlot mySimpleXYPlot; | |
private Button resetButton; | |
private SimpleXYSeries[] series = null; | |
private PointF minXY; | |
private PointF maxXY; | |
public void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.touch_zoom_example); | |
resetButton = (Button) findViewById(R.id.resetButton); | |
resetButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
minXY.x = series[0].getX(0).floatValue(); | |
maxXY.x = series[3].getX(series[3].size() - 1).floatValue(); | |
mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.FIXED); | |
// pre 0.5.1 users should use postRedraw() instead. | |
mySimpleXYPlot.redraw(); | |
} | |
}); | |
mySimpleXYPlot = (XYPlot) findViewById(R.id.mySimpleXYPlot); | |
mySimpleXYPlot.setOnTouchListener(this); | |
mySimpleXYPlot.getGraphWidget().setTicksPerRangeLabel(2); | |
mySimpleXYPlot.getGraphWidget().setTicksPerDomainLabel(2); | |
mySimpleXYPlot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT); | |
mySimpleXYPlot.getGraphWidget().setRangeValueFormat( | |
new DecimalFormat("#####")); | |
mySimpleXYPlot.getGraphWidget().setDomainValueFormat( | |
new DecimalFormat("#####.#")); | |
mySimpleXYPlot.getGraphWidget().setRangeLabelWidth(25); | |
mySimpleXYPlot.setRangeLabel(""); | |
mySimpleXYPlot.setDomainLabel(""); | |
mySimpleXYPlot.setBorderStyle(Plot.BorderStyle.NONE, null, null); | |
//mySimpleXYPlot.disableAllMarkup(); | |
series = new SimpleXYSeries[4]; | |
int scale = 1; | |
for (int i = 0; i < 4; i++, scale *= 5) { | |
series[i] = new SimpleXYSeries("S" + i); | |
populateSeries(series[i], scale); | |
} | |
mySimpleXYPlot.addSeries(series[3], | |
new LineAndPointFormatter(Color.rgb(50, 0, 0), null, | |
Color.rgb(100, 0, 0), null)); | |
mySimpleXYPlot.addSeries(series[2], | |
new LineAndPointFormatter(Color.rgb(50, 50, 0), null, | |
Color.rgb(100, 100, 0), null)); | |
mySimpleXYPlot.addSeries(series[1], | |
new LineAndPointFormatter(Color.rgb(0, 50, 0), null, | |
Color.rgb(0, 100, 0), null)); | |
mySimpleXYPlot.addSeries(series[0], | |
new LineAndPointFormatter(Color.rgb(0, 0, 0), null, | |
Color.rgb(0, 0, 150), null)); | |
mySimpleXYPlot.redraw(); | |
mySimpleXYPlot.calculateMinMaxVals(); | |
minXY = new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(), | |
mySimpleXYPlot.getCalculatedMinY().floatValue()); | |
maxXY = new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(), | |
mySimpleXYPlot.getCalculatedMaxY().floatValue()); | |
} | |
private void populateSeries(SimpleXYSeries series, int max) { | |
Random r = new Random(); | |
for(int i = 0; i < SERIES_SIZE; i++) { | |
series.addLast(i, r.nextInt(max)); | |
} | |
} | |
// 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 distBetweenFingers; | |
boolean stopThread = false; | |
@Override | |
public boolean onTouch(View arg0, 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; | |
stopThread = true; | |
break; | |
case MotionEvent.ACTION_UP: | |
case MotionEvent.ACTION_POINTER_UP: | |
mode = NONE; | |
break; | |
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()); | |
scroll(oldFirstFinger.x - firstFinger.x); | |
mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, | |
BoundaryMode.FIXED); | |
mySimpleXYPlot.redraw(); | |
} else if (mode == TWO_FINGERS_DRAG) { | |
float oldDist = distBetweenFingers; | |
distBetweenFingers = spacing(event); | |
zoom(oldDist / distBetweenFingers); | |
mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, | |
BoundaryMode.FIXED); | |
mySimpleXYPlot.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; | |
minXY.x = Math.min(minXY.x, series[3].getX(series[3].size() - 3) | |
.floatValue()); | |
maxXY.x = Math.max(maxXY.x, series[0].getX(1).floatValue()); | |
clampToDomainBounds(domainSpan); | |
} | |
private void scroll(float pan) { | |
float domainSpan = maxXY.x - minXY.x; | |
float step = domainSpan / mySimpleXYPlot.getWidth(); | |
float offset = pan * step; | |
minXY.x = minXY.x + offset; | |
maxXY.x = maxXY.x + offset; | |
clampToDomainBounds(domainSpan); | |
} | |
private void clampToDomainBounds(float domainSpan) { | |
float leftBoundary = series[0].getX(0).floatValue(); | |
float rightBoundary = series[3].getX(series[3].size() - 1).floatValue(); | |
// enforce left scroll boundary: | |
if (minXY.x < leftBoundary) { | |
minXY.x = leftBoundary; | |
maxXY.x = leftBoundary + domainSpan; | |
} else if (maxXY.x > series[3].getX(series[3].size() - 1).floatValue()) { | |
maxXY.x = rightBoundary; | |
minXY.x = rightBoundary - domainSpan; | |
} | |
} | |
private float spacing(MotionEvent event) { | |
float x = event.getX(0) - event.getX(1); | |
float y = event.getY(0) - event.getY(1); | |
return (float) Math.hypot(x, y); | |
} | |
} | |