/**
* Copyright (C) 2009, 2010 SC 4ViewSoft SRL
*
* 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.androsz.achartengine;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import com.androsz.achartengine.chart.AbstractChart;
import com.androsz.achartengine.chart.XYChart;
import com.androsz.achartengine.renderer.DefaultRenderer;
import com.androsz.achartengine.renderer.XYMultipleSeriesRenderer;
import com.androsz.achartengine.tools.FitZoom;
import com.androsz.achartengine.tools.Pan;
import com.androsz.achartengine.tools.Zoom;
/**
* The view that encapsulates the graphical chart.
*/
public class GraphicalView extends View {
/** The chart to be drawn. */
private final AbstractChart mChart;
/** The chart renderer. */
private XYMultipleSeriesRenderer mRenderer;
/** The view bounds. */
private final Rect mRect = new Rect();
/** The user interface thread handler. */
private final Handler mHandler;
/** The old x coordinate. */
private float oldX;
/** The old y coordinate. */
private float oldY;
/** The old x2 coordinate. */
private float oldX2;
/** The old y2 coordinate. */
private float oldY2;
/** The zoom buttons rectangle. */
private final RectF zoomR = new RectF();
/** The zoom in icon. */
private Bitmap zoomInImage;
/** The zoom out icon. */
private Bitmap zoomOutImage;
/** The fit zoom icon. */
private Bitmap fitZoomImage;
/** The zoom area size. */
private static final int ZOOM_SIZE = 45;
/** The zoom buttons background color. */
private static final int ZOOM_BUTTONS_COLOR = Color
.argb(175, 150, 150, 150);
/** The pan tool. */
private Pan pan;
/** The zoom in tool. */
private Zoom zoomIn;
/** The zoom out tool. */
private Zoom zoomOut;
/** The zoom for the pinch gesture. */
private Zoom pinchZoom;
/** The fit zoom tool. */
private FitZoom fitZoom;
/** The paint to be used when drawing the chart. */
private final Paint mPaint = new Paint();
/**
* Creates a new graphical view.
*
* @param context
* the context
* @param chart
* the chart to be drawn
*/
public GraphicalView(Context context, AbstractChart chart) {
super(context);
mChart = chart;
mHandler = new Handler();
if (mChart instanceof XYChart) {
zoomInImage = BitmapFactory.decodeStream(getClass()
.getResourceAsStream("image/zoom_in.png"));
zoomOutImage = BitmapFactory.decodeStream(getClass()
.getResourceAsStream("image/zoom_out.png"));
fitZoomImage = BitmapFactory.decodeStream(getClass()
.getResourceAsStream("image/zoom-1.png"));
mRenderer = ((XYChart) mChart).getRenderer();
if (mRenderer.getMarginsColor() == DefaultRenderer.NO_COLOR) {
mRenderer.setMarginsColor(mPaint.getColor());
}
if (mRenderer.isPanXEnabled() || mRenderer.isPanYEnabled()) {
pan = new Pan((XYChart) mChart);
}
if (mRenderer.isZoomXEnabled() || mRenderer.isZoomYEnabled()) {
zoomIn = new Zoom((XYChart) mChart, true,
mRenderer.getZoomRate());
zoomOut = new Zoom((XYChart) mChart, false,
mRenderer.getZoomRate());
fitZoom = new FitZoom((XYChart) mChart);
pinchZoom = new Zoom((XYChart) mChart, true, 1);
}
}
}
public void handleTouch(MotionEvent event) {
final int action = event.getAction();
if (mRenderer != null && action == MotionEvent.ACTION_MOVE) {
if (oldX >= 0 || oldY >= 0) {
final float newX = event.getX(0);
final float newY = event.getY(0);
if (event.getPointerCount() > 1
&& (oldX2 >= 0 || oldY2 >= 0)
&& (mRenderer.isZoomXEnabled() || mRenderer
.isZoomYEnabled())) {
final float newX2 = event.getX(1);
final float newY2 = event.getY(1);
final float newDeltaX = Math.abs(newX - newX2);
final float newDeltaY = Math.abs(newY - newY2);
final float oldDeltaX = Math.abs(oldX - oldX2);
final float oldDeltaY = Math.abs(oldY - oldY2);
float zoomRate = 1;
if (Math.abs(newX - oldX) >= Math.abs(newY - oldY)) {
zoomRate = newDeltaX / oldDeltaX;
} else {
zoomRate = newDeltaY / oldDeltaY;
}
if (zoomRate > 0.909 && zoomRate < 1.1) {
pinchZoom.setZoomRate(zoomRate);
pinchZoom.apply();
}
oldX2 = newX2;
oldY2 = newY2;
} else if (mRenderer.isPanXEnabled()
|| mRenderer.isPanYEnabled()) {
pan.apply(oldX, oldY, newX, newY);
oldX2 = 0;
oldY2 = 0;
}
oldX = newX;
oldY = newY;
repaint();
}
} else if (action == MotionEvent.ACTION_DOWN) {
oldX = event.getX(0);
oldY = event.getY(0);
if (mRenderer != null
&& (mRenderer.isZoomXEnabled() || mRenderer
.isZoomYEnabled()) && zoomR.contains(oldX, oldY)) {
if (oldX < zoomR.left + zoomR.width() / 3) {
zoomIn.apply();
} else if (oldX < zoomR.left + zoomR.width() * 2 / 3) {
zoomOut.apply();
} else {
fitZoom.apply();
}
}
} else if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_POINTER_UP) {
oldX = 0;
oldY = 0;
oldX2 = 0;
oldY2 = 0;
if (action == MotionEvent.ACTION_POINTER_UP) {
oldX = -1;
oldY = -1;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.getClipBounds(mRect);
final int top = mRect.top;
final int left = mRect.left;
final int width = mRect.width();
final int height = mRect.height();
mChart.draw(canvas, left, top, width, height, mPaint);
if (mRenderer != null
&& (mRenderer.isZoomXEnabled() || mRenderer.isZoomYEnabled())) {
mPaint.setColor(ZOOM_BUTTONS_COLOR);
zoomR.set(left + width - ZOOM_SIZE * 3, top + height - ZOOM_SIZE
* 0.775f, left + width, top + height);
canvas.drawRoundRect(zoomR, ZOOM_SIZE / 3, ZOOM_SIZE / 3, mPaint);
final float buttonY = top + height - ZOOM_SIZE * 0.625f;
canvas.drawBitmap(zoomInImage, left + width - ZOOM_SIZE * 2.75f,
buttonY, null);
canvas.drawBitmap(zoomOutImage, left + width - ZOOM_SIZE * 1.75f,
buttonY, null);
canvas.drawBitmap(fitZoomImage, left + width - ZOOM_SIZE * 0.75f,
buttonY, null);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mRenderer != null
&& (mRenderer.isPanXEnabled() || mRenderer.isPanYEnabled()
|| mRenderer.isZoomXEnabled() || mRenderer
.isZoomYEnabled())) {
handleTouch(event);
return true;
}
return super.onTouchEvent(event);
}
/**
* Schedule a view content repaint.
*/
public void repaint() {
mHandler.post(new Runnable() {
@Override
public void run() {
invalidate();
}
});
}
/**
* Schedule a view content repaint, in the specified rectangle area.
*
* @param left
* the left position of the area to be repainted
* @param top
* the top position of the area to be repainted
* @param right
* the right position of the area to be repainted
* @param bottom
* the bottom position of the area to be repainted
*/
public void repaint(final int left, final int top, final int right,
final int bottom) {
mHandler.post(new Runnable() {
@Override
public void run() {
invalidate(left, top, right, bottom);
}
});
}
/**
* Sets the zoom rate.
*
* @param rate
* the zoom rate
*/
public void setZoomRate(float rate) {
if (zoomIn != null && zoomOut != null) {
zoomIn.setZoomRate(rate);
zoomOut.setZoomRate(rate);
}
}
/**
* Do a chart zoom in.
*/
public void zoomIn() {
if (zoomIn != null) {
zoomIn.apply();
repaint();
}
}
/**
* Do a chart zoom out.
*/
public void zoomOut() {
if (zoomOut != null) {
zoomOut.apply();
repaint();
}
}
/**
* Do a chart zoom reset / fit zoom.
*/
public void zoomReset() {
if (fitZoom != null) {
fitZoom.apply();
repaint();
}
}
}