Skip to content

Commit 33a7196

Browse files
authored
Merge pull request #3 from Valodim/worker-thread
Add methods for rendering on worker thread
2 parents a51aac7 + 6109098 commit 33a7196

File tree

2 files changed

+82
-14
lines changed

2 files changed

+82
-14
lines changed

HeatMapLib/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ android {
2222

2323
dependencies {
2424
compile fileTree(dir: 'libs', include: ['*.jar'])
25+
compile 'com.android.support:support-annotations:24.2.1'
2526
}
2627

2728
apply plugin: 'com.github.dcendents.android-maven'

HeatMapLib/src/main/java/ca/hss/heatmaplib/HeatMap.java

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import android.graphics.PorterDuff;
2929
import android.graphics.RadialGradient;
3030
import android.graphics.Shader;
31+
import android.support.annotation.AnyThread;
32+
import android.support.annotation.WorkerThread;
3133
import android.util.AttributeSet;
3234
import android.view.MotionEvent;
3335
import android.view.View;
@@ -136,10 +138,13 @@ public class HeatMap extends View implements View.OnTouchListener {
136138

137139
private Canvas mShadowCanvas = null;
138140

141+
private final Object tryRefreshLock = new Object();
142+
139143
/**
140144
* Set the blur factor for the heat map. Must be between 0 and 1.
141145
* @param blur The blur factor
142146
*/
147+
@AnyThread
143148
public void setBlur(double blur) {
144149
if (blur > 1.0 || blur < 0.0)
145150
throw new IllegalArgumentException("Blur must be between 0 and 1.");
@@ -148,6 +153,7 @@ public void setBlur(double blur) {
148153
/**
149154
* Get the heat map's blur factor.
150155
*/
156+
@AnyThread
151157
public double getBlur() { return mBlur; }
152158

153159
/**
@@ -156,6 +162,7 @@ public void setBlur(double blur) {
156162
* This should be greater than the minimum value.
157163
* @param max The maximum value.
158164
*/
165+
@AnyThread
159166
public void setMaximum(double max) { this.max = max; }
160167

161168
/**
@@ -164,37 +171,43 @@ public void setBlur(double blur) {
164171
* This should be less than the maximum value.
165172
* @param min The minimum value.
166173
*/
174+
@AnyThread
167175
public void setMinimum(double min) { this.min = min; }
168176

169177
/**
170178
* Set the opacity to be used in the heat map. This opacity will be used for the entire map.
171179
* @param opacity The opacity in the range [0,255].
172180
*/
181+
@AnyThread
173182
public void setOpacity(int opacity) { this.opacity = opacity; }
174183

175184
/**
176185
* Set the minimum opacity to be used in the map. Only used when {@link HeatMap#opacity} is 0.
177186
* @param min The minimum opacity in the range [0,255].
178187
*/
188+
@AnyThread
179189
public void setMinimumOpactity(int min) { this.minOpacity = min; }
180190

181191
/**
182192
* Set the maximum opacity to be used in the map. Only used when {@link HeatMap#opacity} is 0.
183193
* @param max The maximum opacity in the range [0,255].
184194
*/
195+
@AnyThread
185196
public void setMaximumOpactity(int max) { this.maxOpacity = max; }
186197

187198
/**
188199
* Set the circles radius when drawing data points.
189200
* @param radius The radius in pixels.
190201
*/
202+
@AnyThread
191203
public void setRadius(double radius) { this.mRadius = radius; }
192204

193205
/**
194206
* Set the color stops used for the heat map's gradient. There needs to be at least 2 stops
195207
* and there should be one at a position of 0 and one at a position of 1.
196208
* @param stops A map from stop positions (as fractions of the width in [0,1]) to ARGB colors.
197209
*/
210+
@AnyThread
198211
public void setColorStops(Map<Float, Integer> stops) {
199212
if (stops.size() < 2)
200213
throw new IllegalArgumentException("There must be at least 2 color stops");
@@ -214,6 +227,7 @@ public void setColorStops(Map<Float, Integer> stops) {
214227
* Does not refresh the display. See {@link HeatMap#forceRefresh()} in order to redraw the heat map.
215228
* @param point A new data point.
216229
*/
230+
@AnyThread
217231
public void addData(DataPoint point) {
218232
dataBuffer.add(point);
219233
dataModified = true;
@@ -224,6 +238,7 @@ public void addData(DataPoint point) {
224238
*
225239
* Does not refresh the display. See {@link HeatMap#forceRefresh()} in order to redraw the heat map.
226240
*/
241+
@AnyThread
227242
public void clearData() {
228243
dataBuffer.clear();
229244
dataModified = true;
@@ -315,23 +330,69 @@ private void initialize() {
315330
this.setDrawingCacheBackgroundColor(Color.TRANSPARENT);
316331
}
317332

318-
private void redrawShadow() {
333+
@AnyThread
334+
private void redrawShadow(Bitmap drawingCache, int width, int height) {
319335
mRenderBoundaries[0] = 10000;
320336
mRenderBoundaries[1] = 10000;
321337
mRenderBoundaries[2] = 0;
322338
mRenderBoundaries[3] = 0;
323339

324-
mShadow = getDrawingCache();
340+
mShadow = drawingCache;
325341
mShadowCanvas = new Canvas(mShadow);
326342

327-
drawTransparent(mShadowCanvas);
343+
drawTransparent(mShadowCanvas, width, height);
344+
}
345+
346+
/**
347+
* Draws the heatmap from a background thread.
348+
*
349+
* This allows offloading some of the work that would usualy be done in
350+
* {@link #onDraw(Canvas)} into a background thread. If the view is redrawn
351+
* for some reason while this operation is still ongoing, the UI thread
352+
* will block until this call is finished.
353+
*
354+
* The caller should take care to invalidate the view on the UI thread
355+
* afterwards, but not before this call has finished.
356+
*
357+
* <pre>{@code
358+
* final HeatMap heatmap = (HeatMap) findViewById(R.id.heatmap);
359+
* new AsyncTask<Void,Void,Void>() {
360+
* protected Void doInBackground(Void... params) {
361+
* Random rand = new Random();
362+
* //add 20 random points of random intensity
363+
* for (int i = 0; i < 20; i++) {
364+
* heatmap.addData(getRandomDataPoint());
365+
* }
366+
*
367+
* heatmap.refreshImmediateInBackgroundThread();
368+
*
369+
* return null;
370+
* }
371+
*
372+
* protected void onPostExecute(Void aVoid) {
373+
* heatmap.invalidate();
374+
* heatmap.setAlpha(0.0f);
375+
* heatmap.animate().alpha(1.0f).setDuration(700L).start();
376+
* }
377+
* }.execute();
378+
* }</pre>
379+
*/
380+
@WorkerThread
381+
public void forceRefreshOnWorkerThread() {
382+
synchronized (tryRefreshLock) {
383+
// These getters are in fact available on this thread. The caller will have to
384+
// take care that the view is in an acceptable state here.
385+
// noinspection WrongThread
386+
tryRefresh(true, getDrawingCache(), getWidth(), getHeight());
387+
}
328388
}
329389

330390
/**
331391
* If needed, refresh the palette.
332392
*/
333-
private void tryRefresh() {
334-
if (needsRefresh) {
393+
@AnyThread
394+
private void tryRefresh(boolean forceRefresh, Bitmap drawingCache, int width, int height) {
395+
if (forceRefresh || needsRefresh) {
335396
Bitmap bit = Bitmap.createBitmap(256, 1, Bitmap.Config.ARGB_8888);
336397
Canvas canvas = new Canvas(bit);
337398
LinearGradient grad;
@@ -350,13 +411,13 @@ private void tryRefresh() {
350411
dataModified = false;
351412
}
352413

353-
redrawShadow();
354-
}
355-
else if (sizeChange) {
356-
redrawShadow();
414+
redrawShadow(drawingCache, width, height);
415+
} else if (sizeChange) {
416+
sizeChange = false;
417+
redrawShadow(drawingCache, width, height);
357418
}
419+
358420
needsRefresh = false;
359-
sizeChange = false;
360421
}
361422

362423
@Override
@@ -372,7 +433,9 @@ public void onSizeChanged(int w, int h, int oldw, int oldh) {
372433
*/
373434
@Override
374435
protected void onDraw(Canvas canvas) {
375-
tryRefresh();
436+
synchronized (tryRefreshLock) {
437+
tryRefresh(false, getDrawingCache(), getWidth(), getHeight());
438+
}
376439

377440
drawColour(canvas);
378441
}
@@ -388,6 +451,7 @@ protected void onDraw(Canvas canvas) {
388451
* @param blurFactor A factor to scale the circles width by.
389452
* @param alpha The transparency of the gradient.
390453
*/
454+
@AnyThread
391455
private void drawDataPoint(Canvas canvas, float x, float y, double radius, double blurFactor, double alpha) {
392456
if (blurFactor == 1) {
393457
canvas.drawCircle(x, y, (float)radius, mBlack);
@@ -407,8 +471,11 @@ private void drawDataPoint(Canvas canvas, float x, float y, double radius, doubl
407471
* version.
408472
*
409473
* @param canvas Canvas to draw into.
474+
* @param width The width of the view
475+
* @param height The height of the view
410476
*/
411-
private void drawTransparent(Canvas canvas) {
477+
@AnyThread
478+
private void drawTransparent(Canvas canvas, int width, int height) {
412479
//invert the blur factor
413480
double blur = 1 - mBlur;
414481

@@ -417,8 +484,8 @@ private void drawTransparent(Canvas canvas) {
417484

418485
//loop through the data points
419486
for (DataPoint point : data) {
420-
float x = point.x * getWidth();
421-
float y = point.y * getHeight();
487+
float x = point.x * width;
488+
float y = point.y * height;
422489
double value = Math.max(min, Math.min(point.value, max));
423490
//the edge of the bounding rectangle for the circle
424491
double rectX = x - mRadius;

0 commit comments

Comments
 (0)