App

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

PathPickerView.java
Go to the documentation of this file.
1 package mhr.appandroid.views.pathpicker;
2
3 import mhr.appandroid.R;
4 import mhr.appandroid.adapters.APDBitmap;
5 import mhr.appandroid.views.MaskView;
6 import mhr.appandroid.views.MaskView.OnMaskCanvasChangedListener;
7 import mhr.appandroid.views.brushpicker.RoundBrushPickerView;
8 import mhr.appandroid.views.brushpicker.RoundBrushPickerView.RoundBrushPickerEvent;
9 import mhr.appandroid.views.brushpicker.RoundBrushPickerView.RoundBrushPickerViewEventListener;
10 import mhr.appcore.bitmap.Depth;
11 import mhr.appcore.bitmap.NBitmap;
12 import mhr.appcore.blending.Blender;
13 import mhr.appcore.generators.BrushGenerator;
14 import mhr.appcore.utils.Rect;
15 import android.app.Activity;
16 import android.content.Context;
17 import android.graphics.BitmapFactory;
18 import android.graphics.BitmapShader;
19 import android.graphics.Paint;
20 import android.graphics.Shader.TileMode;
21 import android.os.Bundle;
22 import android.os.Parcelable;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.view.View.OnClickListener;
26 import android.widget.Button;
27 import android.widget.CheckBox;
28 import android.widget.CompoundButton;
29 import android.widget.FrameLayout;
30 import android.widget.SeekBar;
31 import android.widget.TextView;
32 import android.widget.CompoundButton.OnCheckedChangeListener;
33 import android.widget.SeekBar.OnSeekBarChangeListener;
34
40 public class PathPickerView extends FrameLayout implements OnSeekBarChangeListener, OnMaskCanvasChangedListener, OnCheckedChangeListener, OnClickListener {
41
42 //===== INTERFACES, CLASSES, ENUMS ==========================================================================================================//
43 //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
47 protected enum redrawScope {
48 REDRAW_NONE, REDRAW_JUST_OPACITY, REDRAW_ALL,
49 }
50
51 //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
55 public static enum PathPickerEvent {
56 EVENT_SELECTED,
57 EVENT_CANCELED,
58 EVENT_CHANGED,
59 EVENT_LIVE_PREVIEW_STATE_CHANGED
60 }
61
65 public static interface PathPickerViewEventListener {
71 public void onPathPickerEvent(PathPickerEvent e, PathPickerView v);
72 }
73
79 public static class PathParams {
80 public int opacity = 100;
81 public int flow = 100;
82 public int spacing = 50;
83
84 public PathParams() {
85
86 }
87
88 public PathParams(int opacity, int flow, int spacing) {
89 this.opacity = opacity;
90 this.flow = flow;
91 this.spacing = spacing;
92 }
93
94 public PathParams(PathParams other) {
95 opacity = other.opacity;
96 flow = other.flow;
97 spacing = other.spacing;
98 }
99 }
100
101 //===== FIELDS ==============================================================================================================================//
102 //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
103 protected SeekBar opacitySB;
104 protected SeekBar flowSB;
105 protected SeekBar spacingSB;
106 protected CheckBox livePreviewChB;
107 protected Button selectBtn;
108 protected Button cancelBtn;
109
110 protected MaskView previewMskV;
111 protected TextView opacityTV;
112 protected TextView flowTV;
113 protected TextView spacingTV;
114
115 protected boolean livePreviewOn;
116
117 protected NBitmap previewCanvas = null;
118
119 protected NBitmap brush = null;
120
121 protected PathParams oldParams = new PathParams();
122 protected PathParams newParams = new PathParams();
123
124 protected PathPickerViewEventListener listener = null;
125
126 //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
127
128 //===== CONSTRUCTORS, DESTRUCTORS, RELATED METHODS ==========================================================================================//
129 //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
133 protected void init() {
134 try {
135 ((Activity) getContext()).getLayoutInflater().inflate(R.layout.view_path_picker, this, true);
136 } catch (ClassCastException exc) {
137 // Tento kod probehne, pokud je poroblem s pretypovanim, napriklad pro nahled v Eclipse
138 TextView tv = new TextView(getContext());
139 tv.setText("Prewiew not available, requires Activity to be Context.");
140 addView(tv);
141 return;
142 }
143
144 // Nalezení potomků pro rychlý přístup
145 opacitySB = (SeekBar) findViewById(R.id.OpacityValueSB);
146 flowSB = (SeekBar) findViewById(R.id.FlowValueSB);
147 spacingSB = (SeekBar) findViewById(R.id.SpacingValueSB);
148 livePreviewChB = (CheckBox) findViewById(R.id.PathLivePreviewChckB);
149 opacityTV = (TextView) findViewById(R.id.OpacityValueTV);
150 flowTV = (TextView) findViewById(R.id.FlowValueTV);
151 spacingTV = (TextView) findViewById(R.id.SpacingValueTV);
152 previewMskV = (MaskView) findViewById(R.id.PathPreviewMskV);
153 selectBtn = (Button) findViewById(R.id.PathPickerSelectBtn);
154 cancelBtn = (Button) findViewById(R.id.PathPickerCancelBtn);
155
156 // Inicializace stavu třídy
157 livePreviewOn = livePreviewChB.isChecked();
158
159 // Nastavení vzhledu komponent
160 BitmapShader bsh = new BitmapShader(BitmapFactory.decodeResource(getResources(), R.drawable.checkboard), TileMode.REPEAT, TileMode.REPEAT);
161 previewMskV.getMaskViewBackground().setShader(bsh);
162
163 // Nastavení sebe jako posluchačů příslušných událostí.
164 opacitySB.setOnSeekBarChangeListener(this);
165 flowSB.setOnSeekBarChangeListener(this);
166 spacingSB.setOnSeekBarChangeListener(this);
167 previewMskV.setOnMaskCanvasChangedListener(this);
168 livePreviewChB.setOnCheckedChangeListener(this);
169 selectBtn.setOnClickListener(this);
170 cancelBtn.setOnClickListener(this);
171
172 updateLabels();
173 updateSeekBars();
174
175 brush = BrushGenerator.createRoundBrush(10, 0.5, Depth.DEPTH_8_BIT);
176 }
177
179 @Override
180 protected void onDetachedFromWindow() {
181 if (previewCanvas != null) {
182 previewCanvas.dispose();
183 previewCanvas = null;
184 }
185 if (brush != null) {
186 brush.dispose();
187 brush = null;
188 }
189 }
190
191 //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
192 public PathPickerView(Context context) {
193 super(context);
194 init();
195 }
196
197 public PathPickerView(Context context, AttributeSet attrs) {
198 super(context, attrs);
199 init();
200 }
201
202 public PathPickerView(Context context, AttributeSet attrs, int defStyle) {
203 super(context, attrs, defStyle);
204 init();
205 }
206
207 //===== METHODS =============================================================================================================================//
208 //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
209
213 protected void updateSeekBars() {
214 opacitySB.setProgress(newParams.opacity);
215 flowSB.setProgress(newParams.flow);
216 spacingSB.setProgress((int) ((500.0 / 3.0) * Math.log10(newParams.spacing) + 0.5));
217 }
218
222 protected void redrawPath(redrawScope scope) {
223 if (brush == null) {
224 return;
225 }
226 if (previewCanvas == null) {
227 if (!initPreviewCanvas(previewMskV)) {
228 return;
229 }
230 }
231 if (scope == redrawScope.REDRAW_NONE) {
232 return;
233 }
234
235 previewMskV.getMaskViewForeground().setAlpha((int) (newParams.opacity * 255 / 100.0 + 0.5));
236
237 if (scope == redrawScope.REDRAW_JUST_OPACITY) {
238 previewMskV.invalidate();
239 return;
240 }
241
242 previewCanvas.fill(previewCanvas.getRect(), 0x00);
243
244 int cw = previewCanvas.getWidth();
245 int tly = (previewCanvas.getHeight() - brush.getHeight()) / 2;
246 int bw = brush.getWidth();
247 int space = (int) (bw * newParams.spacing / 100.0 + 0.5);
248 if (space < 1) {
249 space = 1;
250 }
251 Rect brushRect = brush.getRect();
252 double flow = newParams.flow / 100.0;
253
254 int iMax = cw - bw;
255 for (int i = 0; i <= iMax; i += space) {
256 Blender.addTo(previewCanvas, brush, brushRect, i, tly, flow);
257 }
258 previewMskV.invalidate();
259 }
260
266 protected boolean initPreviewCanvas(MaskView view) {
267 if (previewCanvas != null) {
268 previewCanvas.dispose();
269 previewCanvas = null;
270 }
271 APDBitmap maskCanvas = view.getMaskCanvas();
272 if (maskCanvas == null) {
273 return false;
274 } else {
275 previewCanvas = new NBitmap(maskCanvas);
276 return true;
277 }
278 }
279
283 protected redrawScope updateValues() {
284 redrawScope scope = redrawScope.REDRAW_NONE;
285 int tmp;
286 tmp = newParams.opacity;
287 newParams.opacity = opacitySB.getProgress();
288 if (tmp != newParams.opacity) {
289 scope = redrawScope.REDRAW_JUST_OPACITY;
290 }
291
292 tmp = newParams.flow;
293 newParams.flow = flowSB.getProgress();
294 if (tmp != newParams.flow) {
295 scope = redrawScope.REDRAW_ALL;
296 }
297
298 tmp = newParams.spacing;
299 newParams.spacing = (int) (Math.pow(500, spacingSB.getProgress() / 500.0) + 0.5);
300 if (tmp != newParams.spacing) {
301 scope = redrawScope.REDRAW_ALL;
302 }
303 return scope;
304 }
305
309 protected void updateLabels() {
310 opacityTV.setText(Integer.toString(newParams.opacity));
311 flowTV.setText(Integer.toString(newParams.flow));
312 spacingTV.setText(Integer.toString(newParams.spacing));
313 }
314
318 protected void updateStateFromSeekbars(redrawScope forceParam) {
319 if (forceParam != null) {
320 updateValues();
321 redrawPath(forceParam);
322 updateLabels();
323 } else {
324 redrawPath(updateValues());
325 updateLabels();
326 }
327 }
328
332 protected void updateStateFromParams() {
333 updateSeekBars();
334 redrawPath(redrawScope.REDRAW_ALL);
335 updateLabels();
336 }
337
338 //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
339
344 public void setZoom(double zoom) {
345 previewMskV.setZoom(zoom);
346 }
347
352 public double getZoom() {
353 return previewMskV.getZoom();
354 }
355
360 public void setPathParams(PathParams params) {
361 newParams = new PathParams(params);
362 updateStateFromParams();
363 }
364
369 public PathParams getNewPathParams() {
370 return new PathParams(newParams);
371 }
372
377 public PathParams getOldPathParams() {
378 return new PathParams(newParams);
379 }
380
385 public void setState(PathParams params) {
386 newParams = new PathParams(params);
387 oldParams = new PathParams(params);
388 updateStateFromParams();
389 }
390
395 public void setBrush(NBitmap brush) {
396 if (this.brush != null) {
397 this.brush.dispose();
398 this.brush = null;
399 }
400 this.brush = new NBitmap(brush);
401 redrawPath(redrawScope.REDRAW_ALL);
402 }
403
408 public Paint getPreviewBackground() {
409 return previewMskV.getMaskViewBackground();
410 }
411
416 public Paint getPreviewForeground() {
417 return previewMskV.getMaskViewForeground();
418 }
419
424 public boolean isLivePreviewOn() {
425 return livePreviewOn;
426 }
427
432 public void setLivePreviewOn(boolean val) {
433 livePreviewOn = val;
434 livePreviewChB.setChecked(val);
435 }
436
441 public void setPathPickerListener(PathPickerViewEventListener l) {
442 listener = l;
443 }
444
445 @Override
446 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
447 if (fromUser) {
448 if (livePreviewOn) {
449 updateStateFromSeekbars(null);
450 } else {
451 updateValues();
452 updateLabels();
453 }
454 if (listener != null) {
455 listener.onPathPickerEvent(PathPickerEvent.EVENT_CHANGED, this);
456 }
457 }
458 }
459
460 @Override
461 public void onStartTrackingTouch(SeekBar seekBar) {
462 // Nepouzity callback
463 }
464
465 @Override
466 public void onStopTrackingTouch(SeekBar seekBar) {
467 if (!livePreviewOn) {
468 if (seekBar == opacitySB) {
469 updateStateFromSeekbars(redrawScope.REDRAW_JUST_OPACITY); // Hypoteticky může zbytečně překreslit stejnou stoupu
470 } else {
471 updateStateFromSeekbars(redrawScope.REDRAW_ALL);
472 }
473 if (listener != null) {
474 listener.onPathPickerEvent(PathPickerEvent.EVENT_CHANGED, this);
475 }
476 }
477 }
478
479 @Override
480 public void onMaskCanvasChanged(MaskView v) {
481 initPreviewCanvas(previewMskV);
482 redrawPath(redrawScope.REDRAW_ALL);
483 }
484
485 @Override
486 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
487 livePreviewOn = isChecked;
488 if (listener != null) {
489 listener.onPathPickerEvent(PathPickerEvent.EVENT_LIVE_PREVIEW_STATE_CHANGED, this);
490 }
491 }
492
493 @Override
494 public void onClick(View v) {
495 if (v == selectBtn) {
496 oldParams = new PathParams(newParams);
497 if (listener != null) {
498 listener.onPathPickerEvent(PathPickerEvent.EVENT_SELECTED, this);
499 }
500 } else if (v == cancelBtn) {
501 newParams = new PathParams(oldParams);
502 updateSeekBars();
503 updateStateFromSeekbars(redrawScope.REDRAW_ALL);
504 if (listener != null) {
505 listener.onPathPickerEvent(PathPickerEvent.EVENT_CHANGED, this);
506 }
507 if (listener != null) {
508 listener.onPathPickerEvent(PathPickerEvent.EVENT_CANCELED, this);
509 }
510 }
511 }
512
513 }