1/* 2 * Copyright 2013 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 17package com.androidplot.demos; 18import android.app.Activity; 19import android.graphics.Color; 20import android.graphics.DashPathEffect; 21import android.graphics.Paint; 22import android.os.Bundle; 23import com.androidplot.Plot; 24import com.androidplot.util.PixelUtils; 25import com.androidplot.xy.XYSeries; 26import com.androidplot.xy.*; 27 28import java.text.DecimalFormat; 29import java.util.Observable; 30import java.util.Observer; 31 32public class DynamicXYPlotActivity extends Activity { 33 34 // redraws a plot whenever an update is received: 35 private class MyPlotUpdater implements Observer { 36 Plot plot; 37 38 public MyPlotUpdater(Plot plot) { 39 this.plot = plot; 40 } 41 42 @Override 43 public void update(Observable o, Object arg) { 44 plot.redraw(); 45 } 46 } 47 48 private XYPlot dynamicPlot; 49 private MyPlotUpdater plotUpdater; 50 SampleDynamicXYDatasource data; 51 private Thread myThread; 52 53 @Override 54 public void onCreate(Bundle savedInstanceState) { 55 56 // android boilerplate stuff 57 super.onCreate(savedInstanceState); 58 setContentView(R.layout.dynamicxyplot_example); 59 60 // get handles to our View defined in layout.xml: 61 dynamicPlot = (XYPlot) findViewById(R.id.dynamicXYPlot); 62 63 plotUpdater = new MyPlotUpdater(dynamicPlot); 64 65 // only display whole numbers in domain labels 66 dynamicPlot.getGraphWidget().setDomainValueFormat(new DecimalFormat("0")); 67 68 // getInstance and position datasets: 69 data = new SampleDynamicXYDatasource(); 70 SampleDynamicSeries sine1Series = new SampleDynamicSeries(data, 0, "Sine 1"); 71 SampleDynamicSeries sine2Series = new SampleDynamicSeries(data, 1, "Sine 2"); 72 73 LineAndPointFormatter formatter1 = new LineAndPointFormatter( 74 Color.rgb(0, 0, 0), null, null, null); 75 formatter1.getLinePaint().setStrokeJoin(Paint.Join.ROUND); 76 formatter1.getLinePaint().setStrokeWidth(10); 77 dynamicPlot.addSeries(sine1Series, 78 formatter1); 79 80 LineAndPointFormatter formatter2 = 81 new LineAndPointFormatter(Color.rgb(0, 0, 200), null, null, null); 82 formatter2.getLinePaint().setStrokeWidth(10); 83 formatter2.getLinePaint().setStrokeJoin(Paint.Join.ROUND); 84 85 //formatter2.getFillPaint().setAlpha(220); 86 dynamicPlot.addSeries(sine2Series, formatter2); 87 88 // hook up the plotUpdater to the data model: 89 data.addObserver(plotUpdater); 90 91 // thin out domain tick labels so they dont overlap each other: 92 dynamicPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL); 93 dynamicPlot.setDomainStepValue(5); 94 95 dynamicPlot.setRangeStepMode(XYStepMode.INCREMENT_BY_VAL); 96 dynamicPlot.setRangeStepValue(10); 97 98 dynamicPlot.setRangeValueFormat(new DecimalFormat("###.#")); 99 100 // uncomment this line to freeze the range boundaries: 101 dynamicPlot.setRangeBoundaries(-100, 100, BoundaryMode.FIXED); 102 103 // create a dash effect for domain and range grid lines: 104 DashPathEffect dashFx = new DashPathEffect( 105 new float[] {PixelUtils.dpToPix(3), PixelUtils.dpToPix(3)}, 0); 106 dynamicPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(dashFx); 107 dynamicPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(dashFx); 108 109 110 } 111 112 @Override 113 public void onResume() { 114 // kick off the data generating thread: 115 myThread = new Thread(data); 116 myThread.start(); 117 super.onResume(); 118 } 119 120 @Override 121 public void onPause() { 122 data.stopThread(); 123 super.onPause(); 124 } 125 126 class SampleDynamicXYDatasource implements Runnable { 127 128 // encapsulates management of the observers watching this datasource for update events: 129 class MyObservable extends Observable { 130 @Override 131 public void notifyObservers() { 132 setChanged(); 133 super.notifyObservers(); 134 } 135 } 136 137 private static final double FREQUENCY = 5; // larger is lower frequency 138 private static final int MAX_AMP_SEED = 100; 139 private static final int MIN_AMP_SEED = 10; 140 private static final int AMP_STEP = 1; 141 public static final int SINE1 = 0; 142 public static final int SINE2 = 1; 143 private static final int SAMPLE_SIZE = 30; 144 private int phase = 0; 145 private int sinAmp = 1; 146 private MyObservable notifier; 147 private boolean keepRunning = false; 148 149 { 150 notifier = new MyObservable(); 151 } 152 153 public void stopThread() { 154 keepRunning = false; 155 } 156 157 //@Override 158 public void run() { 159 try { 160 keepRunning = true; 161 boolean isRising = true; 162 while (keepRunning) { 163 164 Thread.sleep(10); // decrease or remove to speed up the refresh rate. 165 phase++; 166 if (sinAmp >= MAX_AMP_SEED) { 167 isRising = false; 168 } else if (sinAmp <= MIN_AMP_SEED) { 169 isRising = true; 170 } 171 172 if (isRising) { 173 sinAmp += AMP_STEP; 174 } else { 175 sinAmp -= AMP_STEP; 176 } 177 notifier.notifyObservers(); 178 } 179 } catch (InterruptedException e) { 180 e.printStackTrace(); 181 } 182 } 183 184 public int getItemCount(int series) { 185 return SAMPLE_SIZE; 186 } 187 188 public Number getX(int series, int index) { 189 if (index >= SAMPLE_SIZE) { 190 throw new IllegalArgumentException(); 191 } 192 return index; 193 } 194 195 public Number getY(int series, int index) { 196 if (index >= SAMPLE_SIZE) { 197 throw new IllegalArgumentException(); 198 } 199 double angle = (index + (phase))/FREQUENCY; 200 double amp = sinAmp * Math.sin(angle); 201 switch (series) { 202 case SINE1: 203 return amp; 204 case SINE2: 205 return -amp; 206 default: 207 throw new IllegalArgumentException(); 208 } 209 } 210 211 public void addObserver(Observer observer) { 212 notifier.addObserver(observer); 213 } 214 215 public void removeObserver(Observer observer) { 216 notifier.deleteObserver(observer); 217 } 218 219 } 220 221 class SampleDynamicSeries implements XYSeries { 222 private SampleDynamicXYDatasource datasource; 223 private int seriesIndex; 224 private String title; 225 226 public SampleDynamicSeries(SampleDynamicXYDatasource datasource, int seriesIndex, String title) { 227 this.datasource = datasource; 228 this.seriesIndex = seriesIndex; 229 this.title = title; 230 } 231 232 @Override 233 public String getTitle() { 234 return title; 235 } 236 237 @Override 238 public int size() { 239 return datasource.getItemCount(seriesIndex); 240 } 241 242 @Override 243 public Number getX(int index) { 244 return datasource.getX(seriesIndex, index); 245 } 246 247 @Override 248 public Number getY(int index) { 249 return datasource.getY(seriesIndex, index); 250 } 251 } 252} 253