一、效果图:

Android 模仿360二维码扫描功能_广告


Android 模仿360二维码扫描功能_360_02


二、框架搭建

1、首先,下载最新zxing开源项目。 下载地址:http://code.google.com/p/zxing/
或 链接:http://pan.baidu.com/s/1c2JRUfQ 密码:hqcg

2、分析项目结构,明确扫描框架需求。在zxing中,有很多其他的功能,项目结构比较复杂;针对二维码QRCode扫描,我们需要几个包:
(1)com.google.zxing.client.android.Camera 基于Camera调用以及参数配置,核心包
(2)DecodeFormatManager、DecodeThread、DecodeHandler 基于解码格式、解码线程、解码结果处理的解码类
(3)ViewfinderView、ViewfinderResultPointCallBack 基于取景框视图定义的View类
(4)CaptureActivity、CaptureActivityHandler 基于扫描Activity以及扫描结果处理的Capture类
(5)InactivityTimer、BeepManager、FinishListener 基于休眠、声音、退出的辅助管理类
(6)Intents、IntentSource、PrefrencesActivity 基于常量存储的常量类

3、新建工程,添加如下权限permission:

<uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />

4、 添加core.jar文件,并BuildPath;将上述类或包加入工程后,会报一系列错误,原因有几点:
(1)资源文件缺乏,将zxing下需要的资源文件copy到新工程下
(2)版本兼容问题,zxing下很多技术都是使用4.0版本及以上,集成到低版本之后,须做相应改动,详情参照项目源码
(3)包结构引用问题,需要重新导入包引用

5、最后框架

Android 模仿360二维码扫描功能_ide_03

三、具体实现

1、创建MainActivity用于跳转到扫描页面

/**
 * 二维码扫描
 * @Project    App_ZXing
 * @Package    com.android.scan
 * @author     chenlin
 * @version    1.0
 * @Date       2014年3月6日
 */
public class MainActivity extends Activity {

    private Button mBtnScan;
    private Button mBtnBack;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnBack = (Button) findViewById(R.id.btn_back);
        mBtnBack.setVisibility(View.GONE);


        mBtnScan = (Button) findViewById(R.id.btn_scan);
        mBtnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, ScanActivity.class));
            }
        });
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/btn_scan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@color/white"
        android:padding="20dp"
        android:background="@drawable/btn_scan_result"
        android:text="扫一扫" />

    <include
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            layout="@layout/activity_scan_title" />

</RelativeLayout>

二、扫描页面

/**
 * 条形码扫描
 * 
 * @Project App_ZXing
 * @Package com.android.scan
 * @author chenlin
 * @version 1.0
 * @Date 2014年3月6日
 */
public class ScanActivity extends Activity implements Callback {
    private static final float BEEP_VOLUME = 0.10f;
    protected static final String RESULT_TEXT = "result";
    protected static final String RESULT_BITMAP = "bitmap";

    /** 扫描界面 */
    private SurfaceView mSurfaceView;
    /** 自定义的View,就是我们看见的拍摄时中间的框框了 */
    private ViewfinderView mFindView;
    /** 解码处理类,负责调用另外的线程进行解码。 */
    private CaptureActivityHandler mHandler;
    /** 判断是否创建了SurfaceView */
    private boolean hasSurface;
    /** decodeFormats 条形码,二维码等的集合 */
    private Vector<BarcodeFormat> mDecodeFormats;
    /** 字符编码 */
    private String characterSet;
    /** 定时器 */
    private InactivityTimer inactivityTimer;
    /** 媒体播放器 */
    private MediaPlayer mediaPlayer;
    /** 是否播放声音 */
    private boolean playBeep;
    /** 是否震动 */
    private boolean vibrate;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        mSurfaceView = (SurfaceView) findViewById(R.id.preview_view);
        mFindView = (ViewfinderView) findViewById(R.id.viewfinder_view);
        CameraManager.init(getApplication());
        hasSurface = false;
        inactivityTimer = new InactivityTimer(this);
    }

    /**
     * 暂停后恢复时处理内容
     */
    @SuppressWarnings("all")
    @Override
    protected void onResume() {
        super.onResume();
        // 先重新获得holder
        SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
        if (hasSurface) {
            initCamera(surfaceHolder);
        } else {
            surfaceHolder.addCallback(this);
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
        mDecodeFormats = null;
        characterSet = null;

        playBeep = true;
        AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
        // 如果当前是铃音模式,则继续准备下面的 蜂鸣提示音操作,如果是静音或者震动模式。就不要继续了。因为用户选择了无声的模式,我们就也不要出声了。
        if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
            playBeep = false;
        }
        // 初始化播放声音
        initBeepSound();
        //播放
        vibrate = true;

    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mHandler != null) {
            mHandler.quitSynchronously();
            mHandler = null;
        }
        // 关闭摄像头信息
        CameraManager.get().closeDriver();
    }

    @Override
    protected void onDestroy() {
        // 关闭定时器
        inactivityTimer.shutdown();
        super.onDestroy();
    }

    /**
     * 处理扫描结果
     *
     * @param result
     * @param barcode
     */
    public void handleDecode(Result result, Bitmap barcode) {
        // 添加定时器
        inactivityTimer.onActivity();
        // 响铃和震动
        playBeepSoundAndVibrate();
        String resultString = result.getText();

        if (TextUtils.isEmpty(resultString)) {
            ToastUtil.show(this, "扫描失败");
            ScanActivity.this.finish();
        } else {
            //扫描完成后传递结果
            Intent resultIntent = new Intent();
            resultIntent.setClass(ScanActivity.this, ScanResultActivity.class);
            resultIntent.putExtra(RESULT_TEXT, resultString);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            barcode.compress(Bitmap.CompressFormat.PNG, 100, baos);
            byte[] bitmapByte = baos.toByteArray();
            resultIntent.putExtra(RESULT_BITMAP, bitmapByte);
            startActivity(resultIntent);
            overridePendingTransition(R.anim.activity_in_from_rigth, R.anim.activity_out_to_scale);
            ScanActivity.this.finish();
        }

    }

    private void initCamera(SurfaceHolder surfaceHolder) {
        try {
            CameraManager.get().openDriver(surfaceHolder);
        } catch (IOException ioe) {
            return;
        } catch (RuntimeException e) {
            return;
        }
        if (mHandler == null) {
            mHandler = new CaptureActivityHandler(this, mDecodeFormats, characterSet);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    /**
     * 在视图创建的时候初始化摄像头
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (!hasSurface) {
            hasSurface = true;
            initCamera(holder);
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        hasSurface = false;
    }

    public ViewfinderView getViewfinderView() {
        return mFindView;
    }

    public Handler getHandler() {
        return mHandler;
    }

    public void drawViewfinder() {
        mFindView.drawViewfinder();
    }

    /**
     * 初始化声音资源
     */
    private void initBeepSound() {
        // 如果要播放声音并且没有播放器时
        if (playBeep && mediaPlayer == null) {
            // 设置声道流格式为音乐
            setVolumeControlStream(AudioManager.STREAM_MUSIC);
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            // 设置声音完成后监听
            mediaPlayer.setOnCompletionListener(beepListener);

            // 设定数据源,并准备播放
            AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
            try {
                mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
                file.close();
                mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);// 设置音量
                mediaPlayer.prepare();
            } catch (IOException e) {
                mediaPlayer = null;
            }
        }
    }

    private static final long VIBRATE_DURATION = 200L;

    /**
     * 响铃和震动
     */
    private void playBeepSoundAndVibrate() {
        if (playBeep && mediaPlayer != null) {
            mediaPlayer.start();
        }
        if (vibrate) {
            Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
            // 震动一次
            vibrator.vibrate(VIBRATE_DURATION);

            // 第一个参数,指代一个震动的频率数组。每两个为一组,每组的第一个为等待时间,第二个为震动时间。
            // 比如 [2000,500,100,400],会先等待2000毫秒,震动500,再等待100,震动400
            // 第二个参数,repest指代从 第几个索引(第一个数组参数) 的位置开始循环震动。
            // 会一直保持循环,我们需要用 vibrator.cancel()主动终止
            // vibrator.vibrate(new long[]{300,500},0);
        }
    }

    /**
     * 注册事件。当播放完毕一次后,重新指向流文件的开头,以准备下次播放。
     */
    private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() {
        public void onCompletion(MediaPlayer mediaPlayer) {
            mediaPlayer.seekTo(0);
        }
    };

}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <SurfaceView
            android:id="@+id/preview_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center" />

        <com.android.scan.view.ViewfinderView
            android:id="@+id/viewfinder_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <include
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            layout="@layout/activity_scan_title" />
    </RelativeLayout>

</FrameLayout>

3、结果页面

/**
 * 
 * @Project App_ZXing
 * @Package com.android.scan
 * @author chenlin
 * @version 1.0
 * @Date 2014年3月6日
 * @Note TODO
 */
public class ScanResultActivity extends Activity {
    private EditText mEtScan;
    private Button mBtnBack;
    private Button mBtnCopy;
    private ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_result);
        initViews();
        init();
    }

    private void init() {
        final String result = getIntent().getStringExtra(ScanActivity.RESULT_TEXT);
        mEtScan.setText(result);

        final ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

        mBtnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ScanResultActivity.this.finish();
            }
        });

        mBtnCopy.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (result!=null) {
                    ClipData myClip = ClipData.newPlainText("text", result);
                    cm.setPrimaryClip(myClip);
                }

            }
        });

        //设置图片信息
        byte[] imgByte = getIntent().getByteArrayExtra(ScanActivity.RESULT_BITMAP);
        if (imgByte != null) {
            Drawable drawable = BitmapUtil.byte2Drawable(imgByte);
            if (drawable != null) {
                mImageView.setImageDrawable(drawable);
            }

        }

    }

    // public void paste(View view){
    // ClipData abc = myClipboard.getPrimaryClip();
    // ClipData.Item item = abc.getItemAt(0);
    // String text = item.getText().toString();
    // pasteField.setText(text);
    // Toast.makeText(getApplicationContext(), "Text Pasted",
    // Toast.LENGTH_SHORT).show();
    // }

    private void initViews() {
        mEtScan = (EditText) findViewById(R.id.et_scan_result);
        mBtnBack = (Button) findViewById(R.id.btn_back);
        mBtnCopy = (Button) findViewById(R.id.btn_copy);
    }

}

四、代码下载

链接:http://pan.baidu.com/s/1nvesbxB 密码:tyyw