Home | History | Annotate | Download | only in widget
      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 
     17 package com.androidplot.ui.widget;
     18 
     19 import android.graphics.*;
     20 import com.androidplot.exception.PlotRenderException;
     21 import com.androidplot.ui.*;
     22 import com.androidplot.util.DisplayDimensions;
     23 import com.androidplot.ui.XLayoutStyle;
     24 import com.androidplot.ui.YLayoutStyle;
     25 import com.androidplot.util.PixelUtils;
     26 
     27 /**
     28  * A Widget is a graphical sub-element of a Plot that can be positioned relative
     29  * to the bounds of the Plot.
     30  */
     31 public abstract class Widget implements BoxModelable, Resizable {
     32 
     33     private Paint borderPaint;
     34     private Paint backgroundPaint;
     35     private boolean clippingEnabled = true;
     36     private BoxModel boxModel = new BoxModel();
     37     private SizeMetrics sizeMetrics;
     38     private DisplayDimensions plotDimensions = new DisplayDimensions();
     39     private DisplayDimensions widgetDimensions = new DisplayDimensions();
     40     private boolean isVisible = true;
     41     private PositionMetrics positionMetrics;
     42     private LayoutManager layoutManager;
     43 
     44     public Widget(LayoutManager layoutManager, SizeMetric heightMetric, SizeMetric widthMetric) {
     45         this(layoutManager, new SizeMetrics(heightMetric, widthMetric));
     46     }
     47 
     48     public Widget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {
     49         this.layoutManager = layoutManager;
     50         SizeMetrics oldSize = this.sizeMetrics;
     51         setSize(sizeMetrics);
     52         onMetricsChanged(oldSize, sizeMetrics);
     53     }
     54 
     55     public DisplayDimensions getWidgetDimensions() {
     56         return widgetDimensions;
     57     }
     58 
     59     public AnchorPosition getAnchor() {
     60         return getPositionMetrics().getAnchor();
     61     }
     62 
     63     public void setAnchor(AnchorPosition anchor) {
     64         getPositionMetrics().setAnchor(anchor);
     65     }
     66 
     67 
     68     /**
     69      * Same as {@link #position(float, com.androidplot.ui.XLayoutStyle, float, com.androidplot.ui.YLayoutStyle, com.androidplot.ui.AnchorPosition)}
     70      * but with the anchor parameter defaulted to the upper left corner.
     71      * @param x
     72      * @param xLayoutStyle
     73      * @param y
     74      * @param yLayoutStyle
     75      */
     76     public void position(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle) {
     77         position(x, xLayoutStyle, y, yLayoutStyle, AnchorPosition.LEFT_TOP);
     78     }
     79 
     80     /**
     81      * @param x            X-Coordinate of the top left corner of element.  When using RELATIVE, must be a value between 0 and 1.
     82      * @param xLayoutStyle LayoutType to use when orienting this element's X-Coordinate.
     83      * @param y            Y_VALS_ONLY-Coordinate of the top-left corner of element.  When using RELATIVE, must be a value between 0 and 1.
     84      * @param yLayoutStyle LayoutType to use when orienting this element's Y_VALS_ONLY-Coordinate.
     85      * @param anchor       The point of reference used by this positioning call.
     86      */
     87     public void position(float x, XLayoutStyle xLayoutStyle, float y,
     88                          YLayoutStyle yLayoutStyle, AnchorPosition anchor) {
     89         setPositionMetrics(new PositionMetrics(x, xLayoutStyle, y, yLayoutStyle, anchor));
     90         layoutManager.addToTop(this);
     91     }
     92 
     93     /**
     94      * Can be overridden by subclasses to respond to resizing events.
     95      *
     96      * @param oldSize
     97      * @param newSize
     98      */
     99     protected void onMetricsChanged(SizeMetrics oldSize, SizeMetrics newSize) {
    100     }
    101 
    102     /**
    103      * Can be overridden by subclasses to handle any final resizing etc. that
    104      * can only be done after XML configuration etc. has completed.
    105      */
    106     public void onPostInit() {
    107     }
    108 
    109     /**
    110      * Determines whether or not point lies within this Widget.
    111      *
    112      * @param point
    113      * @return
    114      */
    115     public boolean containsPoint(PointF point) {
    116         //return outlineRect != null && outlineRect.contains(point.x, point.y);
    117         return widgetDimensions.canvasRect.contains(point.x, point.y);
    118     }
    119 
    120     public void setSize(SizeMetrics sizeMetrics) {
    121         this.sizeMetrics = sizeMetrics;
    122     }
    123 
    124 
    125     public void setWidth(float width) {
    126         sizeMetrics.getWidthMetric().setValue(width);
    127     }
    128 
    129     public void setWidth(float width, SizeLayoutType layoutType) {
    130         sizeMetrics.getWidthMetric().set(width, layoutType);
    131     }
    132 
    133     public void setHeight(float height) {
    134         sizeMetrics.getHeightMetric().setValue(height);
    135     }
    136 
    137     public void setHeight(float height, SizeLayoutType layoutType) {
    138         sizeMetrics.getHeightMetric().set(height, layoutType);
    139     }
    140 
    141     public SizeMetric getWidthMetric() {
    142         return sizeMetrics.getWidthMetric();
    143     }
    144 
    145     public SizeMetric getHeightMetric() {
    146         return sizeMetrics.getHeightMetric();
    147     }
    148 
    149     public float getWidthPix(float size) {
    150         return sizeMetrics.getWidthMetric().getPixelValue(size);
    151     }
    152 
    153     public float getHeightPix(float size) {
    154         return sizeMetrics.getHeightMetric().getPixelValue(size);
    155     }
    156 
    157     public RectF getMarginatedRect(RectF widgetRect) {
    158         return boxModel.getMarginatedRect(widgetRect);
    159     }
    160 
    161     public RectF getPaddedRect(RectF widgetMarginRect) {
    162         return boxModel.getPaddedRect(widgetMarginRect);
    163     }
    164 
    165     public void setMarginRight(float marginRight) {
    166         boxModel.setMarginRight(marginRight);
    167     }
    168 
    169     public void setMargins(float left, float top, float right, float bottom) {
    170         boxModel.setMargins(left, top, right, bottom);
    171     }
    172 
    173     public void setPadding(float left, float top, float right, float bottom) {
    174         boxModel.setPadding(left, top, right, bottom);
    175     }
    176 
    177     public float getMarginTop() {
    178         return boxModel.getMarginTop();
    179     }
    180 
    181     public void setMarginTop(float marginTop) {
    182         boxModel.setMarginTop(marginTop);
    183     }
    184 
    185     public float getMarginBottom() {
    186         return boxModel.getMarginBottom();
    187     }
    188 
    189     @Override
    190     public float getPaddingLeft() {
    191         return boxModel.getPaddingLeft();
    192     }
    193 
    194     @Override
    195     public void setPaddingLeft(float paddingLeft) {
    196         boxModel.setPaddingLeft(paddingLeft);
    197     }
    198 
    199     @Override
    200     public float getPaddingTop() {
    201         return boxModel.getPaddingTop();
    202     }
    203 
    204     @Override
    205     public void setPaddingTop(float paddingTop) {
    206         boxModel.setPaddingTop(paddingTop);
    207     }
    208 
    209     @Override
    210     public float getPaddingRight() {
    211         return boxModel.getPaddingRight();
    212     }
    213 
    214     @Override
    215     public void setPaddingRight(float paddingRight) {
    216         boxModel.setPaddingRight(paddingRight);
    217     }
    218 
    219     @Override
    220     public float getPaddingBottom() {
    221         return boxModel.getPaddingBottom();
    222     }
    223 
    224     @Override
    225     public void setPaddingBottom(float paddingBottom) {
    226         boxModel.setPaddingBottom(paddingBottom);
    227     }
    228 
    229     @SuppressWarnings("SameParameterValue")
    230     public void setMarginBottom(float marginBottom) {
    231         boxModel.setMarginBottom(marginBottom);
    232     }
    233 
    234     public float getMarginLeft() {
    235         return boxModel.getMarginLeft();
    236     }
    237 
    238     public void setMarginLeft(float marginLeft) {
    239         boxModel.setMarginLeft(marginLeft);
    240     }
    241 
    242     public float getMarginRight() {
    243         return boxModel.getMarginRight();
    244     }
    245 
    246     /**
    247      * Causes the pixel dimensions used for rendering this Widget
    248      * to be recalculated.  Should be called any time a parameter that factors
    249      * into this Widget's size or position is altered.
    250      */
    251     public synchronized void refreshLayout() {
    252         if(positionMetrics == null) {
    253             // make sure positionMetrics have been set.  this method can be
    254             // automatically called during xml configuration of certain params
    255             // before the widget is fully configured.
    256             return;
    257         }
    258         float elementWidth = getWidthPix(plotDimensions.paddedRect.width());
    259         float elementHeight = getHeightPix(plotDimensions.paddedRect.height());
    260         PointF coords = getElementCoordinates(elementHeight,
    261                 elementWidth, plotDimensions.paddedRect, positionMetrics);
    262 
    263         RectF widgetRect = new RectF(coords.x, coords.y,
    264                 coords.x + elementWidth, coords.y + elementHeight);
    265         RectF marginatedWidgetRect = getMarginatedRect(widgetRect);
    266         RectF paddedWidgetRect = getPaddedRect(marginatedWidgetRect);
    267         widgetDimensions = new DisplayDimensions(widgetRect,
    268                 marginatedWidgetRect, paddedWidgetRect);
    269     }
    270 
    271     @Override
    272     public synchronized void layout(final DisplayDimensions plotDimensions) {
    273         this.plotDimensions = plotDimensions;
    274         refreshLayout();
    275     }
    276 
    277     public PointF getElementCoordinates(float height, float width, RectF viewRect, PositionMetrics metrics) {
    278             float x = metrics.getXPositionMetric().getPixelValue(viewRect.width()) + viewRect.left;
    279             float y = metrics.getYPositionMetric().getPixelValue(viewRect.height()) + viewRect.top;
    280             PointF point = new PointF(x, y);
    281             return PixelUtils.sub(point, getAnchorOffset(width, height, metrics.getAnchor()));
    282         }
    283 
    284     public static PointF getAnchorOffset(float width, float height, AnchorPosition anchorPosition) {
    285             PointF point = new PointF();
    286             switch (anchorPosition) {
    287                 case LEFT_TOP:
    288                     break;
    289                 case LEFT_MIDDLE:
    290                     point.set(0, height / 2);
    291                     break;
    292                 case LEFT_BOTTOM:
    293                     point.set(0, height);
    294                     break;
    295                 case RIGHT_TOP:
    296                     point.set(width, 0);
    297                     break;
    298                 case RIGHT_BOTTOM:
    299                     point.set(width, height);
    300                     break;
    301                 case RIGHT_MIDDLE:
    302                     point.set(width, height / 2);
    303                     break;
    304                 case TOP_MIDDLE:
    305                     point.set(width / 2, 0);
    306                     break;
    307                 case BOTTOM_MIDDLE:
    308                     point.set(width / 2, height);
    309                     break;
    310                 case CENTER:
    311                     point.set(width / 2, height / 2);
    312                     break;
    313                 default:
    314                     throw new IllegalArgumentException("Unsupported anchor location: " + anchorPosition);
    315             }
    316             return point;
    317         }
    318 
    319     public static PointF getAnchorCoordinates(RectF widgetRect, AnchorPosition anchorPosition) {
    320             return PixelUtils.add(new PointF(widgetRect.left, widgetRect.top),
    321                     getAnchorOffset(widgetRect.width(), widgetRect.height(), anchorPosition));
    322         }
    323 
    324         public static PointF getAnchorCoordinates(float x, float y, float width, float height, AnchorPosition anchorPosition) {
    325             return getAnchorCoordinates(new RectF(x, y, x+width, y+height), anchorPosition);
    326         }
    327 
    328     public void draw(Canvas canvas, RectF widgetRect) throws PlotRenderException {
    329         //outlineRect = widgetRect;
    330         if (isVisible()) {
    331             if (backgroundPaint != null) {
    332                 drawBackground(canvas, widgetDimensions.canvasRect);
    333             }
    334 
    335             /* RectF marginatedRect = new RectF(outlineRect.left + marginLeft,
    336           outlineRect.top + marginTop,
    337           outlineRect.right - marginRight,
    338           outlineRect.bottom - marginBottom);*/
    339 
    340             /*RectF marginatedRect = boxModel.getMarginatedRect(widgetRect);
    341             RectF paddedRect = boxModel.getPaddedRect(marginatedRect);*/
    342             doOnDraw(canvas, widgetDimensions.paddedRect);
    343 
    344             if (borderPaint != null) {
    345                 drawBorder(canvas, widgetDimensions.paddedRect);
    346             }
    347         }
    348     }
    349 
    350     protected void drawBorder(Canvas canvas, RectF paddedRect) {
    351         canvas.drawRect(paddedRect, borderPaint);
    352     }
    353 
    354     protected void drawBackground(Canvas canvas, RectF widgetRect) {
    355         canvas.drawRect(widgetRect, backgroundPaint);
    356     }
    357 
    358     /**
    359      * @param canvas     The Canvas to draw onto
    360      * @param widgetRect the size and coordinates of this widget
    361      */
    362     protected abstract void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException;
    363 
    364     public Paint getBorderPaint() {
    365         return borderPaint;
    366     }
    367 
    368     public void setBorderPaint(Paint borderPaint) {
    369         this.borderPaint = borderPaint;
    370     }
    371 
    372     public Paint getBackgroundPaint() {
    373         return backgroundPaint;
    374     }
    375 
    376     public void setBackgroundPaint(Paint backgroundPaint) {
    377         this.backgroundPaint = backgroundPaint;
    378     }
    379 
    380     public boolean isClippingEnabled() {
    381         return clippingEnabled;
    382     }
    383 
    384     public void setClippingEnabled(boolean clippingEnabled) {
    385         this.clippingEnabled = clippingEnabled;
    386     }
    387 
    388     public boolean isVisible() {
    389         return isVisible;
    390     }
    391 
    392     public void setVisible(boolean visible) {
    393         isVisible = visible;
    394     }
    395 
    396     public PositionMetrics getPositionMetrics() {
    397         return positionMetrics;
    398     }
    399 
    400     public void setPositionMetrics(PositionMetrics positionMetrics) {
    401         this.positionMetrics = positionMetrics;
    402     }
    403 }
    404