• 基于二维码的移动互联应用


    分享开发 Android 手机应用的开发经验——QR生成器

    转:http://android-mini.iteye.com/blog/736299?page=2

       声明一下:本次活动是谷歌举办的,要求是在校大学生。我今年本科毕业,但是继续上研了,目前好像处于无人管阶段,即不属于人事部也不属于教育部,不知道我有没有资格参加这个活动 。好了,闲话少说,有没有资格暂且不管,先把博文写好吧。 
            本文讲述的是我从接触android到开发出第一款软件的过程,期间也有很多毕业前的烦事所扰,断断续续大概用了一个多月的时间吧,算是搞出了第一个像样点的app。当然,我这边文章讲述的android开发经验肯定是入门级别的,希望大牛们看了不要笑话(^o^)/~。 
            话说接触android开发,算是机缘巧合。今年4月份中旬左右,那时的我还在为自己本科的毕设迷茫和忙碌。在实验室窝了一天,去吃饭时候刚好遇到班上一哥们,说要去听一个“XX公司”举办的android比赛的报告会,就跟着去了。本来是去凑热闹的,没想到听后觉得这个挺有意思,刚好自己有一款传说能装android系统的M8,就尝试着学学吧。自己摸索了十几天,有点感觉了,就准备着做二维码生成器和扫描仪了。顶着毕设的压力,最后竟然把这个二维码做出来了,还获得了这个公司比赛的奖品,现在暑假还来到了这个公司实习,继续android方面的开发,实在是幸运。 
            关于Android平台我就不介绍了,相信大家也都略知一二,只要知道它是开源的,现在有很多手机用,以后也会有很多手机用就OK了。下面我就把我是怎么从对Android一无所知到开发出第一款软件的过程分析给大家看看,算是抛砖引玉了哈。 


             一:查资料。 

             说干就干。在这之前,对Android只是听过名字而已,“开放手机联盟”根本没听说过。上网查了下Android方面的资料,不看不知道,一看吓一跳,铺天盖地的介绍,真是火的不得了,这才发现计算机科班出身的我是多么的落后。 
             不怕慢,只怕停。我接受新知识的能力总是很慢,说得好听点是慢工出细活,其实就是脑子迟钝,O(∩_∩)O~,见笑。但是我相信一句话:不怕慢,就怕停。这么多年过来了,就是靠着这句话才算混到现在。Android方面的资料多有好处也有坏处。好处就是资料多了,你想看哪个看哪个,想看什么有什么,坏处却是资料太多,不知道看哪个好。特别对于新手,没有鉴别能力,今天看这本书,明天看那本书,结果书换了好几本,知识掌握的寥寥无几。 
             所以我做的第一件事就是甄选资料。去eoe,安卓网等论坛逛逛,把介绍差不多的电子书,例子什么的都先下载下来,然后一个个先快速浏览一下,最后确定看哪本书。我当时资料下载了很多,光电子书就有六七本,英文的也有好几本,最后我选择《深入浅出AndroidGoogle》作为入门书籍。原因有二:1这本电子书的整体色彩不错,采用淡绿色作为背景,看着挺舒服。2这点是最重要的一点,我甄别是否适合入门书的标准就是看书中介绍第一个工程的方法,是不是能让初学者一看就懂。这本书这点就做得很好,比如他讲解Android预设程序架构的结构时,一步步,先分析资源中的XML文件,并且对XML文件的结构做了详细介绍,初学者一看就明白了Android程序的MVC框架,也知道了Android程序的档案结构。基于这两点,我选择这本书作为入门书。 

               


              资料选择好了,剩下的就是好好看了。那个时候自己真是疯了,毕设做的一塌糊涂,竟然天天在Eclipse下调试Android程序。用了大概十天时间,把这本书啃完了。看书的过程就不用多说了,无非是把书中的代码自己调试调试,增加印象。这本书的作者很细心,给出的代码基本没错误,照着调肯定都对。回头总结一下,感觉Android入门其实挺容易的哈。 
              上面就是我从对Android一无所知到入门的过程。这个过程很短,也就十几天时间,可是很多人却不肯迈出这一步。当时XX公司在我学校举办这个比赛时候,很多同学都知道,可是很少有人参加,因为以前大家都没接触过Android,所以就觉得这很神秘,很难,就放弃了。其实只要你愿意跨出这一步,就会觉得很多事情都很简单的。 
               我现在已经是eoe和海卓网论坛的版主,每天都看很多管理很多Android方面的帖子,偶尔还会为刚入门的同仁解决一两个小问题。想想一月前连Android怎么拼写都不知道的我,真是应了那句话“天下无难事,只怕有心人”。 


                 二:QR生成器统览 

                 QR简单介绍:QR码是二维条码的一种,1994年由日本Denso-Wave公司发明。QR来自英文“Quick Response”的缩写,即快速反应的意思,源自发明者希望QR码可让其内容快速被解码。按我的理解,QR就像国内超市商品中常用的一维码一样,可以储存信息,然后可以被扫描识别信息。 
                 闲话少数,先上图 

    工程文件预览
    主界面 
    名片信息编辑界面
    QR图像
    关于信息


                  本程序最初做的包含两个Activity,程序完成后,代码差不多一千行,可是生成的二维码图片不能保存。后来仔细看了看Canvas,Btimap,SurfaceView,View的用法,觉得先前我用的方法根本不能实现保存,不得不换一个方法来生成QR图片,没想到这样改来改去,程序完成时候剩下一个Activity,代码不到四百行。一下子砍掉了一大半,自己都不敢相信,可见方法的重要性。这个问题的解决过程是很有意思的,也是我做这款软件收获比较大的地方,下面会详细说明的。 
                  简单使用说明:本程序不需要在因特网连接的环境下运行,它利用本地库编译运行,节省流量。用户运行软件后,会进入主界面,根据提示,用户可以选择“名片”“短信”“电子邮件”“文本”“网络书签”等按钮进入相应的信息编辑界面,在信息编辑界面编辑完信息后,就可以生成QR图像了,然后用户可以把图像保存下来。 


                  三:代码分析 

                  这篇文章的定位是开发经验的介绍,再细分一下就是新手开发经验介绍,新手写的,当然是给尚未入门或是刚入门的同学们看的,虽然这个APP代码部分没什么难点,但我还是会把一些很基础的东西列出来,分析我是如何逐步完成这个APP的,希望高手们不要见笑。 

    3.1:主界面和事件设计 

                  主界面用的是RadioGroup的组事件。RadioGroup可将各自不同的RadioButton设置于同一个Radio按钮组,同属一个RadioGroup组里的按钮,只能做出单一选择。 

    Xml代码  收藏代码
    1. <RadioGroup  
    2.       android:id="@+id/select"  
    3.       android:layout_width="wrap_content"  
    4.       android:layout_height="wrap_content"  
    5.       xmlns:android="http://schemas.android.com/apk/res/android"  
    6.       android:orientation="vertical"  
    7.     >  
    8.       <RadioButton  
    9.         android:id="@+id/select1"  
    10.         android:layout_width="wrap_content"  
    11.         android:layout_height="wrap_content"  
    12.         android:text="名片"  
    13.       >  
    14.       </RadioButton>  
    15.       <RadioButton  
    16.         android:id="@+id/select2"  
    17.         android:layout_width="wrap_content"  
    18.         android:layout_height="wrap_content"  
    19.         android:text="短信"  
    20.       >  
    21.       </RadioButton>  
    22.          <RadioButton  
    23.         android:id="@+id/select3"  
    24.         android:layout_width="wrap_content"  
    25.         android:layout_height="wrap_content"  
    26.         android:text="电子邮件"  
    27.       >  
    28.       </RadioButton>  
    29.         <RadioButton  
    30.         android:id="@+id/select4"  
    31.         android:layout_width="wrap_content"  
    32.         android:layout_height="wrap_content"  
    33.         android:text="文本"  
    34.       >  
    35.       </RadioButton>  
    36.         <RadioButton  
    37.         android:id="@+id/select5"  
    38.         android:layout_width="wrap_content"  
    39.         android:layout_height="wrap_content"  
    40.         android:text="网址书签"  
    41.       >  
    42.       </RadioButton>  
    43.     </RadioGroup>  



              当然,为了符合MVC的理念,最好把text的内容都写到String.xml中。 
    主界面的事件很简单,就是单击按钮,会跳到对应的界面。我把名片,短信等界面的控件的初始化和界面的现实都写到对应的函数里面,界面切换时候调用对应的函数就可以了。 

    Java代码  收藏代码
    1. private RadioGroup.OnCheckedChangeListener mChangeRadio=new RadioGroup.OnCheckedChangeListener()  
    2.    {  
    3.     @Override  
    4.     public void onCheckedChanged(RadioGroup group,int checkedId)  
    5.     {  
    6.         if(checkedId==mRadioname.getId())  
    7.         {  
    8.             jumptolayoutbusinesscard();//跳到名片界面  
    9.               
    10.         }  
    11.         else if(checkedId==mRadiosms.getId())  
    12.         {  
    13.             jumptolayoutsms();//跳到短信界面  
    14.         }  
    15.         else if(checkedId==mRadioemail.getId())  
    16.         {  
    17.             jumptolayoutemail();//跳到电子邮件界面  
    18.         }  
    19.         else if(checkedId==mRadiotext.getId())  
    20.         {  
    21.             jumptolayouttext();//跳到文本界面  
    22.         }  
    23.         else  
    24.         {  
    25.             jumptolayouturl();//跳到网络标签界面  
    26.         }  
    27.     }  
    28.    };  


              这样主界面和事件的设计就完成了,是不是觉得很简单呢,哈哈。 

    3.2:名片界面和事件设计 

              名片,短信等这些界面的设计都是很初级的东西,一个TextView,对应一个EditText,然后再加上几个Button,就组成了这样的界面。 
              这部分代码很简单,就不列出来了。 
    3.3:关于程序信息--Menu功能菜单 

              用户单击Menu时,会弹出【关于】和【退出】按钮,【关于】会弹跳出AlertDialog,现实这个程序的“关于”信息,【退出】则会安全退出本程序。这部分功能对应三个简单的函数,大家看看就明白了。 

    Java代码  收藏代码
    1. public boolean onCreateOptionsMenu(Menu menu)  
    2.    {  
    3.      menu.add(0, 0, 0, R.string.app_about);  
    4.      menu.add(0, 1, 1, R.string.str_exit);  
    5.      return super.onCreateOptionsMenu(menu);  
    6.    }  
    7.      
    8.    public boolean onOptionsItemSelected(MenuItem item)  
    9.    {  
    10.      super.onOptionsItemSelected(item);  
    11.      switch(item.getItemId())  
    12.      {  
    13.        case 0:  
    14.          openOptionsDialog();  
    15.          break;  
    16.        case 1:  
    17.          finish();  
    18.          break;  
    19.      }  
    20.      return true;  
    21.    }  
    22.      
    23.    private void openOptionsDialog()  
    24.    {  
    25.      new AlertDialog.Builder(this)  
    26.      .setTitle(R.string.app_about)  
    27.      .setMessage(R.string.app_about_msg)  
    28.      .setPositiveButton(R.string.str_ok,  
    29.          new DialogInterface.OnClickListener()  
    30.          {  
    31.           public void onClick(DialogInterface dialoginterface, int i)  
    32.           {  
    33.           }  
    34.           }  
    35.          )  
    36.      .show();  
    37.    }  



    3.4:自定义产生QRCode的函数 

            难者不会,会者不难。这句话说的真好,你不要看下面的代码就寥寥几行,在这几行代码的背后,我可是费了很大功夫的。代码如下: 

    Java代码  收藏代码
    1. public Bitmap AndroidQREncode(String strEncoding, int qrcodeVersion) {  
    2.         Log.i("wwj", "QREncode");  
    3.         Bitmap canvasBmp = null;  
    4.   
    5.         try {  
    6.             // 构建QRCode编码对象  
    7.             com.swetake.util.Qrcode testQrcode = new com.swetake.util.Qrcode();  
    8.   
    9.             /* L','M','Q','H' */  
    10.             testQrcode.setQrcodeErrorCorrect('M');//错误修正率  
    11.             /* "N","A" or other */  
    12.             testQrcode.setQrcodeEncodeMode('B');  
    13.             /* 0-20 */  
    14.             testQrcode.setQrcodeVersion(qrcodeVersion);  
    15.             // getBytes  
    16.             byte[] bytesEncoding = strEncoding.getBytes("utf-8");  
    17.   
    18.             if (bytesEncoding.length > 0) {  
    19.                 // 转化成boolean数组  
    20.                 bEncoding = testQrcode.calQrcode(bytesEncoding);  
    21.                 int w = 320;  
    22.                 int h = 240;  
    23.                 canvasBmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);//创建一个可改变的Bitmat对象  
    24.                 Canvas cn = new Canvas(canvasBmp);//创建Canvas对象  
    25.                 onDraw(cn);//调用画图函数  
    26.                 cn.save(Canvas.ALL_SAVE_FLAG);  
    27.                 cn.restore();  
    28.             }  
    29.         } catch (Exception e) {  
    30.             e.printStackTrace();  
    31.         }  
    32.         return canvasBmp;  
    33.     }  


            需要说明的我都做了注释,这段代码用到了第三方的类库SwetakeQRCode.jar包,这个包在http://swetake.com可以下载(该程序使用的函数库版本为ver. 0.50 beta)。这段代码的主要作用就是利用jar包的函数库,把输入的信息转换成产生QR图片文件的二维数组。当然,在这个函数里面我们还创建了Bitmap和Canvas对象,这些都和后面介绍的图片保存有关。 

    3.5:既然已经生成了产生条形码的依据,剩下就是画图的问题了。 

             做java的都知道,绘图肯定首先需要一个Canvas,然后在用Graphics在上面绘制自己想要图案。不错,Android上面也类似,你可以从一个Bitmap得到它的Canvas,进行绘制,也可以自定义一个View,用它的Canvas。不同的时,Android里没有Graphics,而用Paint代之,当然用法也稍有不同。 
             单独说画图的问题,是很简单的,也有很多方法实现,可是涉及到绘图后的图片保存问题,画图问题貌似复杂了。绘制QR图片,我用到了两种方法,这里都列出来,让大家看看。 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

    SurfaceView和Canvas绘图


             SurfaceView重写onDraw()方法是没有用的。SurfaceView中画图的关键对象Canvas对象必须要从一个SurfaceHolder对象获取: 

    Canvas canvas = holder.lockCanvas();


    拿到canvas之后就可以进行绘画了。 
             绘画完毕之后还要做的一件事就是提交绘画 

    holder.unlockCanvasAndPost();


             从方法的命名可以看出,这个方法将原来锁定的(lockCanvas())的画板解除锁定,然后将画板的内容Post提交出去,应该是提交给SurfaceView,然后显示出来。 

    Java代码  收藏代码
    1. //在SurfaceView上绘制QRCode条形码  
    2.   private void drawQRCode(boolean[][] bRect, int colorFill)  
    3.   {  
    4.     /* test Canvas*/  
    5.     int intPadding = 20;  
    6.       
    7.   //绘图前先锁定Surfaceholder  
    8.     Canvas mCanvas01 = mSurfaceHolder01.lockCanvas();  
    9.   //设置画图绘图颜色  
    10.     mCanvas01.drawColor(getResources().getColor(R.drawable.white));  
    11.       
    12.     //创建画笔  
    13.     Paint mPaint01 = new Paint();  
    14.       
    15.     //设置画笔颜色和模式  
    16.     mPaint01.setStyle(Paint.Style.FILL);  
    17.     mPaint01.setColor(colorFill);  
    18.     mPaint01.setStrokeWidth(1.0F);  
    19.       
    20.     //逐一加载boolean数组  
    21.     for (int i=0;i<bRect.length;i++)  
    22.     {  
    23.       for (int j=0;j<bRect.length;j++)  
    24.       {  
    25.         if (bRect[j][i])  
    26.         {  
    27.           //绘出条形码方块  
    28.           mCanvas01.drawRect  
    29.           (  
    30.             new Rect  
    31.             (  
    32.               intPadding+j*3+2,//左上边x  
    33.               intPadding+i*3+2,//左上边y  
    34.               intPadding+j*3+2+3,//右下边x  
    35.               intPadding+i*3+2+3//右下边y  
    36.              ), mPaint01  
    37.           );  
    38.         }  
    39.       }  
    40.     }  
    41.    //解锁并绘图  
    42.     mSurfaceHolder01.unlockCanvasAndPost(mCanvas01);  
    43.      
    44.   }  
    45.     


             代码片段中用到的boolean数组就是从AndroidQREncode()函数中得到的。 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

    Canvas和Bitmap绘图


             Canvas可以用来显示位图,可是Canvas画图本身位图没有关系,不过可以把Canvas和Bitmap联系起来,这样图像保存时候就大有用途了。你还记得这两行代码吗: 

    canvasBmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);//创建可改变的Bitmat对象
    Canvas cn = new Canvas(canvasBmp);//创建Canvas对象


             这两行代码的意思是canvasBmp是一个刚好canvas大小的空Bitmap ,Canvas画完该会自动保存到canvasBmp中。 

    Java代码  收藏代码
    1. public void onDraw(Canvas mCanvas01) {  
    2.         super.onDraw(mCanvas01);  
    3.         Log.i("wwj", "onDraw");  
    4.         int intPadding = 50;  
    5.         // 设置画图绘图颜色  
    6.         mCanvas01.drawColor(getResources().getColor(R.drawable.white));  
    7.   
    8.         // 创建画笔  
    9.         Paint mPaint01 = new Paint();  
    10.   
    11.         // 设置画笔颜色和模式  
    12.         mPaint01.setStyle(Paint.Style.FILL);  
    13.         mPaint01.setColor(getResources().getColor(R.drawable.black));  
    14.         mPaint01.setStrokeWidth(1.0F);  
    15.   
    16.         // 逐一加载boolean数组  
    17.         for (int i = 0; i < bEncoding.length; i++) {  
    18.             for (int j = 0; j < bEncoding.length; j++) {  
    19.                 if (bEncoding[j][i]) {  
    20.                     // 绘出条形码方块  
    21.                     mCanvas01.drawRect(new Rect(intPadding + j * 3 + 2,// 左上边x  
    22.                             intPadding + i * 3 + 2,// 左上边y  
    23.                             intPadding + j * 3 + 2 + 3,// 右下边x  
    24.                             intPadding + i * 3 + 2 + 3// 右下边y  
    25.                     ), mPaint01);  
    26.                 }  
    27.             }  
    28.   
    29.         }  
    30.     }  


            你没看错,这段代码和第一种方法的代码基本一模一样。我是这样理解的:Canvas绘图和SurfaceView和Bitmap都没有实质的关系,但是在创建Canvas对象时,Canvas有几种创建方式,一种是用SurfaceHolder对象获取,另一种是和Bitmap关联。用SurfaceView和Canvas绘图后,图像会显示在SurfaceView上。用Bitmap和Canvas绘图后,图像信息会储存在Bitmap对象中。 

    3.6:QR图像保存 

            前面关于绘图的讨论那么多,就是为保存问题做铺垫。在Android上绘图后保存,貌似只能通过图像的Bitmap信息保存,也可能是我孤陋寡闻,只知道这一种方法。 

    Java代码  收藏代码
    1. public void saveMyBitmap(String bitName, Bitmap bmp) throws IOException {  
    2.           
    3.         File f = new File("/sdcard/" + bitName + ".png");         
    4.         f.createNewFile();    
    5.         FileOutputStream fOut = null;  
    6.         try {  
    7.             fOut = new FileOutputStream(f);  
    8.   
    9.         } catch (FileNotFoundException e) {  
    10.             e.printStackTrace();  
    11.         }  
    12.         // Log.i("wwj",""+bmp);  
    13.         bmp.compress(Bitmap.CompressFormat.PNG, 100, fOut);  
    14.         try {  
    15.             fOut.flush();  
    16.         } catch (IOException e) {  
    17.             e.printStackTrace();  
    18.         }  
    19.         try {  
    20.             fOut.close();  
    21.         } catch (IOException e) {  
    22.             e.printStackTrace();  
    23.         }  
    24.     }  
    25. }  


            哈哈,看到了吧,还是那句话,难者不会,会者不难,仅仅一句代码,就把保存问题解决了。 

    bmp.compress(Bitmap.CompressFormat.PNG, 100, fOut);


            既然我只知道这一种图像保存的方法,那只能用第二种图像生成的方法了。不要只看表面改动了几行代码,其实这是牵一发而动全身的问题。也怪自己开发经验不足,程序耦合性太高,用第一种绘图方法时候是两个Activity,第二种绘图方法时把绘图这一块自定义了一个View,然后再Layout布局里面加载这个View,这样就不用intent和bundle传递和接受消息了,代码量自然就少了很多。 

  • 相关阅读:
    Mysql:read-only 配置
    Mysql:我自己总也记不住的【选项语法规则】: Command-Line-Options、Configure-Options、System-Variables、Status-Variables
    Mysql:Delayed Replication:延迟复制
    Mysql:Semi-Synchronous Replication:大名鼎鼎的 【半同步】复制
    Mysql:Replication
    Mysql:Group Replication:实战:注意事项
    Mysql:Group Replication
    端口映射工具En化版:PortMap_Gold_Edition_Premium_3.4:可以绑定特定的网卡NIC
    Mysql:SQL Modes:最令我烦的sql-modes:一个标准它不香么?!
    Mysql:Alternative Storage Engines:可替换存储引擎:mysqld的核心特性
  • 原文地址:https://www.cnblogs.com/ghostTao/p/4161171.html
Copyright © 2020-2023  润新知