Home | History | Annotate | Download | only in demos
      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.demos;
     18 
     19 import java.text.DateFormatSymbols;
     20 import java.text.FieldPosition;
     21 import java.text.NumberFormat;
     22 import java.text.ParsePosition;
     23 import java.util.Arrays;
     24 import java.util.Iterator;
     25 
     26 import android.app.Activity;
     27 import android.graphics.Color;
     28 import android.graphics.Paint;
     29 import android.graphics.PointF;
     30 import android.os.Bundle;
     31 import android.util.Pair;
     32 import android.view.MotionEvent;
     33 import android.view.View;
     34 import android.widget.AdapterView;
     35 import android.widget.AdapterView.OnItemSelectedListener;
     36 import android.widget.ArrayAdapter;
     37 import android.widget.CheckBox;
     38 import android.widget.CompoundButton;
     39 import android.widget.SeekBar;
     40 import android.widget.Spinner;
     41 
     42 import com.androidplot.LineRegion;
     43 import com.androidplot.ui.AnchorPosition;
     44 import com.androidplot.ui.SeriesRenderer;
     45 import com.androidplot.ui.SizeLayoutType;
     46 import com.androidplot.ui.SizeMetrics;
     47 import com.androidplot.ui.TextOrientationType;
     48 import com.androidplot.ui.widget.TextLabelWidget;
     49 import com.androidplot.util.PixelUtils;
     50 import com.androidplot.xy.*;
     51 import com.androidplot.ui.XLayoutStyle;
     52 import com.androidplot.ui.YLayoutStyle;
     53 
     54 /**
     55  * The simplest possible example of using AndroidPlot to plot some data.
     56  */
     57 public class BarPlotExampleActivity extends Activity
     58 {
     59 
     60     private static final String NO_SELECTION_TXT = "Touch bar to select.";
     61     private XYPlot plot;
     62 
     63     private CheckBox series1CheckBox;
     64     private CheckBox series2CheckBox;
     65     private Spinner spRenderStyle, spWidthStyle, spSeriesSize;
     66     private SeekBar sbFixedWidth, sbVariableWidth;
     67 
     68     private XYSeries series1;
     69     private XYSeries series2;
     70     private enum SeriesSize {
     71         TEN,
     72         TWENTY,
     73         SIXTY
     74     }
     75 
     76     // Create a couple arrays of y-values to plot:
     77     Number[] series1Numbers10 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5};
     78     Number[] series2Numbers10 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4};
     79     Number[] series1Numbers20 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
     80     Number[] series2Numbers20 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
     81     Number[] series1Numbers60 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
     82     Number[] series2Numbers60 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
     83     Number[] series1Numbers = series1Numbers10;
     84     Number[] series2Numbers = series2Numbers10;
     85 
     86     private MyBarFormatter formatter1 =
     87             new MyBarFormatter(Color.argb(200, 100, 150, 100), Color.LTGRAY);
     88 
     89     private MyBarFormatter formatter2 =
     90             new MyBarFormatter(Color.argb(200, 100, 100, 150), Color.LTGRAY);
     91 
     92     private MyBarFormatter selectionFormatter =
     93             new MyBarFormatter(Color.YELLOW, Color.WHITE);
     94 
     95     private TextLabelWidget selectionWidget;
     96 
     97     private Pair<Integer, XYSeries> selection;
     98 
     99     @Override
    100     public void onCreate(Bundle savedInstanceState)
    101     {
    102 
    103         super.onCreate(savedInstanceState);
    104         setContentView(R.layout.bar_plot_example);
    105 
    106         // initialize our XYPlot reference:
    107         plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
    108 
    109         selectionWidget = new TextLabelWidget(plot.getLayoutManager(), NO_SELECTION_TXT,
    110                 new SizeMetrics(
    111                         PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE,
    112                         PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE),
    113                 TextOrientationType.HORIZONTAL);
    114 
    115         selectionWidget.getLabelPaint().setTextSize(PixelUtils.dpToPix(16));
    116 
    117         // add a dark, semi-transparent background to the selection label widget:
    118         Paint p = new Paint();
    119         p.setARGB(100, 0, 0, 0);
    120         selectionWidget.setBackgroundPaint(p);
    121 
    122         selectionWidget.position(
    123                 0, XLayoutStyle.RELATIVE_TO_CENTER,
    124                 PixelUtils.dpToPix(45), YLayoutStyle.ABSOLUTE_FROM_TOP,
    125                 AnchorPosition.TOP_MIDDLE);
    126         selectionWidget.pack();
    127 
    128 
    129         // reduce the number of range labels
    130         plot.setTicksPerRangeLabel(3);
    131         plot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
    132         plot.getGraphWidget().setGridPadding(30, 10, 30, 0);
    133 
    134         plot.setTicksPerDomainLabel(2);
    135 
    136 
    137         // setup checkbox listers:
    138         series1CheckBox = (CheckBox) findViewById(R.id.s1CheckBox);
    139         series1CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    140             @Override
    141             public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
    142                 onS1CheckBoxClicked(b);
    143             }
    144         });
    145 
    146         series2CheckBox = (CheckBox) findViewById(R.id.s2CheckBox);
    147         series2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    148             @Override
    149             public void onCheckedChanged(CompoundButton compoundButton, boolean b) {onS2CheckBoxClicked(b);
    150             }
    151         });
    152 
    153         plot.setOnTouchListener(new View.OnTouchListener() {
    154             @Override
    155             public boolean onTouch(View view, MotionEvent motionEvent) {
    156                 if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
    157                     onPlotClicked(new PointF(motionEvent.getX(), motionEvent.getY()));
    158                 }
    159                 return true;
    160             }
    161         });
    162 
    163         spRenderStyle = (Spinner) findViewById(R.id.spRenderStyle);
    164         ArrayAdapter <BarRenderer.BarRenderStyle> adapter = new ArrayAdapter <BarRenderer.BarRenderStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarRenderStyle.values() );
    165         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    166         spRenderStyle.setAdapter(adapter);
    167         spRenderStyle.setSelection(BarRenderer.BarRenderStyle.OVERLAID.ordinal());
    168         spRenderStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
    169             public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
    170                 updatePlot();
    171             }
    172 			@Override
    173 			public void onNothingSelected(AdapterView<?> arg0) {
    174 			}
    175         });
    176 
    177         spWidthStyle = (Spinner) findViewById(R.id.spWidthStyle);
    178         ArrayAdapter <BarRenderer.BarWidthStyle> adapter1 = new ArrayAdapter <BarRenderer.BarWidthStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarWidthStyle.values() );
    179         adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    180         spWidthStyle.setAdapter(adapter1);
    181         spWidthStyle.setSelection(BarRenderer.BarWidthStyle.FIXED_WIDTH.ordinal());
    182         spWidthStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
    183             public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
    184             	if (BarRenderer.BarWidthStyle.FIXED_WIDTH.equals(spWidthStyle.getSelectedItem())) {
    185             		sbFixedWidth.setVisibility(View.VISIBLE);
    186             		sbVariableWidth.setVisibility(View.INVISIBLE);
    187             	} else {
    188             		sbFixedWidth.setVisibility(View.INVISIBLE);
    189             		sbVariableWidth.setVisibility(View.VISIBLE);
    190             	}
    191                 updatePlot();
    192             }
    193 			@Override
    194 			public void onNothingSelected(AdapterView<?> arg0) {
    195 			}
    196         });
    197 
    198         spSeriesSize = (Spinner) findViewById(R.id.spSeriesSize);
    199         ArrayAdapter <SeriesSize> adapter11 = new ArrayAdapter <SeriesSize> (this, android.R.layout.simple_spinner_item, SeriesSize.values() );
    200         adapter11.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    201         spSeriesSize.setAdapter(adapter11);
    202         spSeriesSize.setSelection(SeriesSize.TEN.ordinal());
    203         spSeriesSize.setOnItemSelectedListener(new OnItemSelectedListener() {
    204             public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
    205                 switch ((SeriesSize)arg0.getSelectedItem()) {
    206 				case TEN:
    207 					series1Numbers = series1Numbers10;
    208 					series2Numbers = series2Numbers10;
    209 					break;
    210 				case TWENTY:
    211 					series1Numbers = series1Numbers20;
    212 					series2Numbers = series2Numbers20;
    213 					break;
    214 				case SIXTY:
    215 					series1Numbers = series1Numbers60;
    216 					series2Numbers = series2Numbers60;
    217 					break;
    218 				default:
    219 					break;
    220                 }
    221                 updatePlot();
    222             }
    223 			@Override
    224 			public void onNothingSelected(AdapterView<?> arg0) {
    225 			}
    226         });
    227 
    228 
    229         sbFixedWidth = (SeekBar) findViewById(R.id.sbFixed);
    230         sbFixedWidth.setProgress(50);
    231         sbFixedWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    232             @Override
    233             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
    234             @Override
    235             public void onStartTrackingTouch(SeekBar seekBar) {}
    236 
    237             @Override
    238             public void onStopTrackingTouch(SeekBar seekBar) {}
    239         });
    240 
    241 
    242         sbVariableWidth = (SeekBar) findViewById(R.id.sbVariable);
    243         sbVariableWidth.setProgress(1);
    244         sbVariableWidth.setVisibility(View.INVISIBLE);
    245         sbVariableWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    246             @Override
    247             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
    248             @Override
    249             public void onStartTrackingTouch(SeekBar seekBar) {}
    250             @Override
    251             public void onStopTrackingTouch(SeekBar seekBar) {}
    252         });
    253 
    254         plot.setDomainValueFormat(new NumberFormat() {
    255             @Override
    256             public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
    257                 int year = (int) (value + 0.5d) / 12;
    258                 int month = (int) ((value + 0.5d) % 12);
    259                 return new StringBuffer(DateFormatSymbols.getInstance().getShortMonths()[month] + " '0" + year);
    260             }
    261 
    262             @Override
    263             public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
    264                 throw new UnsupportedOperationException("Not yet implemented.");
    265             }
    266 
    267             @Override
    268             public Number parse(String string, ParsePosition position) {
    269                 throw new UnsupportedOperationException("Not yet implemented.");
    270             }
    271         });
    272         updatePlot();
    273 
    274     }
    275 
    276     private void updatePlot() {
    277 
    278     	// Remove all current series from each plot
    279         Iterator<XYSeries> iterator1 = plot.getSeriesSet().iterator();
    280         while(iterator1.hasNext()) {
    281         	XYSeries setElement = iterator1.next();
    282         	plot.removeSeries(setElement);
    283         }
    284 
    285         // Setup our Series with the selected number of elements
    286         series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Us");
    287         series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Them");
    288 
    289         // add a new series' to the xyplot:
    290         if (series1CheckBox.isChecked()) plot.addSeries(series1, formatter1);
    291         if (series2CheckBox.isChecked()) plot.addSeries(series2, formatter2);
    292 
    293         // Setup the BarRenderer with our selected options
    294         MyBarRenderer renderer = ((MyBarRenderer)plot.getRenderer(MyBarRenderer.class));
    295         renderer.setBarRenderStyle((BarRenderer.BarRenderStyle)spRenderStyle.getSelectedItem());
    296         renderer.setBarWidthStyle((BarRenderer.BarWidthStyle)spWidthStyle.getSelectedItem());
    297         renderer.setBarWidth(sbFixedWidth.getProgress());
    298         renderer.setBarGap(sbVariableWidth.getProgress());
    299 
    300         if (BarRenderer.BarRenderStyle.STACKED.equals(spRenderStyle.getSelectedItem())) {
    301         	plot.setRangeTopMin(15);
    302         } else {
    303         	plot.setRangeTopMin(0);
    304         }
    305 
    306         plot.redraw();
    307 
    308     }
    309 
    310     private void onPlotClicked(PointF point) {
    311 
    312         // make sure the point lies within the graph area.  we use gridrect
    313         // because it accounts for margins and padding as well.
    314         if (plot.getGraphWidget().getGridRect().contains(point.x, point.y)) {
    315             Number x = plot.getXVal(point);
    316             Number y = plot.getYVal(point);
    317 
    318 
    319             selection = null;
    320             double xDistance = 0;
    321             double yDistance = 0;
    322 
    323             // find the closest value to the selection:
    324             for (XYSeries series : plot.getSeriesSet()) {
    325                 for (int i = 0; i < series.size(); i++) {
    326                     Number thisX = series.getX(i);
    327                     Number thisY = series.getY(i);
    328                     if (thisX != null && thisY != null) {
    329                         double thisXDistance =
    330                                 LineRegion.measure(x, thisX).doubleValue();
    331                         double thisYDistance =
    332                                 LineRegion.measure(y, thisY).doubleValue();
    333                         if (selection == null) {
    334                             selection = new Pair<Integer, XYSeries>(i, series);
    335                             xDistance = thisXDistance;
    336                             yDistance = thisYDistance;
    337                         } else if (thisXDistance < xDistance) {
    338                             selection = new Pair<Integer, XYSeries>(i, series);
    339                             xDistance = thisXDistance;
    340                             yDistance = thisYDistance;
    341                         } else if (thisXDistance == xDistance &&
    342                                 thisYDistance < yDistance &&
    343                                 thisY.doubleValue() >= y.doubleValue()) {
    344                             selection = new Pair<Integer, XYSeries>(i, series);
    345                             xDistance = thisXDistance;
    346                             yDistance = thisYDistance;
    347                         }
    348                     }
    349                 }
    350             }
    351 
    352         } else {
    353             // if the press was outside the graph area, deselect:
    354             selection = null;
    355         }
    356 
    357         if(selection == null) {
    358             selectionWidget.setText(NO_SELECTION_TXT);
    359         } else {
    360             selectionWidget.setText("Selected: " + selection.second.getTitle() +
    361                     " Value: " + selection.second.getY(selection.first));
    362         }
    363         plot.redraw();
    364     }
    365 
    366     private void onS1CheckBoxClicked(boolean checked) {
    367         if (checked) {
    368             plot.addSeries(series1, formatter1);
    369         } else {
    370             plot.removeSeries(series1);
    371         }
    372         plot.redraw();
    373     }
    374 
    375     private void onS2CheckBoxClicked(boolean checked) {
    376         if (checked) {
    377             plot.addSeries(series2, formatter2);
    378         } else {
    379             plot.removeSeries(series2);
    380         }
    381         plot.redraw();
    382     }
    383 
    384     class MyBarFormatter extends BarFormatter {
    385         public MyBarFormatter(int fillColor, int borderColor) {
    386             super(fillColor, borderColor);
    387         }
    388 
    389         @Override
    390         public Class<? extends SeriesRenderer> getRendererClass() {
    391             return MyBarRenderer.class;
    392         }
    393 
    394         @Override
    395         public SeriesRenderer getRendererInstance(XYPlot plot) {
    396             return new MyBarRenderer(plot);
    397         }
    398     }
    399 
    400     class MyBarRenderer extends BarRenderer<MyBarFormatter> {
    401 
    402         public MyBarRenderer(XYPlot plot) {
    403             super(plot);
    404         }
    405 
    406         /**
    407          * Implementing this method to allow us to inject our
    408          * special selection formatter.
    409          * @param index index of the point being rendered.
    410          * @param series XYSeries to which the point being rendered belongs.
    411          * @return
    412          */
    413         //@Override
    414         // TODO: figure out why using @Override screws up the Maven builds
    415         protected MyBarFormatter getFormatter(int index, XYSeries series) {
    416             if(selection != null &&
    417                     selection.second == series &&
    418                     selection.first == index) {
    419                 return selectionFormatter;
    420             } else {
    421                 return getFormatter(series);
    422             }
    423         }
    424     }
    425 }
    426