首页 > 代码库 > [Unity+Android]横版扫描二维码
[Unity+Android]横版扫描二维码
终于解决了一个忧伤好久的问题,严重拖了项目进度,深感惭愧!一直被一系列的问题所困扰,然后又只能自己一个人摸索,也是一段辛酸忧伤史,现在小结一下上个月在做二维码的过程中所碰到的问题以及解决办法,现在庆幸终于解决好了,终于能将这个功能告一段落,一下小结也是分享一下Unity的某些“坑”,让同行少走弯路,起码在二维码这方面应该会有所启迪,欣慰的是接下来几天终于可以做自己应该做的事情了!
效果图:
先小结一下碰到的问题:
1.Unity工程屏幕方向与Android工程屏幕方向要一致的问题
本来我测试Android扫码的功能,已经测是通过了,可以参考之前写的博文:http://blog.csdn.net/dingxiaowei2013/article/details/24677795,后来将这这个插件添加到现有的项目中,但导出项目apk后,打开应用怎么就是闪退,表示很忧伤,后来一步一步的精简对比测试,表示自己五一那天一直摸索到凌晨三点才发现这一问题根源,有了这个基础才有接下来思路的转变。我的Android工程设置的屏幕旋转方向是portrait,但由于项目原因,Unity的导出方向必须是landscapeleft,这两者方向不一致会导致应用异常奔溃闪退!当然我的解决思路可能是比较笨的,我将Android工程做成的插件的屏幕方向也做成了横屏的,为了和Unity的方向一致,我想应该就不会出现那种问题。我原本的思路是将Unity导出屏幕设置成auto,然后代码控制屏幕的方向,但貌似还是不成功,没辙只能还是换种思路!网上有不少Android的扫码工程,最多的就是基于zxing,但是都是有很多冗余,不适合初学者,尤其是像我这种Android初学者,我倒是看到一个比较适合入门的精简的扫码工程,可惜是纵屏的,上一篇文章就是介绍的这个,但并不适合我的项目需求,所以我就想办法将这个工程改成横屏工程,本以为只要改改xml配置文件就OK的,结果并不是想象的那么简单,仅仅那么该会出现扫的过程中图片压缩以及移动别扭等现象,还是需要修改里面的工程的!这里可以参考:http://dingxiaowei2013.blog.163.com/blog/static/21965310720144595534507/,这篇是将横屏改纵屏的解决方案,然后将其逆序操作,但我还是出现了一些问题,比如扫到一半的时候应用异常崩溃,又表示很忧伤,为啥都不是一帆风顺呢!然后反复重做,终于成功了!精简版的工程源码我会贴出来共享!为了实现这个功能,可谓是一次有一次的导出,一次有一次的测试,千言万语无法言表其中的忧伤,总而言之,坚韧,是程序猿必须具备的品质!
2.Unity与Android场景跳转的问题
Unity和Android交互不仅仅是调用函数,交互数据,很大的一个还是需要交互视图和场景,将其很好的进行切换。当然场景的跳转或者是切换还是通过调用接口函数来实现,但这里通过了一个很重要的Activity来实现了这一效果,就是UnityPlayerActivity,Android的接口视图就是继承了这个UnityPlayerActivity,这个是为Unity和Android搭建了一个桥梁,当然这个类必须导入Unity的接口包,在Unity的安装目录下,详细可以参考上一篇文章,继承了UnityPlayerActivity的视图是作为Unity和Android的一个通用视图,它是Android插件的一个入口,无论是在Unity切换到Android界面还是Android切换到Unity界面,必须都要通过这个Activity来操作,记得是必须,这里我也是吃过苦头,我想在其他的Activity中来调用Unity的接口函数来实现Unity界面的跳转,但都尝试失败!后来再重头再来,这些都是经过了一遍又一遍的实验才得出的结论!
3.LG的手机作为Unity的测试机貌似有一些问题(纯属个人感觉)
附带修改的Android精简版的扫码工程代码(只贴修改过的脚本):
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.qr_codescan"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar" >
<activity
android:name="com.example.qr_codescan.MainActivity"
android:screenOrientation="landscape"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MipcaActivityCapture"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="landscape"
android:windowSoftInputMode="stateAlwaysHidden" >
</activity>
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
</manifest>
MainActivity.java
package com.example.qr_codescan;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
publicclass MainActivity extends UnityPlayerActivity {
privatefinalstaticint SCANNIN_GREQUEST_CODE = 1;
/**
* 显示扫描结果
*/
// private TextView mTextView ;
/**
* 显示扫描拍的图片
*/
// private ImageView mImageView;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
//
// mTextView = (TextView) findViewById(R.id.result);
// mImageView = (ImageView) findViewById(R.id.qrcode_bitmap);
//
// //点击按钮跳转到二维码扫描界面,这里用的是startActivityForResult跳转
// //扫描完了之后调到该界面
// Button mButton = (Button) findViewById(R.id.button1);
// mButton.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// Intent intent = new Intent();
// intent.setClass(MainActivity.this, MipcaActivityCapture.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
// }
// });
}
publicvoid Show()
{
Intent intent = new Intent();
intent.setClass(MainActivity.this, MipcaActivityCapture.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
}
@Override
protectedvoid onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case SCANNIN_GREQUEST_CODE:
if(resultCode == RESULT_OK){
Bundle bundle = data.getExtras();
//显示扫描到的内容
// mTextView.setText(bundle.getString("result"));
//显示
// mImageView.setImageBitmap((Bitmap) data.getParcelableExtra("bitmap"));
UnityPlayer.UnitySendMessage("GameObject","GetString",bundle.getString("result"));
}
break;
}
}
}
MipcaActivityCapture.java
CameraConfigurationManager.java
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mining.app.zxing.camera;
import android.content.Context;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
finalclass CameraConfigurationManager {
privatestaticfinal String TAG = CameraConfigurationManager.class.getSimpleName();
privatestaticfinalint TEN_DESIRED_ZOOM = 27;
privatestaticfinalint DESIRED_SHARPNESS = 30;
privatestaticfinal Pattern COMMA_PATTERN = Pattern.compile(",");
privatefinal Context context;
private Point screenResolution;
private Point cameraResolution;
privateint previewFormat;
private String previewFormatString;
CameraConfigurationManager(Context context) {
this.context = context;
}
/**
* Reads, one time, values from the camera that are needed by the app.
*/
void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
previewFormat = parameters.getPreviewFormat();
previewFormatString = parameters.get("preview-format");
Log.d(TAG, "Default preview format: " + previewFormat + ‘/‘ + previewFormatString);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
screenResolution = new Point(display.getWidth(), display.getHeight());
Log.d(TAG, "Screen resolution: " + screenResolution);
cameraResolution = getCameraResolution(parameters, screenResolution);
Log.d(TAG, "Camera resolution: " + screenResolution);
}
/**
* Sets the camera up to take preview images which are used for both preview and decoding.
* We detect the preview format here so that buildLuminanceSource() can build an appropriate
* LuminanceSource subclass. In the future we may want to force YUV420SP as it‘s the smallest,
* and the planar Y can be used for barcode scanning without a copy in some cases.
*/
void setDesiredCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Log.d(TAG, "Setting preview size: " + cameraResolution);
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
setFlash(parameters);
setZoom(parameters);
//setSharpness(parameters);
//modify here
// camera.setDisplayOrientation(90);
//兼容2.1
//setDisplayOrientation(camera, 90);
camera.setParameters(parameters);
}
Point getCameraResolution() {
return cameraResolution;
}
Point getScreenResolution() {
return screenResolution;
}
int getPreviewFormat() {
return previewFormat;
}
String getPreviewFormatString() {
return previewFormatString;
}
privatestatic Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
String previewSizeValueString = parameters.get("preview-size-values");
// saw this on Xperia
if (previewSizeValueString == null) {
previewSizeValueString = parameters.get("preview-size-value");
}
Point cameraResolution = null;
if (previewSizeValueString != null) {
Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
}
if (cameraResolution == null) {
// Ensure that the camera resolution is a multiple of 8, as the screen may not be.
cameraResolution = new Point(
(screenResolution.x >> 3) << 3,
(screenResolution.y >> 3) << 3);
}
return cameraResolution;
}
privatestatic Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
int bestX = 0;
int bestY = 0;
int diff = Integer.MAX_VALUE;
for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
previewSize = previewSize.trim();
int dimPosition = previewSize.indexOf(‘x‘);
if (dimPosition < 0) {
Log.w(TAG, "Bad preview-size: " + previewSize);
continue;
}
int newX;
int newY;
try {
newX = Integer.parseInt(previewSize.substring(0, dimPosition));
newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
} catch (NumberFormatException nfe) {
Log.w(TAG, "Bad preview-size: " + previewSize);
continue;
}
int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
if (newDiff == 0) {
bestX = newX;
bestY = newY;
break;
} elseif (newDiff < diff) {
bestX = newX;
bestY = newY;
diff = newDiff;
}
}
if (bestX > 0 && bestY > 0) {
returnnew Point(bestX, bestY);
}
returnnull;
}
privatestaticint findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
int tenBestValue = http://www.mamicode.com/0;
for (String stringValue : COMMA_PATTERN.split(stringValues)) {
stringValue = http://www.mamicode.com/stringValue.trim();
double value;
try {
value = http://www.mamicode.com/Double.parseDouble(stringValue);
} catch (NumberFormatException nfe) {
return tenDesiredZoom;
}
int tenValue = http://www.mamicode.com/(int) (10.0 * value);
if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
tenBestValue = http://www.mamicode.com/tenValue;
}
}
return tenBestValue;
}
privatevoid setFlash(Camera.Parameters parameters) {
// FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
// And this is a hack-hack to work around a different value on the Behold II
// Restrict Behold II check to Cupcake, per Samsung‘s advice
//if (Build.MODEL.contains("Behold II") &&
// CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
parameters.set("flash-value", 1);
} else {
parameters.set("flash-value", 2);
}
// This is the standard setting to turn the flash off that all devices should honor.
parameters.set("flash-mode", "off");
}
privatevoid setZoom(Camera.Parameters parameters) {
String zoomSupportedString = parameters.get("zoom-supported");
if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
return;
}
int tenDesiredZoom = TEN_DESIRED_ZOOM;
String maxZoomString = parameters.get("max-zoom");
if (maxZoomString != null) {
try {
int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
if (tenDesiredZoom > tenMaxZoom) {
tenDesiredZoom = tenMaxZoom;
}
} catch (NumberFormatException nfe) {
Log.w(TAG, "Bad max-zoom: " + maxZoomString);
}
}
String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
if (takingPictureZoomMaxString != null) {
try {
int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
if (tenDesiredZoom > tenMaxZoom) {
tenDesiredZoom = tenMaxZoom;
}
} catch (NumberFormatException nfe) {
Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
}
}
String motZoomValuesString = parameters.get("mot-zoom-values");
if (motZoomValuesString != null) {
tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
}
String motZoomStepString = parameters.get("mot-zoom-step");
if (motZoomStepString != null) {
try {
double motZoomStep = Double.parseDouble(motZoomStepString.trim());
int tenZoomStep = (int) (10.0 * motZoomStep);
if (tenZoomStep > 1) {
tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
}
} catch (NumberFormatException nfe) {
// continue
}
}
// Set zoom. This helps encourage the user to pull back.
// Some devices like the Behold have a zoom parameter
if (maxZoomString != null || motZoomValuesString != null) {
parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
}
// Most devices, like the Hero, appear to expose this zoom parameter.
// It takes on values like "27" which appears to mean 2.7x zoom
if (takingPictureZoomMaxString != null) {
parameters.set("taking-picture-zoom", tenDesiredZoom);
}
}
publicstaticint getDesiredSharpness() {
return DESIRED_SHARPNESS;
}
/**
* compatible 1.6
* @param camera
* @param angle
*/
protectedvoid setDisplayOrientation(Camera camera, int angle){
Method downPolymorphic;
try
{
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
if (downPolymorphic != null)
downPolymorphic.invoke(camera, new Object[] { angle });
}
catch (Exception e1)
{
}
}
}
本文出自 “丁小未的专栏” 博客,请务必保留此出处http://dingxiaowei.blog.51cto.com/4561335/1406958