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 android.app.Activity; 20 import android.graphics.Color; 21 import android.graphics.DashPathEffect; 22 import android.graphics.Paint; 23 import android.os.Bundle; 24 import android.widget.CheckBox; 25 import android.widget.CompoundButton; 26 import com.androidplot.util.PixelUtils; 27 import com.androidplot.xy.XYSeries; 28 import com.androidplot.ui.*; 29 import com.androidplot.xy.*; 30 31 import java.text.DecimalFormat; 32 import java.text.FieldPosition; 33 import java.text.NumberFormat; 34 import java.text.ParsePosition; 35 import java.util.Arrays; 36 37 /** 38 * Demonstration of the usage of Marker and RectRegion. 39 */ 40 public class XYRegionExampleActivity extends Activity { 41 42 private static final float HOME_RUN_DIST = 325; 43 private static final int LINE_THICKNESS_DP = 2; 44 private static final int POINT_SIZE_DP = 6; 45 private XYPlot plot; 46 private final Number[] timHits = {105, 252, 220, 350, 12, 250, 353}; 47 private final Number[] nickHits = {110, 191, 61, 371, 289, 101, 10}; 48 private final Number[] joeHits = {25, 375, 364, 128, 178, 289, 346}; 49 private final Number[] jamesHits = {250, 285, 295, 211, 311, 365, 241}; 50 private LineAndPointFormatter timFormatter; 51 private LineAndPointFormatter nickFormatter; 52 private LineAndPointFormatter joeFormatter; 53 private LineAndPointFormatter jamesFormatter; 54 55 private XYSeries timSeries; 56 private XYSeries nickSeries; 57 private XYSeries joeSeries; 58 private XYSeries jamesSeries; 59 60 private RectRegion shortRegion; 61 private RectRegion warmupRegion; 62 private RectRegion homeRunRegion; 63 64 //private XYRegionFormatter rf1; 65 private XYRegionFormatter shortRegionFormatter; 66 private XYRegionFormatter warmupRegionFormatter; 67 private XYRegionFormatter homeRunRegionFormatter; 68 //private XYRegionFormatter rf5; 69 70 private CheckBox timCB; 71 private CheckBox nickCB; 72 private CheckBox joeCB; 73 private CheckBox jamesCB; 74 75 private CheckBox r2CheckBox; 76 private CheckBox r3CheckBox; 77 private CheckBox r4CheckBox; 78 onCreate(Bundle savedInstanceState)79 public void onCreate(Bundle savedInstanceState) { 80 super.onCreate(savedInstanceState); 81 setContentView(R.layout.xyregion_example); 82 plot = (XYPlot) findViewById(R.id.xyRegionExamplePlot); 83 timCB = (CheckBox) findViewById(R.id.s1CheckBox); 84 timCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 85 @Override 86 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 87 onS1CheckBoxClicked(); 88 } 89 }); 90 91 nickCB = (CheckBox) findViewById(R.id.s2CheckBox); 92 nickCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 93 @Override 94 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 95 onS2CheckBoxClicked(); 96 } 97 }); 98 99 joeCB = (CheckBox) findViewById(R.id.s3CheckBox); 100 joeCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 101 @Override 102 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 103 onS3CheckBoxClicked(); 104 } 105 }); 106 107 jamesCB = (CheckBox) findViewById(R.id.s4CheckBox); 108 jamesCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 109 @Override 110 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 111 onS4CheckBoxClicked(); 112 } 113 }); 114 115 116 117 r2CheckBox = (CheckBox) findViewById(R.id.r2CheckBox); 118 r2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 119 @Override 120 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 121 onCheckBoxClicked(r2CheckBox, timFormatter, shortRegionFormatter, shortRegion); 122 } 123 }); 124 125 r3CheckBox = (CheckBox) findViewById(R.id.r3CheckBox); 126 r3CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 127 @Override 128 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 129 onCheckBoxClicked(r3CheckBox, nickFormatter, warmupRegionFormatter, warmupRegion); 130 } 131 }); 132 133 r4CheckBox = (CheckBox) findViewById(R.id.r4CheckBox); 134 r4CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 135 @Override 136 public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 137 onCheckBoxClicked(r4CheckBox, nickFormatter, homeRunRegionFormatter, homeRunRegion); 138 } 139 }); 140 141 seriesSetup(); 142 markerSetup(); 143 axisLabelSetup(); 144 regionSetup(); 145 makePlotPretty(); 146 } 147 onS1CheckBoxClicked()148 private void onS1CheckBoxClicked() { 149 if(timCB.isChecked()) { 150 plot.addSeries(timSeries, timFormatter); 151 r2CheckBox.setEnabled(true); 152 } else { 153 plot.removeSeries(timSeries); 154 r2CheckBox.setEnabled(false); 155 } 156 plot.redraw(); 157 } 158 onS2CheckBoxClicked()159 private void onS2CheckBoxClicked() { 160 if(nickCB.isChecked()) { 161 plot.addSeries(nickSeries, nickFormatter); 162 r3CheckBox.setEnabled(true); 163 r4CheckBox.setEnabled(true); 164 } else { 165 plot.removeSeries(nickSeries); 166 r3CheckBox.setEnabled(false); 167 r4CheckBox.setEnabled(false); 168 } 169 plot.redraw(); 170 } 171 onS3CheckBoxClicked()172 private void onS3CheckBoxClicked() { 173 if(joeCB.isChecked()) { 174 plot.addSeries(joeSeries, joeFormatter); 175 } else { 176 plot.removeSeries(joeSeries); 177 } 178 plot.redraw(); 179 } 180 onS4CheckBoxClicked()181 private void onS4CheckBoxClicked() { 182 if(jamesCB.isChecked()) { 183 plot.addSeries(jamesSeries, jamesFormatter); 184 } else { 185 plot.removeSeries(jamesSeries); 186 } 187 plot.redraw(); 188 } 189 190 /** 191 * Processes a check box event 192 * @param cb The checkbox event origin 193 * @param lpf LineAndPointFormatter with which rr and rf are to be added/removed 194 * @param rf The XYRegionFormatter with which rr should be rendered 195 * @param rr The RectRegion to add/remove 196 */ onCheckBoxClicked(CheckBox cb, LineAndPointFormatter lpf, XYRegionFormatter rf, RectRegion rr)197 private void onCheckBoxClicked(CheckBox cb, LineAndPointFormatter lpf, 198 XYRegionFormatter rf, RectRegion rr) { 199 if(cb.isChecked()) { 200 lpf.removeRegion(rr); 201 } else { 202 lpf.addRegion(rr, rf); 203 } 204 } 205 206 /** 207 * Cleans up the plot's general layout and color scheme 208 */ makePlotPretty()209 private void makePlotPretty() { 210 // use a 2x5 grid with room for 10 items: 211 plot.getLegendWidget().setTableModel(new DynamicTableModel(4, 2)); 212 213 // add a semi-transparent black background to the legend 214 // so it's easier to see overlaid on top of our plot: 215 Paint bgPaint = new Paint(); 216 bgPaint.setColor(Color.BLACK); 217 bgPaint.setStyle(Paint.Style.FILL); 218 bgPaint.setAlpha(40); 219 220 plot.getLegendWidget().setBackgroundPaint(bgPaint); 221 222 // adjust the padding of the legend widget to look a little nicer: 223 plot.getLegendWidget().setPadding(5, 5, 5, 5); 224 225 plot.setRangeValueFormat(new NumberFormat() { 226 @Override 227 public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) { 228 return new StringBuffer(value + "'"); 229 } 230 231 @Override 232 public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) { 233 throw new UnsupportedOperationException("Not yet implemented."); 234 } 235 236 @Override 237 public Number parse(String string, ParsePosition position) { 238 throw new UnsupportedOperationException("Not yet implemented."); 239 } 240 }); 241 242 plot.setDomainValueFormat(new DecimalFormat("#")); 243 244 plot.getLegendWidget().setWidth(PixelUtils.dpToPix(100), SizeLayoutType.FILL); 245 246 247 // adjust the legend size so there is enough room 248 // to draw the new legend grid: 249 //plot.getLegendWidget().getHeightMetric().setLayoutType(SizeLayoutType.ABSOLUTE); 250 //plot.getLegendWidget().getWidthMetric().setLayoutType(SizeLayoutType.ABSOLUTE); 251 //plot.getLegendWidget().setSize( 252 // new SizeMetrics(70, SizeLayoutType.ABSOLUTE, 80, SizeLayoutType.ABSOLUTE)); 253 254 // reposition the grid so that it rests above the bottom-left 255 // edge of the graph widget: 256 257 plot.getLegendWidget().position( 258 125, 259 XLayoutStyle.ABSOLUTE_FROM_LEFT, 260 65, 261 YLayoutStyle.ABSOLUTE_FROM_TOP, 262 AnchorPosition.LEFT_TOP); 263 264 plot.getGraphWidget().setRangeLabelHorizontalOffset(-1); 265 266 // add enough space to ensure range value labels arent cut off on the left/right: 267 plot.getGraphWidget().setRangeLabelWidth(25); 268 269 // add enough space to make sure domain value labels arent cut off on the bottom: 270 plot.getGraphWidget().setDomainLabelWidth(15); 271 272 plot.getGraphWidget().setDomainLabelVerticalOffset(-6); 273 274 plot.setRangeBoundaries(0, BoundaryMode.FIXED, 500, BoundaryMode.FIXED); 275 } 276 277 /** 278 * Create 4 XYSeries from the values defined above add add them to the plot. 279 * Also add some arbitrary regions. 280 */ seriesSetup()281 private void seriesSetup() { 282 283 284 // TIM 285 timFormatter = new LineAndPointFormatter( 286 Color.rgb(100, 25, 20), 287 Color.rgb(100, 25, 20), 288 null, null); 289 timFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP)); 290 timFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP)); 291 292 timSeries = new SimpleXYSeries(Arrays.asList(timHits), 293 SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Tim"); 294 295 plot.addSeries(timSeries, timFormatter); 296 297 // SERIES #2: 298 nickFormatter = new LineAndPointFormatter( 299 Color.rgb(100, 25, 200), 300 Color.rgb(100, 25, 200), 301 null, null); 302 nickFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP)); 303 nickFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP)); 304 305 306 307 nickSeries = new SimpleXYSeries(Arrays.asList(nickHits), 308 SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Nick"); 309 310 plot.addSeries(nickSeries, nickFormatter); 311 312 // SERIES #3: 313 joeFormatter = new LineAndPointFormatter( 314 Color.rgb(200, 25, 200), 315 Color.rgb(200, 25, 200), 316 null, null); 317 joeFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP)); 318 joeFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP)); 319 320 joeSeries = new SimpleXYSeries(Arrays.asList(joeHits), 321 SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Joe"); 322 323 plot.addSeries(joeSeries, joeFormatter); 324 325 // SERIES #4: 326 jamesFormatter = new LineAndPointFormatter( 327 Color.rgb(220, 25, 20), 328 Color.rgb(220, 25, 20), 329 null, null); 330 331 jamesFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP)); 332 jamesFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP)); 333 334 jamesSeries = new SimpleXYSeries(Arrays.asList(jamesHits), 335 SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,"James"); 336 plot.addSeries(jamesSeries, jamesFormatter); 337 338 plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 100); 339 //plot.setTicksPerRangeLabel(1); 340 plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1); 341 } 342 343 /** 344 * Add some color coded regions to our axis labels. 345 */ axisLabelSetup()346 private void axisLabelSetup() { 347 // DOMAIN 348 plot.getGraphWidget().addDomainAxisValueLabelRegion( 349 Double.NEGATIVE_INFINITY, 2, new AxisValueLabelFormatter(Color.GRAY)); 350 plot.getGraphWidget().addDomainAxisValueLabelRegion( 351 2, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.WHITE)); 352 // RANGE 353 plot.getGraphWidget().addRangeAxisValueLabelRegion( 354 Double.NEGATIVE_INFINITY, HOME_RUN_DIST, new AxisValueLabelFormatter(Color.RED)); 355 plot.getGraphWidget().addRangeAxisValueLabelRegion( 356 HOME_RUN_DIST, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.GREEN)); 357 } 358 359 /** 360 * Add some markers to our plot. 361 */ markerSetup()362 private void markerSetup() { 363 364 YValueMarker fenwayLfMarker = new YValueMarker( 365 380, // y-val to mark 366 "Fenway Park LF Wall", // marker label 367 new XPositionMetric( // object instance to set text positioning on the marker 368 PixelUtils.dpToPix(5), // 5dp offset 369 XLayoutStyle.ABSOLUTE_FROM_RIGHT), // offset origin 370 Color.BLUE, // line paint color 371 Color.BLUE); // text paint color 372 373 YValueMarker attRfMarker = new YValueMarker( 374 309, // y-val to mark 375 "ATT Park RF Wall", // marker label 376 new XPositionMetric( // object instance to set text positioning on the marker 377 PixelUtils.dpToPix(5), // 5dp offset 378 XLayoutStyle.ABSOLUTE_FROM_RIGHT), // offset origin 379 Color.CYAN, // line paint color 380 Color.CYAN); // text paint color 381 382 383 fenwayLfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14)); 384 attRfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14)); 385 386 DashPathEffect dpe = new DashPathEffect( 387 new float[]{PixelUtils.dpToPix(2), PixelUtils.dpToPix(2)}, 0); 388 389 fenwayLfMarker.getLinePaint().setPathEffect(dpe); 390 attRfMarker.getLinePaint().setPathEffect(dpe); 391 392 plot.addMarker(fenwayLfMarker); 393 plot.addMarker(attRfMarker); 394 } 395 396 /** 397 * Add some fill regions to our series data 398 */ regionSetup()399 private void regionSetup() { 400 401 402 // and another region: 403 shortRegionFormatter = new XYRegionFormatter(Color.RED); 404 shortRegionFormatter.getPaint().setAlpha(75); 405 shortRegion = new RectRegion(2, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, HOME_RUN_DIST, "Short"); 406 timFormatter.addRegion(shortRegion, shortRegionFormatter); 407 nickFormatter.addRegion(shortRegion, shortRegionFormatter); 408 joeFormatter.addRegion(shortRegion, shortRegionFormatter); 409 jamesFormatter.addRegion(shortRegion, shortRegionFormatter); 410 411 // the next three regions are horizontal regions with minY/maxY 412 // set to negative and positive infinity respectively. 413 warmupRegionFormatter = new XYRegionFormatter(Color.WHITE); 414 warmupRegionFormatter.getPaint().setAlpha(75); 415 416 warmupRegion = new RectRegion(0, 2, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "Warmup"); 417 timFormatter.addRegion(warmupRegion, warmupRegionFormatter); 418 nickFormatter.addRegion(warmupRegion, warmupRegionFormatter); 419 joeFormatter.addRegion(warmupRegion, warmupRegionFormatter); 420 jamesFormatter.addRegion(warmupRegion, warmupRegionFormatter); 421 422 homeRunRegionFormatter = new XYRegionFormatter(Color.GREEN); 423 homeRunRegionFormatter.getPaint().setAlpha(75); 424 425 homeRunRegion = new RectRegion(2, Double.POSITIVE_INFINITY, HOME_RUN_DIST, Double.POSITIVE_INFINITY, "H. Run"); 426 timFormatter.addRegion(homeRunRegion, homeRunRegionFormatter); 427 nickFormatter.addRegion(homeRunRegion, homeRunRegionFormatter); 428 joeFormatter.addRegion(homeRunRegion, homeRunRegionFormatter); 429 jamesFormatter.addRegion(homeRunRegion, homeRunRegionFormatter); 430 431 nickFormatter.setFillDirection(FillDirection.RANGE_ORIGIN); 432 } 433 }