折腾了一天,今天基本把自定义扫描二维码界面实现了,主要碰到的问题是文本过长,要居中并换行,绘制图片,点击切换不同图片,打开或关闭闪关灯,结果发现在一些机型上出现空指针异常,又牵扯到硬件加速问题。。。参考了网上一下解决办法,记录一下 
先上一张效果图 
 
开发者遇到的问题以及知识点 
1.长文本换行与居中 
处理文字可以使用StaticLayout,本例中用到的构造方法是 
public StaticLayout(CharSequence source, TextPaint paint, 
int width, 
Alignment align, float spacingmult, float spacingadd, 
boolean includepad) 
1).需要填写的字符串

2) .画笔

3).layout的宽度,字符串超出宽度时,自动换行。

4).对齐方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。使用ALIGN_CENTER可居中

5).相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。

6).相对行间距,0表示0个像素。

实际行间距等于这两者的和。

7).没查到这个参数表示的意思 
默认是从画布的(0,0)坐标开始,如果需要在指定位置绘画,在draw之前移Canvas的起始坐标canvas.translate(x,y); 
在canvas.translate(x,y)操作中,可以使用canvas的一些方法进行处理 
canvas.save();//锁画布(为了保存之前的画布状态) 
canvas.translate(x, y);//把当前画布的原点移到(x,y),后面的操作都以(x,y)作为参照点,默认原点为(0,0) 
canvas.restore();//把当前画布返回(调整)到上一个save()状态之前 
如果不进行处理也可以,再重新绘制其他View的时候,坐标就需要相应的调整了 
2.第二个问题是在界面上绘制图片 
绘制图片有两种方法 
1)、基本 
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 
//Bitmap:图片,left:图片左边位置,top:图片顶部的位置 
2)、对图片裁剪以及限定显示区域 
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint); 
bitmap的默认坐标是0,0.矩形src为我们所剪辑的图片的包围框,即你所剪的图片,为空,就是整张图片。 
矩形dst容纳裁剪的图片,然后根据此矩形的位置设置图片的位置。此参数不能为空。 
当你剪的图片大小大于dst时,多余的部分将不会显示。 
也就是说src是裁减区,对原始图的裁减区域,而dst是代表图片显示位置. 
3.drawBitmap()方法空指针异常 
在小米手机上测试,竟然提示 
java.lang.NullPointerException 
at android.view.GLES20RecordingCanvas.drawBitmap 
在网上查了一下,这个是和硬件加速有关系,在AndroidManifest.xml application下设定: 
android:hardwareAccelerated=”false” 
测试没发现问题 
下边记录一下在网上找到的关于硬件加速的资料 
硬件加速可以在一下四个级别开启或关闭: 
Application 
Activity 
Window 
View

Application级别 
往您的应用程序AndroidManifest.xml文件为application标签添加application android:hardwareAccelerated=”true”属性即可为整个应用程序开启硬件加速 
Activity级别 
控制每个activity是否开启硬件加速,只需在activity元素中添加android:hardwareAccelerated属性 
Window级别 
使用如下代码开启某个window的硬件加速: 
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 
View级别 
用以下的代码关闭单个view的硬件加速: 
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 
在这四个层次中,应用和Activity是可以选择的,Window只能打开,View只能关闭。

