/* * 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.ui.widget; import android.graphics.*; import com.androidplot.exception.PlotRenderException; import com.androidplot.ui.*; import com.androidplot.util.DisplayDimensions; import com.androidplot.ui.XLayoutStyle; import com.androidplot.ui.YLayoutStyle; import com.androidplot.util.PixelUtils; /** * A Widget is a graphical sub-element of a Plot that can be positioned relative * to the bounds of the Plot. */ public abstract class Widget implements BoxModelable, Resizable { private Paint borderPaint; private Paint backgroundPaint; private boolean clippingEnabled = true; private BoxModel boxModel = new BoxModel(); private SizeMetrics sizeMetrics; private DisplayDimensions plotDimensions = new DisplayDimensions(); private DisplayDimensions widgetDimensions = new DisplayDimensions(); private boolean isVisible = true; private PositionMetrics positionMetrics; private LayoutManager layoutManager; public Widget(LayoutManager layoutManager, SizeMetric heightMetric, SizeMetric widthMetric) { this(layoutManager, new SizeMetrics(heightMetric, widthMetric)); } public Widget(LayoutManager layoutManager, SizeMetrics sizeMetrics) { this.layoutManager = layoutManager; SizeMetrics oldSize = this.sizeMetrics; setSize(sizeMetrics); onMetricsChanged(oldSize, sizeMetrics); } public DisplayDimensions getWidgetDimensions() { return widgetDimensions; } public AnchorPosition getAnchor() { return getPositionMetrics().getAnchor(); } public void setAnchor(AnchorPosition anchor) { getPositionMetrics().setAnchor(anchor); } /** * Same as {@link #position(float, com.androidplot.ui.XLayoutStyle, float, com.androidplot.ui.YLayoutStyle, com.androidplot.ui.AnchorPosition)} * but with the anchor parameter defaulted to the upper left corner. * @param x * @param xLayoutStyle * @param y * @param yLayoutStyle */ public void position(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle) { position(x, xLayoutStyle, y, yLayoutStyle, AnchorPosition.LEFT_TOP); } /** * @param x X-Coordinate of the top left corner of element. When using RELATIVE, must be a value between 0 and 1. * @param xLayoutStyle LayoutType to use when orienting this element's X-Coordinate. * @param y Y_VALS_ONLY-Coordinate of the top-left corner of element. When using RELATIVE, must be a value between 0 and 1. * @param yLayoutStyle LayoutType to use when orienting this element's Y_VALS_ONLY-Coordinate. * @param anchor The point of reference used by this positioning call. */ public void position(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle, AnchorPosition anchor) { setPositionMetrics(new PositionMetrics(x, xLayoutStyle, y, yLayoutStyle, anchor)); layoutManager.addToTop(this); } /** * Can be overridden by subclasses to respond to resizing events. * * @param oldSize * @param newSize */ protected void onMetricsChanged(SizeMetrics oldSize, SizeMetrics newSize) { } /** * Can be overridden by subclasses to handle any final resizing etc. that * can only be done after XML configuration etc. has completed. */ public void onPostInit() { } /** * Determines whether or not point lies within this Widget. * * @param point * @return */ public boolean containsPoint(PointF point) { //return outlineRect != null && outlineRect.contains(point.x, point.y); return widgetDimensions.canvasRect.contains(point.x, point.y); } public void setSize(SizeMetrics sizeMetrics) { this.sizeMetrics = sizeMetrics; } public void setWidth(float width) { sizeMetrics.getWidthMetric().setValue(width); } public void setWidth(float width, SizeLayoutType layoutType) { sizeMetrics.getWidthMetric().set(width, layoutType); } public void setHeight(float height) { sizeMetrics.getHeightMetric().setValue(height); } public void setHeight(float height, SizeLayoutType layoutType) { sizeMetrics.getHeightMetric().set(height, layoutType); } public SizeMetric getWidthMetric() { return sizeMetrics.getWidthMetric(); } public SizeMetric getHeightMetric() { return sizeMetrics.getHeightMetric(); } public float getWidthPix(float size) { return sizeMetrics.getWidthMetric().getPixelValue(size); } public float getHeightPix(float size) { return sizeMetrics.getHeightMetric().getPixelValue(size); } public RectF getMarginatedRect(RectF widgetRect) { return boxModel.getMarginatedRect(widgetRect); } public RectF getPaddedRect(RectF widgetMarginRect) { return boxModel.getPaddedRect(widgetMarginRect); } public void setMarginRight(float marginRight) { boxModel.setMarginRight(marginRight); } public void setMargins(float left, float top, float right, float bottom) { boxModel.setMargins(left, top, right, bottom); } public void setPadding(float left, float top, float right, float bottom) { boxModel.setPadding(left, top, right, bottom); } public float getMarginTop() { return boxModel.getMarginTop(); } public void setMarginTop(float marginTop) { boxModel.setMarginTop(marginTop); } public float getMarginBottom() { return boxModel.getMarginBottom(); } @Override public float getPaddingLeft() { return boxModel.getPaddingLeft(); } @Override public void setPaddingLeft(float paddingLeft) { boxModel.setPaddingLeft(paddingLeft); } @Override public float getPaddingTop() { return boxModel.getPaddingTop(); } @Override public void setPaddingTop(float paddingTop) { boxModel.setPaddingTop(paddingTop); } @Override public float getPaddingRight() { return boxModel.getPaddingRight(); } @Override public void setPaddingRight(float paddingRight) { boxModel.setPaddingRight(paddingRight); } @Override public float getPaddingBottom() { return boxModel.getPaddingBottom(); } @Override public void setPaddingBottom(float paddingBottom) { boxModel.setPaddingBottom(paddingBottom); } @SuppressWarnings("SameParameterValue") public void setMarginBottom(float marginBottom) { boxModel.setMarginBottom(marginBottom); } public float getMarginLeft() { return boxModel.getMarginLeft(); } public void setMarginLeft(float marginLeft) { boxModel.setMarginLeft(marginLeft); } public float getMarginRight() { return boxModel.getMarginRight(); } /** * Causes the pixel dimensions used for rendering this Widget * to be recalculated. Should be called any time a parameter that factors * into this Widget's size or position is altered. */ public synchronized void refreshLayout() { if(positionMetrics == null) { // make sure positionMetrics have been set. this method can be // automatically called during xml configuration of certain params // before the widget is fully configured. return; } float elementWidth = getWidthPix(plotDimensions.paddedRect.width()); float elementHeight = getHeightPix(plotDimensions.paddedRect.height()); PointF coords = getElementCoordinates(elementHeight, elementWidth, plotDimensions.paddedRect, positionMetrics); RectF widgetRect = new RectF(coords.x, coords.y, coords.x + elementWidth, coords.y + elementHeight); RectF marginatedWidgetRect = getMarginatedRect(widgetRect); RectF paddedWidgetRect = getPaddedRect(marginatedWidgetRect); widgetDimensions = new DisplayDimensions(widgetRect, marginatedWidgetRect, paddedWidgetRect); } @Override public synchronized void layout(final DisplayDimensions plotDimensions) { this.plotDimensions = plotDimensions; refreshLayout(); } public PointF getElementCoordinates(float height, float width, RectF viewRect, PositionMetrics metrics) { float x = metrics.getXPositionMetric().getPixelValue(viewRect.width()) + viewRect.left; float y = metrics.getYPositionMetric().getPixelValue(viewRect.height()) + viewRect.top; PointF point = new PointF(x, y); return PixelUtils.sub(point, getAnchorOffset(width, height, metrics.getAnchor())); } public static PointF getAnchorOffset(float width, float height, AnchorPosition anchorPosition) { PointF point = new PointF(); switch (anchorPosition) { case LEFT_TOP: break; case LEFT_MIDDLE: point.set(0, height / 2); break; case LEFT_BOTTOM: point.set(0, height); break; case RIGHT_TOP: point.set(width, 0); break; case RIGHT_BOTTOM: point.set(width, height); break; case RIGHT_MIDDLE: point.set(width, height / 2); break; case TOP_MIDDLE: point.set(width / 2, 0); break; case BOTTOM_MIDDLE: point.set(width / 2, height); break; case CENTER: point.set(width / 2, height / 2); break; default: throw new IllegalArgumentException("Unsupported anchor location: " + anchorPosition); } return point; } public static PointF getAnchorCoordinates(RectF widgetRect, AnchorPosition anchorPosition) { return PixelUtils.add(new PointF(widgetRect.left, widgetRect.top), getAnchorOffset(widgetRect.width(), widgetRect.height(), anchorPosition)); } public static PointF getAnchorCoordinates(float x, float y, float width, float height, AnchorPosition anchorPosition) { return getAnchorCoordinates(new RectF(x, y, x+width, y+height), anchorPosition); } public void draw(Canvas canvas, RectF widgetRect) throws PlotRenderException { //outlineRect = widgetRect; if (isVisible()) { if (backgroundPaint != null) { drawBackground(canvas, widgetDimensions.canvasRect); } /* RectF marginatedRect = new RectF(outlineRect.left + marginLeft, outlineRect.top + marginTop, outlineRect.right - marginRight, outlineRect.bottom - marginBottom);*/ /*RectF marginatedRect = boxModel.getMarginatedRect(widgetRect); RectF paddedRect = boxModel.getPaddedRect(marginatedRect);*/ doOnDraw(canvas, widgetDimensions.paddedRect); if (borderPaint != null) { drawBorder(canvas, widgetDimensions.paddedRect); } } } protected void drawBorder(Canvas canvas, RectF paddedRect) { canvas.drawRect(paddedRect, borderPaint); } protected void drawBackground(Canvas canvas, RectF widgetRect) { canvas.drawRect(widgetRect, backgroundPaint); } /** * @param canvas The Canvas to draw onto * @param widgetRect the size and coordinates of this widget */ protected abstract void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException; public Paint getBorderPaint() { return borderPaint; } public void setBorderPaint(Paint borderPaint) { this.borderPaint = borderPaint; } public Paint getBackgroundPaint() { return backgroundPaint; } public void setBackgroundPaint(Paint backgroundPaint) { this.backgroundPaint = backgroundPaint; } public boolean isClippingEnabled() { return clippingEnabled; } public void setClippingEnabled(boolean clippingEnabled) { this.clippingEnabled = clippingEnabled; } public boolean isVisible() { return isVisible; } public void setVisible(boolean visible) { isVisible = visible; } public PositionMetrics getPositionMetrics() { return positionMetrics; } public void setPositionMetrics(PositionMetrics positionMetrics) { this.positionMetrics = positionMetrics; } }