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