检查是否开启硬件加速 
/ 方法一 
// 返回true,如果myView挂在一个开启了硬件加速的Window之下 
myView.isHardwareAccelerated(); 
// 方法二 
// 返回true,如果canvas在绘制的时候启用了硬件加速 
// 尽量采用此方法来判断是否开启了硬件加速 
canvas.isHardwareAccelerated(); 
4.zxing二维码扫描开启或关闭闪光灯 
不要忘记在onDestroy方法里关闭闪光灯

  1. public void enableFlash(){
  2. FlashlightManager.enableFlashlight();
  3. try {
  4. if (context.getPackageManager().hasSystemFeature(
  5. PackageManager.FEATURE_CAMERA_FLASH)) {
  6. Parameters p = camera.getParameters();
  7. p.setFlashMode(Parameters.FLASH_MODE_TORCH);
  8. camera.setParameters(p);
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. public void disableFlash(){
  15. FlashlightManager.enableFlashlight();
  16. try {
  17. if (context.getPackageManager().hasSystemFeature(
  18. PackageManager.FEATURE_CAMERA_FLASH)) {
  19. Parameters p = camera.getParameters();
  20. p.setFlashMode(Parameters.FLASH_MODE_OFF);
  21. camera.setParameters(p);
  22. }
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26. }

5.点击canvas绘制的图片 
思路:在onTouchEvent中获取手指点击坐标,判断坐标是否在图片范围之内,如果在,进行点击操作

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. // 获取点击屏幕时的点的坐标
  4. float x = event.getX();
  5. float y = event.getY();
  6. if((x>drawableLeft&&x<drawableRight)
  7. ||(y>drawableTop&&y<drawableBottom)){
  8. if(isOpen){
  9. CameraManager.get().enableFlash();
  10. }else{
  11. CameraManager.get().disableFlash();
  12. }
  13. drawBitMap(isOpen);
  14. isOpen=!isOpen;
  15. }
  16. return super.onTouchEvent(event);
  17. }

上完整代码

  1. public final class ViewfinderView extends View {
  2. private static final String TAG = "log";
  3. /**
  4. * 刷新界面的时间
  5. */
  6. private static final long ANIMATION_DELAY = 10L;
  7. private static final int OPAQUE = 0xFF;
  8. /**
  9. * 四个绿色边角对应的长度
  10. */
  11. private int ScreenRate;
  12. /**
  13. * 四个绿色边角对应的宽度
  14. */
  15. private static final int CORNER_WIDTH = 10;
  16. /**
  17. * 扫描框中的中间线的宽度
  18. */
  19. private static final int MIDDLE_LINE_WIDTH = 6;
  20. /**
  21. * 扫描框中的中间线的与扫描框左右的间隙
  22. */
  23. private static final int MIDDLE_LINE_PADDING = 5;
  24. /**
  25. * 中间那条线每次刷新移动的距离
  26. */
  27. private static final int SPEEN_DISTANCE = 5;
  28. /**
  29. * 手机的屏幕密度
  30. */
  31. private static float density;
  32. /**
  33. * 字体大小
  34. */
  35. private static final int TEXT_SIZE = 16;
  36. /**
  37. * 字体距离扫描框下边的距离
  38. */
  39. private static final int TEXT_PADDING_BOTTOM = 50;
  40. /**
  41. * 字体距离扫描框上边的距离
  42. */
  43. private static final int TEXT_PADDING_TOP = 50;
  44. /**
  45. * 画笔对象的引用
  46. */
  47. private Paint paint;
  48. /**
  49. * 中间滑动线的最顶端位置
  50. */
  51. private int slideTop;
  52. /**
  53. * 中间滑动线的最底端位置
  54. */
  55. private int slideBottom;
  56. private Bitmap resultBitmap;
  57. private final int maskColor;
  58. private final int resultColor;
  59. private final int resultPointColor;
  60. private Collection<ResultPoint> possibleResultPoints;
  61. private Collection<ResultPoint> lastPossibleResultPoints;
  62. boolean isFirst;
  63. StaticLayout layoutTop;//绘制扫描框上边字体
  64. StaticLayout layoutBottom;//绘制扫描框下字体
  65. TextPaint textPaint;//绘制字体
  66. //绘制显示的开灯关灯图片
  67. private Resources mResources;
  68. private Paint mBitPaint;
  69. private Bitmap mBitmap;
  70. private Rect mSrcRect, mDestRect;
  71. private Rect frame;
  72. private Canvas drawBitmapCanvas;//绘制图片
  73. //绘制图片的上下左右坐标
  74. private int drawableLeft;
  75. private int drawableTop;
  76. private int drawableBottom;
  77. private int drawableRight;
  78. //是否点击开灯图片
  79. private boolean isOpen=false;
  80. //是否绘画图片
  81. private boolean isDraw = false;
  82. public ViewfinderView(Context context, AttributeSet attrs) {
  83. super(context, attrs);
  84. density = context.getResources().getDisplayMetrics().density;
  85. //将像素转换成dp
  86. ScreenRate = (int) (20 * density);
  87. paint = new Paint();
  88. Resources resources = getResources();
  89. maskColor = resources.getColor(R.color.viewfinder_mask);
  90. resultColor = resources.getColor(R.color.result_view);
  91. resultPointColor = resources.getColor(R.color.possible_result_points);
  92. possibleResultPoints = new HashSet<ResultPoint>(5);
  93. }
  94. @Override
  95. public void onDraw(Canvas canvas) {
  96. //中间的扫描框
  97. frame = CameraManager.get().getFramingRect();
  98. if (frame == null) {
  99. return;
  100. }
  101. drawBitmapCanvas = canvas;
  102. //初始化中间线滑动的最上边和最下边
  103. if (!isFirst) {
  104. isFirst = true;
  105. slideTop = frame.top;
  106. slideBottom = frame.bottom;
  107. }
  108. //获取屏幕的宽和高
  109. int width = canvas.getWidth();
  110. int height = canvas.getHeight();
  111. paint.setColor(resultBitmap != null ? resultColor : maskColor);
  112. //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
  113. //扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
  114. canvas.drawRect(0, 0, width, frame.top, paint);
  115. canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
  116. canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
  117. paint);
  118. canvas.drawRect(0, frame.bottom + 1, width, height, paint);
  119. if (resultBitmap != null) {
  120. // Draw the opaque result bitmap over the scanning rectangle
  121. paint.setAlpha(OPAQUE);
  122. canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
  123. } else {
  124. //画扫描框边上的角,总共8个部分
  125. paint.setColor(Color.GREEN);
  126. canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
  127. frame.top + CORNER_WIDTH, paint);
  128. canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top
  129. + ScreenRate, paint);
  130. canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
  131. frame.top + CORNER_WIDTH, paint);
  132. canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top
  133. + ScreenRate, paint);
  134. canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
  135. + ScreenRate, frame.bottom, paint);
  136. canvas.drawRect(frame.left, frame.bottom - ScreenRate,
  137. frame.left + CORNER_WIDTH, frame.bottom, paint);
  138. canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,
  139. frame.right, frame.bottom, paint);
  140. canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,
  141. frame.right, frame.bottom, paint);
  142. //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
  143. slideTop += SPEEN_DISTANCE;
  144. if (slideTop >= frame.bottom) {
  145. slideTop = frame.top;
  146. }
  147. canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH / 2, frame.right - MIDDLE_LINE_PADDING, slideTop + MIDDLE_LINE_WIDTH / 2, paint);
  148. /* Rect lineRect = new Rect();
  149. lineRect.left = frame.left;
  150. lineRect.right = frame.right;
  151. lineRect.top = slideTop;
  152. lineRect.bottom = slideTop + 18;
  153. canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.qrline))).getBitmap(), null, lineRect, paint);
  154. */
  155. //画扫描框上面的字,并且居中
  156. if (textPaint == null) {
  157. textPaint = new TextPaint();
  158. }
  159. textPaint.setColor(Color.WHITE);
  160. textPaint.setTextSize(TEXT_SIZE * density);
  161. //textPaint.setAlpha(0x40);
  162. textPaint.setTypeface(Typeface.DEFAULT_BOLD);
  163. String textTop = "XXXXXXXXXXXXXXXXXXXXX";
  164. // float textWidthTop = paint.measureText(textTop);
  165. if (layoutTop == null) {
  166. layoutTop = new StaticLayout(textTop, textPaint, frame.right - frame.left, Layout.Alignment.ALIGN_CENTER, 1.0F, 0.0F, true);
  167. }
  168. canvas.save();//锁画布(为了保存之前的画布状态)
  169. //开始绘制的位置
  170. //把当前画布的原点移到(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density)),
  171. // 后面的操作都以(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density))作为参照点,默认原点为(0,0)
  172. canvas.translate(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density));
  173. layoutTop.draw(canvas);
  174. canvas.restore();//把当前画布返回(调整)到上一个save()状态之前
  175. //画扫描框下面的字,并且居中
  176. String textBottom = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
  177. // float textWidthTop = paint.measureText(textTop);
  178. if (layoutBottom == null) {
  179. layoutBottom = new StaticLayout(textBottom, textPaint, frame.right - frame.left, Layout.Alignment.ALIGN_CENTER, 1.0F, 0.0F, true);
  180. }
  181. canvas.save();//锁画布(为了保存之前的画布状态)
  182. //开始绘制的位置
  183. canvas.translate(frame.left, (float) (frame.bottom) + 18);
  184. layoutBottom.draw(canvas);
  185. canvas.restore();
  186. Collection<ResultPoint> currentPossible = possibleResultPoints;
  187. Collection<ResultPoint> currentLast = lastPossibleResultPoints;
  188. if (currentPossible.isEmpty()) {
  189. lastPossibleResultPoints = null;
  190. } else {
  191. possibleResultPoints = new HashSet<ResultPoint>(5);
  192. lastPossibleResultPoints = currentPossible;
  193. paint.setAlpha(OPAQUE);
  194. paint.setColor(resultPointColor);
  195. for (ResultPoint point : currentPossible) {
  196. canvas.drawCircle(frame.left + point.getX(), frame.top
  197. + point.getY(), 6.0f, paint);
  198. }
  199. }
  200. if (currentLast != null) {
  201. paint.setAlpha(OPAQUE / 2);
  202. paint.setColor(resultPointColor);
  203. for (ResultPoint point : currentLast) {
  204. canvas.drawCircle(frame.left + point.getX(), frame.top
  205. + point.getY(), 3.0f, paint);
  206. }
  207. }
  208. //只刷新扫描框的内容,其他地方不刷新
  209. postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
  210. frame.right, frame.bottom);
  211. drawBitMap(isOpen);
  212. }
  213. }
  214. @Override
  215. public boolean onTouchEvent(MotionEvent event) {
  216. // 获取点击屏幕时的点的坐标
  217. float x = event.getX();
  218. float y = event.getY();
  219. if((x>drawableLeft&&x<drawableRight)
  220. ||(y>drawableTop&&y<drawableBottom)){
  221. if(!isOpen){
  222. CameraManager.get().enableFlash();
  223. }else{
  224. CameraManager.get().disableFlash();
  225. }
  226. drawBitMap(isOpen);
  227. isOpen=!isOpen;
  228. }
  229. return super.onTouchEvent(event);
  230. }
  231. //绘制图片,并且根据点击切换不同的图标,切换是否开灯图标
  232. public void drawBitMap(boolean isOpen){
  233. //绘制显示的图片
  234. mResources = getResources();
  235. if(mBitPaint==null){
  236. mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  237. }
  238. mBitPaint.setFilterBitmap(true);
  239. mBitPaint.setDither(true);
  240. try {
  241. if(isOpen){
  242. mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.defaultalarm);;
  243. }else{
  244. mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.alarm);
  245. }
  246. }catch (Exception e){
  247. e.printStackTrace();
  248. }
  249. //要绘制的bitmap 区域
  250. if(mSrcRect == null){
  251. mSrcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
  252. }
  253. //要将bitmap 绘制在屏幕的什么地方
  254. // 计算左边位置
  255. int left =frame.left+(frame.right-frame.left)/2- mBitmap.getWidth() / 2;
  256. // 计算上边位置
  257. int top = frame.bottom + (int)(TEXT_PADDING_TOP * density);
  258. drawableLeft = left;
  259. drawableRight = left+mBitmap.getWidth();
  260. drawableTop = top;
  261. drawableBottom = top+mBitmap.getHeight();
  262. if(mDestRect == null){
  263. mDestRect = new Rect(drawableLeft,drawableTop, drawableRight,drawableBottom);
  264. }
  265. invalidate(drawableLeft,drawableTop, drawableRight,drawableBottom);
  266. drawBitmapCanvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitPaint);
  267. isDraw = true;
  268. }
  269. public void drawViewfinder() {
  270. resultBitmap = null;
  271. invalidate();
  272. }
  273. /**
  274. * Draw a bitmap with the result points highlighted instead of the live
  275. * scanning display.
  276. *
  277. * @param barcode An image of the decoded barcode.
  278. */
  279. public void drawResultBitmap(Bitmap barcode) {
  280. resultBitmap = barcode;
  281. invalidate();
  282. }
  283. public void addPossibleResultPoint(ResultPoint point) {
  284. possibleResultPoints.add(point);
  285. }
  286. }

