折腾了一天,今天基本把自定义扫描二维码界面实现了,主要碰到的问题是文本过长,要居中并换行,绘制图片,点击切换不同图片,打开或关闭闪关灯,结果发现在一些机型上出现空指针异常,又牵扯到硬件加速问题。。。参考了网上一下解决办法,记录一下 
先上一张效果图 
 
开发者遇到的问题以及知识点 
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方法里关闭闪光灯

public void enableFlash(){
FlashlightManager.enableFlashlight();
try {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_FLASH)) {
Parameters p = camera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(p);
}
} catch (Exception e) {
e.printStackTrace();
}
} public void disableFlash(){
FlashlightManager.enableFlashlight(); try {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_FLASH)) {
Parameters p = camera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(p);
}
} catch (Exception e) {
e.printStackTrace();
}
}

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

@Override
public boolean onTouchEvent(MotionEvent event) {
// 获取点击屏幕时的点的坐标
float x = event.getX();
float y = event.getY();
if((x>drawableLeft&&x<drawableRight)
||(y>drawableTop&&y<drawableBottom)){
if(isOpen){
CameraManager.get().enableFlash();
}else{
CameraManager.get().disableFlash();
}
drawBitMap(isOpen);
isOpen=!isOpen;
}
return super.onTouchEvent(event);
}

上完整代码

public final class ViewfinderView extends View {
private static final String TAG = "log";
/**
* 刷新界面的时间
*/
private static final long ANIMATION_DELAY = 10L;
private static final int OPAQUE = 0xFF; /**
* 四个绿色边角对应的长度
*/
private int ScreenRate; /**
* 四个绿色边角对应的宽度
*/
private static final int CORNER_WIDTH = 10;
/**
* 扫描框中的中间线的宽度
*/
private static final int MIDDLE_LINE_WIDTH = 6; /**
* 扫描框中的中间线的与扫描框左右的间隙
*/
private static final int MIDDLE_LINE_PADDING = 5; /**
* 中间那条线每次刷新移动的距离
*/
private static final int SPEEN_DISTANCE = 5; /**
* 手机的屏幕密度
*/
private static float density;
/**
* 字体大小
*/
private static final int TEXT_SIZE = 16;
/**
* 字体距离扫描框下边的距离
*/
private static final int TEXT_PADDING_BOTTOM = 50; /**
* 字体距离扫描框上边的距离
*/
private static final int TEXT_PADDING_TOP = 50; /**
* 画笔对象的引用
*/
private Paint paint; /**
* 中间滑动线的最顶端位置
*/
private int slideTop; /**
* 中间滑动线的最底端位置
*/
private int slideBottom; private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor; private final int resultPointColor;
private Collection<ResultPoint> possibleResultPoints;
private Collection<ResultPoint> lastPossibleResultPoints; boolean isFirst;
StaticLayout layoutTop;//绘制扫描框上边字体
StaticLayout layoutBottom;//绘制扫描框下字体
TextPaint textPaint;//绘制字体 //绘制显示的开灯关灯图片
private Resources mResources;
private Paint mBitPaint;
private Bitmap mBitmap;
private Rect mSrcRect, mDestRect;
private Rect frame;
private Canvas drawBitmapCanvas;//绘制图片
//绘制图片的上下左右坐标
private int drawableLeft;
private int drawableTop;
private int drawableBottom;
private int drawableRight;
//是否点击开灯图片
private boolean isOpen=false;
//是否绘画图片
private boolean isDraw = false; public ViewfinderView(Context context, AttributeSet attrs) {
super(context, attrs); density = context.getResources().getDisplayMetrics().density;
//将像素转换成dp
ScreenRate = (int) (20 * density); paint = new Paint();
Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask);
resultColor = resources.getColor(R.color.result_view); resultPointColor = resources.getColor(R.color.possible_result_points);
possibleResultPoints = new HashSet<ResultPoint>(5);
} @Override
public void onDraw(Canvas canvas) {
//中间的扫描框
frame = CameraManager.get().getFramingRect();
if (frame == null) {
return;
}
drawBitmapCanvas = canvas;
//初始化中间线滑动的最上边和最下边
if (!isFirst) {
isFirst = true;
slideTop = frame.top;
slideBottom = frame.bottom;
} //获取屏幕的宽和高
int width = canvas.getWidth();
int height = canvas.getHeight(); paint.setColor(resultBitmap != null ? resultColor : maskColor); //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
//扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint); if (resultBitmap != null) {
// Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(OPAQUE);
canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
} else { //画扫描框边上的角,总共8个部分
paint.setColor(Color.GREEN);
canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
frame.top + CORNER_WIDTH, paint);
canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top
+ ScreenRate, paint);
canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
frame.top + CORNER_WIDTH, paint);
canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top
+ ScreenRate, paint);
canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
+ ScreenRate, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - ScreenRate,
frame.left + CORNER_WIDTH, frame.bottom, paint);
canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,
frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,
frame.right, frame.bottom, paint); //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
slideTop += SPEEN_DISTANCE;
if (slideTop >= frame.bottom) {
slideTop = frame.top;
}
canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH / 2, frame.right - MIDDLE_LINE_PADDING, slideTop + MIDDLE_LINE_WIDTH / 2, paint);
/* Rect lineRect = new Rect();
lineRect.left = frame.left;
lineRect.right = frame.right;
lineRect.top = slideTop;
lineRect.bottom = slideTop + 18;
canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.qrline))).getBitmap(), null, lineRect, paint);
*/
//画扫描框上面的字,并且居中 if (textPaint == null) {
textPaint = new TextPaint();
}
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(TEXT_SIZE * density);
//textPaint.setAlpha(0x40);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
String textTop = "XXXXXXXXXXXXXXXXXXXXX";
// float textWidthTop = paint.measureText(textTop);
if (layoutTop == null) {
layoutTop = new StaticLayout(textTop, textPaint, frame.right - frame.left, Layout.Alignment.ALIGN_CENTER, 1.0F, 0.0F, true);
}
canvas.save();//锁画布(为了保存之前的画布状态)
//开始绘制的位置
//把当前画布的原点移到(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density)),
// 后面的操作都以(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density))作为参照点,默认原点为(0,0)
canvas.translate(frame.left, (float) (frame.top - (float) TEXT_PADDING_TOP * density));
layoutTop.draw(canvas);
canvas.restore();//把当前画布返回(调整)到上一个save()状态之前
//画扫描框下面的字,并且居中
String textBottom = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
// float textWidthTop = paint.measureText(textTop);
if (layoutBottom == null) {
layoutBottom = new StaticLayout(textBottom, textPaint, frame.right - frame.left, Layout.Alignment.ALIGN_CENTER, 1.0F, 0.0F, true);
}
canvas.save();//锁画布(为了保存之前的画布状态)
//开始绘制的位置
canvas.translate(frame.left, (float) (frame.bottom) + 18);
layoutBottom.draw(canvas);
canvas.restore(); Collection<ResultPoint> currentPossible = possibleResultPoints;
Collection<ResultPoint> currentLast = lastPossibleResultPoints;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new HashSet<ResultPoint>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(OPAQUE);
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frame.left + point.getX(), frame.top
+ point.getY(), 6.0f, paint);
}
}
if (currentLast != null) {
paint.setAlpha(OPAQUE / 2);
paint.setColor(resultPointColor);
for (ResultPoint point : currentLast) {
canvas.drawCircle(frame.left + point.getX(), frame.top
+ point.getY(), 3.0f, paint);
}
} //只刷新扫描框的内容,其他地方不刷新
postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
frame.right, frame.bottom); drawBitMap(isOpen); }
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 获取点击屏幕时的点的坐标
float x = event.getX();
float y = event.getY();
if((x>drawableLeft&&x<drawableRight)
||(y>drawableTop&&y<drawableBottom)){
if(!isOpen){
CameraManager.get().enableFlash();
}else{
CameraManager.get().disableFlash();
}
drawBitMap(isOpen);
isOpen=!isOpen;
}
return super.onTouchEvent(event);
} //绘制图片,并且根据点击切换不同的图标,切换是否开灯图标
public void drawBitMap(boolean isOpen){
//绘制显示的图片
mResources = getResources();
if(mBitPaint==null){
mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
mBitPaint.setFilterBitmap(true);
mBitPaint.setDither(true);
try {
if(isOpen){
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.defaultalarm);;
}else{
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.alarm);
} }catch (Exception e){
e.printStackTrace();
}
//要绘制的bitmap 区域 if(mSrcRect == null){
mSrcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
//要将bitmap 绘制在屏幕的什么地方
// 计算左边位置
int left =frame.left+(frame.right-frame.left)/2- mBitmap.getWidth() / 2;
// 计算上边位置
int top = frame.bottom + (int)(TEXT_PADDING_TOP * density); drawableLeft = left;
drawableRight = left+mBitmap.getWidth();
drawableTop = top;
drawableBottom = top+mBitmap.getHeight(); if(mDestRect == null){
mDestRect = new Rect(drawableLeft,drawableTop, drawableRight,drawableBottom); } invalidate(drawableLeft,drawableTop, drawableRight,drawableBottom); drawBitmapCanvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitPaint);
isDraw = true;
} public void drawViewfinder() {
resultBitmap = null;
invalidate();
} /**
* Draw a bitmap with the result points highlighted instead of the live
* scanning display.
*
* @param barcode An image of the decoded barcode.
*/
public void drawResultBitmap(Bitmap barcode) {
resultBitmap = barcode;
invalidate();
} public void addPossibleResultPoint(ResultPoint point) {
possibleResultPoints.add(point);
}
}

