ViewfinderView.java 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * Copyright (C) 2008 ZXing authors
  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. package com.journeyapps.barcodescanner;
  17. import android.content.Context;
  18. import android.content.res.Resources;
  19. import android.content.res.TypedArray;
  20. import android.graphics.Bitmap;
  21. import android.graphics.Canvas;
  22. import android.graphics.Paint;
  23. import android.graphics.Rect;
  24. import android.util.AttributeSet;
  25. import android.view.View;
  26. import com.google.zxing.ResultPoint;
  27. import com.journeyapps.barcodescanner.R;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. /**
  31. * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
  32. * transparency outside it, as well as the laser scanner animation and result points.
  33. *
  34. * @author dswitkin@google.com (Daniel Switkin)
  35. */
  36. public class ViewfinderView extends View {
  37. protected static final String TAG = ViewfinderView.class.getSimpleName();
  38. protected static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
  39. protected static final long ANIMATION_DELAY = 80L;
  40. protected static final int CURRENT_POINT_OPACITY = 0xA0;
  41. protected static final int MAX_RESULT_POINTS = 20;
  42. protected static final int POINT_SIZE = 6;
  43. protected final Paint paint;
  44. protected Bitmap resultBitmap;
  45. protected int maskColor;
  46. protected final int resultColor;
  47. protected final int laserColor;
  48. protected final int resultPointColor;
  49. protected boolean laserVisibility;
  50. protected int scannerAlpha;
  51. protected List<ResultPoint> possibleResultPoints;
  52. protected List<ResultPoint> lastPossibleResultPoints;
  53. protected CameraPreview cameraPreview;
  54. // Cache the framingRect and previewSize, so that we can still draw it after the preview
  55. // stopped.
  56. protected Rect framingRect;
  57. protected Size previewSize;
  58. // This constructor is used when the class is built from an XML resource.
  59. public ViewfinderView(Context context, AttributeSet attrs) {
  60. super(context, attrs);
  61. // Initialize these once for performance rather than calling them every time in onDraw().
  62. paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  63. Resources resources = getResources();
  64. // Get set attributes on view
  65. TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.zxing_finder);
  66. this.maskColor = attributes.getColor(R.styleable.zxing_finder_zxing_viewfinder_mask,
  67. resources.getColor(R.color.zxing_viewfinder_mask));
  68. this.resultColor = attributes.getColor(R.styleable.zxing_finder_zxing_result_view,
  69. resources.getColor(R.color.zxing_result_view));
  70. this.laserColor = attributes.getColor(R.styleable.zxing_finder_zxing_viewfinder_laser,
  71. resources.getColor(R.color.zxing_viewfinder_laser));
  72. this.resultPointColor = attributes.getColor(R.styleable.zxing_finder_zxing_possible_result_points,
  73. resources.getColor(R.color.zxing_possible_result_points));
  74. this.laserVisibility = attributes.getBoolean(R.styleable.zxing_finder_zxing_viewfinder_laser_visibility,
  75. true);
  76. attributes.recycle();
  77. scannerAlpha = 0;
  78. possibleResultPoints = new ArrayList<>(MAX_RESULT_POINTS);
  79. lastPossibleResultPoints = new ArrayList<>(MAX_RESULT_POINTS);
  80. }
  81. public void setCameraPreview(CameraPreview view) {
  82. this.cameraPreview = view;
  83. view.addStateListener(new CameraPreview.StateListener() {
  84. @Override
  85. public void previewSized() {
  86. refreshSizes();
  87. invalidate();
  88. }
  89. @Override
  90. public void previewStarted() {
  91. }
  92. @Override
  93. public void previewStopped() {
  94. }
  95. @Override
  96. public void cameraError(Exception error) {
  97. }
  98. @Override
  99. public void cameraClosed() {
  100. }
  101. });
  102. }
  103. protected void refreshSizes() {
  104. if (cameraPreview == null) {
  105. return;
  106. }
  107. Rect framingRect = cameraPreview.getFramingRect();
  108. Size previewSize = cameraPreview.getPreviewSize();
  109. if (framingRect != null && previewSize != null) {
  110. this.framingRect = framingRect;
  111. this.previewSize = previewSize;
  112. }
  113. }
  114. @Override
  115. public void onDraw(Canvas canvas) {
  116. refreshSizes();
  117. if (framingRect == null || previewSize == null) {
  118. return;
  119. }
  120. final Rect frame = framingRect;
  121. final Size previewSize = this.previewSize;
  122. final int width = getWidth();
  123. final int height = getHeight();
  124. // Draw the exterior (i.e. outside the framing rect) darkened
  125. paint.setColor(resultBitmap != null ? resultColor : maskColor);
  126. canvas.drawRect(0, 0, width, frame.top, paint);
  127. canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
  128. canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
  129. canvas.drawRect(0, frame.bottom + 1, width, height, paint);
  130. if (resultBitmap != null) {
  131. // Draw the opaque result bitmap over the scanning rectangle
  132. paint.setAlpha(CURRENT_POINT_OPACITY);
  133. canvas.drawBitmap(resultBitmap, null, frame, paint);
  134. } else {
  135. // If wanted, draw a red "laser scanner" line through the middle to show decoding is active
  136. if (laserVisibility) {
  137. paint.setColor(laserColor);
  138. paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
  139. scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
  140. final int middle = frame.height() / 2 + frame.top;
  141. canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
  142. }
  143. final float scaleX = this.getWidth() / (float) previewSize.width;
  144. final float scaleY = this.getHeight() / (float) previewSize.height;
  145. // draw the last possible result points
  146. if (!lastPossibleResultPoints.isEmpty()) {
  147. paint.setAlpha(CURRENT_POINT_OPACITY / 2);
  148. paint.setColor(resultPointColor);
  149. float radius = POINT_SIZE / 2.0f;
  150. for (final ResultPoint point : lastPossibleResultPoints) {
  151. canvas.drawCircle(
  152. (int) (point.getX() * scaleX),
  153. (int) (point.getY() * scaleY),
  154. radius, paint
  155. );
  156. }
  157. lastPossibleResultPoints.clear();
  158. }
  159. // draw current possible result points
  160. if (!possibleResultPoints.isEmpty()) {
  161. paint.setAlpha(CURRENT_POINT_OPACITY);
  162. paint.setColor(resultPointColor);
  163. for (final ResultPoint point : possibleResultPoints) {
  164. canvas.drawCircle(
  165. (int) (point.getX() * scaleX),
  166. (int) (point.getY() * scaleY),
  167. POINT_SIZE, paint
  168. );
  169. }
  170. // swap and clear buffers
  171. final List<ResultPoint> temp = possibleResultPoints;
  172. possibleResultPoints = lastPossibleResultPoints;
  173. lastPossibleResultPoints = temp;
  174. possibleResultPoints.clear();
  175. }
  176. // Request another update at the animation interval, but only repaint the laser line,
  177. // not the entire viewfinder mask.
  178. postInvalidateDelayed(ANIMATION_DELAY,
  179. frame.left - POINT_SIZE,
  180. frame.top - POINT_SIZE,
  181. frame.right + POINT_SIZE,
  182. frame.bottom + POINT_SIZE);
  183. }
  184. }
  185. public void drawViewfinder() {
  186. Bitmap resultBitmap = this.resultBitmap;
  187. this.resultBitmap = null;
  188. if (resultBitmap != null) {
  189. resultBitmap.recycle();
  190. }
  191. invalidate();
  192. }
  193. /**
  194. * Draw a bitmap with the result points highlighted instead of the live scanning display.
  195. *
  196. * @param result An image of the result.
  197. */
  198. public void drawResultBitmap(Bitmap result) {
  199. resultBitmap = result;
  200. invalidate();
  201. }
  202. /**
  203. * Only call from the UI thread.
  204. *
  205. * @param point a point to draw, relative to the preview frame
  206. */
  207. public void addPossibleResultPoint(ResultPoint point) {
  208. if (possibleResultPoints.size() < MAX_RESULT_POINTS)
  209. possibleResultPoints.add(point);
  210. }
  211. public void setMaskColor(int maskColor) {
  212. this.maskColor = maskColor;
  213. }
  214. public void setLaserVisibility(boolean visible) {
  215. this.laserVisibility = visible;
  216. }
  217. }