android zxing自定义界面,点击按钮开关闪光灯的更多相关文章

  1. Android中Listview点击item不变颜色以及设置listselector 无效

    Android中Listview点击item不变颜色以及设置listselector 无效 这是同一个问题,Listview中点击item是会变颜色的,因为listview设置了默认的listsele ...

  2. android通知栏Notification点击,取消,清除响应事件

    主要是检测android通知栏的三种状态的响应事件 这次在实现推送需求的时候,要用到android通知栏Notification点击后进入消息页面,因为要实现一个保存推送用户名字的功能,我在点击后处理 ...

  3. android中ListView点击和里边按钮点击不能同时生效问题解决

    今天遇到一个问题:android中ListView点击和里边button点击不能同时生效问题解决. 原因是: listView 在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得 ...

  4. 基于Android的模拟点击探索

    前言 压力测试中,一般会用到自动化测试.准备写一个APP,可以记录屏幕上的点击事件,然后通过shell命令来模拟自动执行.shell指令,比较容易实现.那么,关键的一步是获取点击的坐标.对于Andro ...

  5. python模拟android屏幕高频点击工具

    一.环境 windows 10  + python3.6 二.需求 1.模拟android设备高频点击事件: 2.模拟规定次数的点击事件或模拟规定时间内的点击事件: 三.code 1.模拟规定时间内的 ...

  6. Android控件点击事件

    1. 介绍 本文介绍了Android控件的点击事件 Android控件点击(onClick)事件可以用如下三种方式来实现 2. 实现onClick方法 在layout的xml中指定onClick方法, ...

  7. android: Android水波纹点击效果

    Android API 21及以上新增了ripple标签用来实现水波纹的效果.我们可以通过设置ripple背景来实现一些View点击效果. 该水波纹效果有两种:一种是有界的(点击后类似于一个矩形向四周 ...

  8. Android编程实现点击链接打开APP功能示例

    本文实例讲述了Android编程实现点击链接打开APP功能.分享给大家供大家参考,具体如下: 在Android中点击链接打开APP是一个很常见的需求.例如,电商为用户发送优惠券之后经常会下发一条短信: ...

  9. Android Zxing 加入闪光灯功能

    近期做了关于二维码解析的模块 选用的是google的开源projectZxing 在Zxing 加入闪光灯功能 例如以下: 在 com.xxx.xxx.Zxing.camera 包下的CameraMa ...

随机推荐

  1. 《疯狂VirtualBox实战讲学录》

    <疯狂VirtualBox实战讲学录:小耗子之VirtualBox修炼全程重现>是市面上第一部同时也是唯一一部完整介绍VirtualBox的“中文版全程实战手册”!本书完整记录了Virtu ...

  2. [Hadoop源码解读](三)MapReduce篇之Job类

    下面,我们只涉及MapReduce 1,而不涉及YARN. 当我们在写MapReduce程序的时候,通常,在main函数里,我们会像下面这样做.建立一个Job对象,设置它的JobName,然后配置输入 ...

  3. 关于I/O的那点事

    转载请著名作者和地址http://www.cnblogs.com/scotth/p/3645489.html 1.关于 IO (fopen出现的错误 errorCode 183) 相关知识点: < ...

  4. [Jacky] 解决Ext.Net GridPanel 选择的行数据刷新后不能获取最新值

    选择GridPanel中一行数据,当变更数据时并重新刷新之后不能获取最新值,需通过如下方式获取: var internalId = gridPanel.getSelectionModel().getL ...

  5. Robotium 系列(1)

    之前也写过一些blog,可是没什么坚持.这个系列算是对之前工作的一个总结和提高:也与大家共同进步! 本文主要介绍的内容是: 1. 软件测试的基本概念 2. 自动化测试的概念 3. 手动测试和自动化测试 ...

  6. 【原】1.1RDD源码解读(一)

    1.RDD(Resilient Distributed DataSet)是Spark生态系统中最基本的抽象,代表不可变的.可并行操作的分区元素集合.RDD这个类有RDD系列所有基本的操作,比如map. ...

  7. Linear Regreesion

       3.似然函数:我是这么理解的,比如说我们知道某个X的概率分布密度函数,但是这个概率分布有未知的参数,但是我想得到这个未知的参数θ,然后我们就通过很多个已知的变量,把这些概率分布密度函数乘起来,这 ...

  8. 【解决】exited with a non-zero exit code 1

      安装好集群后运行hadoop-mapreduce-examples.jar 报错,比对cdh配置后,发现少了yarn.application.classpath,conf目录一定要配置,否则也会出 ...

  9. HW2.24

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  10. HDOJ-ACM1013(JAVA)

    这道题也很简单~主要是将输入转为字符串(存储大数,操作字符数组) 题意: 24 : 2 + 4 = 6  输出6 39 : 3 + 9 = 12 , 1 + 2 = 3 输出3 999: 9 + 9 ...