最新文章

  1. ACM: FZU 2112 Tickets - 欧拉回路 - 并查集
  2. ReactNative真机运行指南
  3. Android Studio debug使用release的签名
  4. 简易的可拖动的桌面悬浮窗效果Demo
  5. 面向对象编程(十二)——final关键字
  6. A+B问题通解_Pascal_C++_Java
  7. My First Django Project (2)
  8. 安装安装.net framework过程中出现的问题
  9. the apply of backbone
  10. PHP 解决未定义变量报错
  11. MySQL最常用日期时间函数
  12. 使用jedis实现Redis消息队列(MQ)的发布(publish)和消息监听(subscribe)
  13. phpcms列表页替换
  14. Xenserver 如何设置VM boot options
  15. latex对齐问题
  16. Windows:查看IP地址,IP地址对应的机器名,占用的端口,以及占用该端口的应用程
  17. javaScript设计模式(一)观察者模式
  18. android------adb命令 pull或push手机和电脑文件交互
  19. PCL利用RANSAC自行拟合分割平面
  20. FFT(快速傅里叶变换)算法详解

热门文章

  1. keytool 错误: java.io.FileNotFoundException: 拒绝访问
  2. USACO4.13Fence Loops(无向图最小环)
  3. cocos2d-x 2.2 开发手记2
  4. Aspcms所有标签调用
  5. bzoj3796
  6. Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞
  7. sdfsdf
  8. How to: Hide the Ribbon in SharePoint 2010
  9. C#基础回顾:正则表达式
  10. LinkedList源码解析