Maison >Java >javaDidacticiel >Android implémente divers effets d'analyse de code QR basés sur Google Zxing

Android implémente divers effets d'analyse de code QR basés sur Google Zxing

高洛峰
高洛峰original
2017-01-13 11:18:531078parcourir

Avec l'arrivée de WeChat, les codes QR sont de plus en plus populaires. Vous pouvez voir les codes QR partout, comme dans les centres commerciaux, KFC, les restaurants, etc. Pour scanner les codes QR, nous utilisons le framework open source de Google Zxing We. Vous pouvez aller sur http://code.google.com/p/zxing/ pour télécharger le code source et le package Jar. Avant, la fonction de numérisation de code QR de mon projet n'implémentait que la fonction de numérisation, et son interface utilisateur était vraiment moche. bon L'interface utilisateur d'un logiciel d'application doit être acceptée par le public, sinon les gens n'utiliseront pas votre logiciel. Par conséquent, les fonctions du logiciel d'application sont aussi importantes que l'interface. Par exemple, WeChat, je pense que l'interface utilisateur de WeChat a été imitée par. De nombreux logiciels d'application. J'ai également imité l'effet de la numérisation du code QR sur WeChat. Bien qu'il ne soit pas aussi raffiné que WeChat, l'effet est toujours bon, je vais donc partager avec vous le code pour modifier l'interface utilisateur et le code pour la numérisation. le code QR. Premièrement, je rencontrerai des problèmes dans les projets futurs. Copiez et utilisez directement la même fonction. La seconde est de donner une référence à ceux qui n'ont pas ajouté la fonction QR code, haha. s'est également tenu sur les épaules de géants avant d'ajouter cette fonction. Ensuite, suivez J'ai implémenté cette fonction étape par étape et supprimé de nombreux fichiers inutiles

Jetons d'abord un coup d'œil à la structure du projet

Android基于google Zxing实现各类二维码扫描效果

Si vous souhaitez faire de même pour votre projet Pour ajouter cette fonction, vous copiez directement les trois packages com.mining.app.zxing.camera, com.mining.app.zxing.decoding , et com.mining.app.zxing.view à votre projet, puis introduisez les ressources correspondantes. Pour saisir les ressources correspondantes, je les ai également citées directement à partir de mon projet. Le nom du package n'a pas été modifié. Bien sûr, je dois également le faire. citant le package Zxing.jar

com.example.qr_codescan, qui contient un MipcaActivityCapture , il introduit également directement le code de mon projet précédent. Cette activité gère principalement les classes d'interface d'analyse. l'analyse est réussie, il y aura des sons et des vibrations, etc. Il se concentre principalement sur la méthode handleDecode (résultat du résultat, code-barres Bitmap) à l'intérieur. Une fois l'analyse terminée, l'analyse affichera les paramètres initiaux du résultat et le bitmap du QR. Le code est transmis à handleDecode (résultat du résultat, code-barres Bitmap). Il suffit d'y écrire le code de traitement correspondant. Il n'est pas nécessaire de modifier d'autres endroits. Je gérerai ici les résultats et les photos prises par numérisation

.
/** 
 * 处理扫描结果 
 * @param result 
 * @param barcode 
 */
public void handleDecode(Result result, Bitmap barcode) { 
  inactivityTimer.onActivity(); 
  playBeepSoundAndVibrate(); 
  String resultString = result.getText(); 
  if (resultString.equals("")) { 
    Toast.makeText(MipcaActivityCapture.this, "Scan failed!", Toast.LENGTH_SHORT).show(); 
  }else { 
    Intent resultIntent = new Intent(); 
    Bundle bundle = new Bundle(); 
    bundle.putString("result", resultString); 
    bundle.putParcelable("bitmap", barcode); 
    resultIntent.putExtras(bundle); 
    this.setResult(RESULT_OK, resultIntent); 
  } 
  MipcaActivityCapture.this.finish(); 
}

J'ai apporté mes propres modifications à la présentation de l'interface MipcaActivityCapture. Jetons d'abord un coup d'œil aux rendus. J'utilise principalement FrameLayout, avec RelativeLayout imbriqué à l'intérieur.

Le code de mise en page est le suivant

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
    android:layout_height="fill_parent" > 
  
  <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" > 
  
    <SurfaceView
      android:id="@+id/preview_view"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:layout_gravity="center" /> 
  
    <com.mining.app.zxing.view.ViewfinderView
      android:id="@+id/viewfinder_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" /> 
  
    <include
      android:id="@+id/include1"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_alignParentTop="true"
      layout="@layout/activity_title" /> 
  </RelativeLayout> 
  
</FrameLayout>

Dans celui-ci, j'ai écrit la partie supérieure de l'interface dans une autre mise en page, puis je l'ai inclus, car ce Activity_title est également utilisé par d'autres activités dans mon projet. Je l'ai également copié directement

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:background="@drawable/mmtitle_bg_alpha" > 
  
  <Button
    android:id="@+id/button_back"
    android:layout_width="75.0dip"
    android:text="返回"
    android:background="@drawable/mm_title_back_btn"
    android:textColor="@android:color/white"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="2dip" /> 
  
  <TextView
    android:id="@+id/textview_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBaseline="@+id/button_back"
    android:layout_alignBottom="@+id/button_back"
    android:layout_centerHorizontal="true"
    android:gravity="center_vertical"
    android:text="二维码扫描"
    android:textColor="@android:color/white"
    android:textSize="18sp" /> 
  
</RelativeLayout>

Dans ma démo, il y a une interface principale MainActivity avec un Button, un ImageView et un TextView Cliquez sur le bouton pour accéder à l'interface de numérisation du code QR. l'analyse est OK Lorsque vous revenez à l'interface principale, affichez les résultats numérisés dans TextView et affichez l'image dans ImageView. Ensuite, vous n'avez pas besoin de traiter l'image. J'ajouterai des images ici. très simple comme suit

<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"
  android:background="#ffe1e0de" > 
  
  <Button
    android:id="@+id/button1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:text="扫描二维码" /> 
  
  <TextView
    android:id="@+id/result"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/button1"
    android:lines="2"
    android:gravity="center_horizontal"
    android:textColor="@android:color/black"
    android:textSize="16sp" /> 
  
  <ImageView
    android:id="@+id/qrcode_bitmap"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/result"/> 
</RelativeLayout>

Le code dans MainActivity est le suivant, les fonctions à l'intérieur ont été mentionnées ci-dessus

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; 
  
public class MainActivity extends Activity { 
  private final static int SCANNIN_GREQUEST_CODE = 1; 
  /** 
   * 显示扫描结果 
   */
  private TextView mTextView ; 
  /** 
   * 显示扫描拍的图片 
   */
  private ImageView mImageView; 
    
  
  @Override
  protected void 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); 
      } 
    }); 
  } 
    
    
  @Override
  protected void 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")); 
      } 
      break; 
    } 
  }   
  
}

Le code ci-dessus est encore relativement simple, mais si vous voulez créez une boîte de numérisation comme WeChat, tenez-vous-y. Le code n'a pas cet effet. Nous devons réécrire la classe ViewfinderView sous le package com.mining.app.zxing.view. Les images dans WeChat sont toutes utilisées. , et les commentaires du code sont relativement clairs. Regardons simplement le code, je pense que vous pouvez comprendre. Si vous souhaitez modifier la taille du cadre de numérisation, accédez à la classe CameraManager et modifiez

/* 
 * Copyright (C) 2008 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 
 * 
 *   * 
 * 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.view; 
  
import java.util.Collection; 
import java.util.HashSet; 
  
import android.content.Context; 
import android.content.res.Resources; 
import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.graphics.Typeface; 
import android.util.AttributeSet; 
import android.view.View; 
  
import com.example.qr_codescan.R; 
import com.google.zxing.ResultPoint; 
import com.mining.app.zxing.camera.CameraManager; 
  
/** 
 * This view is overlaid on top of the camera preview. It adds the viewfinder 
 * rectangle and partial transparency outside it, as well as the laser scanner 
 * animation and result points. 
 * 
 */ 
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_TOP = 30; 
    
  /** 
   * 画笔对象的引用 
   */ 
  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; 
    
  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) { 
    //中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改 
    Rect frame = CameraManager.get().getFramingRect(); 
    if (frame == null) { 
      return; 
    } 
      
    //初始化中间线滑动的最上边和最下边 
    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); 
        
        
      //画扫描框下面的字 
      paint.setColor(Color.WHITE); 
      paint.setTextSize(TEXT_SIZE * density); 
      paint.setAlpha(0x40); 
      paint.setTypeface(Typeface.create("System", Typeface.BOLD)); 
      canvas.drawText(getResources().getString(R.string.scan_text), frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint); 
        
        
  
      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); 
        
    } 
  } 
  
  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); 
  } 
  
}

. . Dans le code ci-dessus, la ligne médiane est l'image utilisée par WeChat. Si vous souhaitez être plus simulé, remplacez le code suivant

canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH/2, frame.right - MIDDLE_LINE_PADDING,slideTop + MIDDLE_LINE_WIDTH/2, paint);

par <.>

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.qrcode_scan_line))).getBitmap(), null, lineRect, paint);

Allez trouver vous-même la ligne de numérisation dans WeChat. Celle que j'ai publiée est déformée. Téléchargez l'apk WeChat, changez le nom du suffixe en zip et. puis décompressez-le.

Le code pour dessiner la police sous la zone de numérisation doit être modifié. Ensuite, il peut être automatiquement disposé au milieu en fonction de la police. Si le mot est trop long, je ne l'ai pas traité. Il doit être automatiquement enveloppé. Vous pouvez le gérer vous-même

paint.setColor(Color.WHITE); 
paint.setTextSize(TEXT_SIZE * density); 
paint.setAlpha(0x40); 
paint.setTypeface(Typeface.DEFAULT_BOLD); 
String text = getResources().getString(R.string.R.string.scan_text);
float textWidth = paint.measureText(text);
  
canvas.drawText(text, (width - textWidth)/2, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint)

Capture d'écran de l'interface en cours d'exécution, dans laquelle la ligne verte du milieu se déplacera de haut en bas, ce qui est similaire à l'effet de WeChat. Bien sûr, vous avez toujours besoin des autorisations correspondantes pour l'exécuter.


Android基于google Zxing实现各类二维码扫描效果

Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à tout le monde d'apprendre la programmation de logiciels Android.

Pour plus d'articles basés sur Android sur la réalisation de divers effets d'analyse de code QR basés sur Google Zxing, veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn