美文网首页简化开发
扫码 项目总结-v1.1.0

扫码 项目总结-v1.1.0

作者: 番茄tomato | 来源:发表于2020-07-07 14:59 被阅读0次
总结

一. 选择本地图片进行二维码扫描

不得不说BGAQRCode真的是功能强大,直接有一个方法就是扫描本地图片或者bitmap,

decodeQRCode
也就是说只要传入本地图片的地址,或者生成的bitmap对象,就可以进行解析,回调onScanQRCodeSuccess方法。
所以需要做的就是选择本地图片得到图片地址或者生成bitmap
第一步: selectLocalPic()函数 调用系统相册选择图片,明确需要选择的文件是image类型,然后使用startActivityForResult函数传入intent和requsetCode启动选择图片的activity(不同系统界面会有区别)
    //选择本地图片
    private fun selectLocalPic() {
        val intent = Intent("android.intent.action.GET_CONTENT")
        intent.type = "image/*"
        startActivityForResult(intent, SELECT_LOCAL_IMAGE)
    }

第二步:onActivityResult()中获取选择图片的uri(注意 这个uri并非图片地址)

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            SELECT_LOCAL_IMAGE -> {
                if (resultCode == Activity.RESULT_OK) {
                    val selectedImage = data?.data
                    selectedImage?.let {
                        scanLocalPic(it)
                    }
                }
            }
        }
    }

第三步:根据图片uri解析图片地址,并进行扫描:
(难点)

    //扫描本地图片
    private fun scanLocalPic(uri: Uri) {
        val path = GetPhotoFromAlbum.getRealPathFromUri(this, uri)
        Log.d("图片path", "$path")
        scanView.decodeQRCode(path)
    }

这里是一个难点,首先java和kotlin的处理方式不同,使用java的解析uri方式,在kotlin中为null,并且对于不同api设备,处理方式也不同。所以我在网上找到了一个工具类GetPhotoFromAlbum
原地址在这里:
https://blog.csdn.net/qq_37216116/article/details/88045161
用于kotlin开发中将图片uri解析为绝对路径:

import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.provider.DocumentsContract
import android.provider.MediaStore

object GetPhotoFromAlbum {


    /**
     * 根据Uri获取图片的绝对路径
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */
    fun getRealPathFromUri(context: Context, uri: Uri): String? {

        val sdkVersion = Build.VERSION.SDK_INT

        return if (sdkVersion >= 19) { // api >= 19

            getRealPathFromUriAboveApi19(context, uri)

        } else { // api < 19

            getRealPathFromUriBelowAPI19(context, uri)

        }

    }


    /**
     * 适配api19以下(不包括api19),根据uri获取图片的绝对路径
     *
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */

    private fun getRealPathFromUriBelowAPI19(context: Context, uri: Uri): String? {
        return getDataColumn(context, uri, null, null)
    }


    /**
     * 适配api19及以上,根据uri获取图片的绝对路径
     *
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */

    @SuppressLint("NewApi")
    private fun getRealPathFromUriAboveApi19(context: Context, uri: Uri): String? {

        var filePath: String? = null

        if (DocumentsContract.isDocumentUri(context, uri)) {

            // 如果是document类型的 uri, 则通过document id来进行处理
            val documentId = DocumentsContract.getDocumentId(uri)

            if (isMediaDocument(uri)) { // MediaProvider
                // 使用':'分割
                val id = documentId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
                val selection = MediaStore.Images.Media._ID + "=?"
                val selectionArgs = arrayOf(id)
                filePath =
                    getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs)

            } else if (isDownloadsDocument(uri)) { // DownloadsProvider

                val contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    java.lang.Long.valueOf(documentId)
                )
                filePath = getDataColumn(context, contentUri, null, null)
            }

        } else if ("content".equals(uri.scheme!!, ignoreCase = true)) {

            // 如果是 content 类型的 Uri

            filePath = getDataColumn(context, uri, null, null)

        } else if ("file" == uri.scheme) {

            // 如果是 file 类型的 Uri,直接获取图片对应的路径
            filePath = uri.path
        }
        return filePath
    }


    /**
     * 获取数据库表中的 _data 列,即返回Uri对应的文件路径
     */

    private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?): String? {

        var path: String? = null
        val projection = arrayOf(MediaStore.Images.Media.DATA)
        var cursor: Cursor? = null

        try {
            cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
            if (cursor != null && cursor.moveToFirst()) {
                val columnIndex = cursor.getColumnIndexOrThrow(projection[0])
                path = cursor.getString(columnIndex)
            }

        } catch (e: Exception) {
            cursor?.close()
        }

        finally {
            cursor?.close()
        }
        return path
    }


    /**
     * @param uri the Uri to check
     *
     * @return Whether the Uri authority is MediaProvider
     */

    private fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }

    /**
     * @param uri the Uri to check
     *
     * @return Whether the Uri authority is DownloadsProvider
     */

    private fun isDownloadsDocument(uri: Uri): Boolean {
        return "com.android.providers.downloads.documents" == uri.authority
    }
}

第四步:onScanQRCodeSuccess中做好判断,因为选择图片扫码的话,结果可能是空的,提示“未识别到二维码”

二. 选择本地图片进行扫码所需要的权限请求

在1.0的开发中也提到了权限请求,当时只有一个相机权限,但是现在增加了文件读写权限,这里也作一下修改,使用官方权限请求框架EasyPermissions
参考文章:https://www.jianshu.com/p/886a1ffb02b9
第一步:在Activity中实现EasyPermissions.PermissionCallbacks接口,并且在onRequestPermissionsResult中添加一句话EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);

 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        // Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    //成功
    @Override
    public void onPermissionsGranted(int requestCode, List<String> list) {
        // Some permissions have been granted
        // ...
    }

    //失败
    @Override
    public void onPermissionsDenied(int requestCode, List<String> list) {
        // Some permissions have been denied
        // ...
    }

第二步:准备好需要请求的权限列表:

   private val perms = arrayOf(
        //外部文件读写权限和相机权限
        Manifest.permission.CAMERA,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE
    )

第三步:检查并请求权限(onCreate中调用checkAndRequestPermission):

    private fun checkAndRequestPermission() {
        //检查是否有权限
        if (EasyPermissions.hasPermissions(this, *perms)) {
            isPermissionAuth = true
        } else {
            //请求权限 四个参数:回调对像实现接口的activity 权限请求对话框标题提示 权限请求码 需要请求的权限
            EasyPermissions.requestPermissions(this, "", PERMS_REQUEST_CODE, *perms)
        }
    }

第四步onPermissionsGranted(申请通过)/onPermissionsDenied(权限拒绝)回调中完成相应操作

第五步 以上过程都完成了 读写权限请求成功 但是选择图片时还是报错说没有权限?
原因:AndroidQ以下,还是使用老的文件存储方式。Android Q仍然使用READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE作为存储相关运行时权限,但现在即使获取了这些权限,访问外部存储也受到了限制。
解决办法:在manifest的applicaiton标签添加 android:requestLegacyExternalStorage="true"
参考:https://blog.csdn.net/MarketAndTechnology/article/details/105955441

三. 使用Android本地数据库保存扫码记录

这个需求涉及到数据库的使用了,原本打算用SQLite的,但是这玩意封装起来很烦,突然发现公司的框架中用到的是Anko
文档地址:https://github.com/Kotlin/anko/wiki/Anko-SQLite
我总结的文章:
https://www.jianshu.com/p/f4f42950ec69

四. 项目多语言

项目默认语言为英语,现在要添加日语,操作步骤如下:
1.右键res->New->Android Resouce File


image.png
image.png
image.png
image.png

5.点击OK后就可以在res目录中看到对应的文件了


image.png

需要注意的是,程序中用到的字段都需要在Strings中声明,并且特定地区的Strings也需要有同样的字段不同语言的翻译。如果某个字段在默认的String中有,而特点地区中没有,IDE会给出警告并使用默认内容。相反,如果特定地区中有的字段,而默认String中没有 则会报错。

补充点:如果想根据国家和地区显示不同图片怎么操作?
比入我这里有两张gif,分别是英文和日文,这个gif需要在启动页一开始进行播放

1
2
同理:英文gif作为默认选择,放在res下的drawable中命名为start_page_gif.gif。那么此时需要新建日本地区的drawable文件夹,按以下步骤:
res右键--->new--->Android Resource Directory
image.png
确定资源文件夹类型
image.png
选择好国家或地区后文件夹会自动命名
image.png
点击ok,将同名的日文gif资源文件放在这个文件夹下,系统会自动根据当前语言地区选择对应资源文件:
image.png
当然也要注意默认的资源必须存在

五. 状态栏全黑问题

为了隐藏掉每个界面自带的appbar,使用了以下主题:

    <style name="AppTheme.FullScreen" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
    </style>

<item name="windowNoTitle">true</item> 去掉标题栏
<item name="android:windowFullscreen">true</item>隐藏状态栏

当添加了隐藏状态栏的代码后,状态栏全黑,无内容。所以把这句话删了,但是此时出现一个问题:状态栏内容正常显示,但是却挡住了界面内容

image.png
其实只需要再在界面根布局中加一句话就行了:
android:fitsSystemWindows="true"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:fitsSystemWindows="true">

六. 其他细节点

6.1 约束布局形成链之后,链头约束的定位改变时,各个控件也会改变,会有一个很舒服的过渡动画,自己看图,再吹一波约束布局
约束布局
这里主界面根布局中加了android:animateLayoutChanges="true"这句话,所以控件的消失会渐入渐出
6.2 xml完成卡片背景

https://blog.csdn.net/qq1171574843/article/details/89386733
关于xml生成背景比如圆角 阴影这些 待学习(和动画比起来应该不难吧,花1小时掌握 2020-7-9)

6.3 修改项目包名

不要傻乎乎的去修改项目的文件夹路径名称,吃力不讨好,包名就像是项目的身份证号码,在module的build.gradle文件的defaultConfig下修改applicationId

image.png
6.4 关于报错

Error: Cannot fit requested classes in a single dex file (# methods: 66190 > 65536)
原因是引入SDK后 方法太多大于限制65536,解决方法:
https://blog.csdn.net/hanshiying007/article/details/105219378/
gradle文件的defaultConfig默认配置里面增加:
multiDexEnabled true
在dependencies中添加:
implementation 'com.android.support:multidex:1.0.3'
或者(开启androidx的话)
implementation 'androidx.multidex:multidex:2.0.1'
自定义的Application的onCreate()中添加:
MultiDex.install(this);

相关文章

  • 扫码 项目总结-v1.1.0

    一. 选择本地图片进行二维码扫描 不得不说BGAQRCode真的是功能强大,直接有一个方法就是扫描本地图片或者bi...

  • 扫码 项目总结-V 1.2.3

    不知不觉入职已经两个月了,到今天其实这个扫码项目V1.3.0已经开发完成了,正在测试中。有好一段时间没有作总结了,...

  • Android 二维码扫描(ZXing)

    项目中用到了扫码功能,度了一下,总结一哈!(都是在官方ZXing基础上动刀子,貌似Android的扫码都是在ZXi...

  • 使用扫码枪输入字符的总结

    项目中的要使用扫码枪录入商品条码,读取微信、支付宝的付款码。在调试的过程中,遇到一些问题,在这里做个总结。 扫码枪...

  • iOS-扫码带给我的坑

    公司很多项目是有扫描二维码的功能的,例如扫码签到、扫码添加好友、扫码安装相应App等等。公司项目采用了QRCode...

  • 二维码分享跳转的配置与实现

    引言: 上篇总结了生成带有二维码的海报图,现在总结一下扫码的后续流程不管主动扫码还是长按识别扫码,都需要到微信小程...

  • weex☞扫码(开灯)

    项目需要增加扫码功能 weex playground中已经实现了扫码功能,weex项目增加的Android依赖中同...

  • 扫码 项目总结-V1.3.0

    好吧,其实V1.3.5 已经在上个月下旬做完了,现在才来写总结,但凡勤快点都不会拖到现在,害呀。这里标题是V1.3...

  • 扫码 项目总结-V1.0.0

    入职新公司,许久没有写过文章了,上周二入职,到本周五,总算是把一个项目做完了。今天周日,还有一些遗留细节改完,有一...

  • iOS 高仿微信扫一扫

    日常项目开发中,经常会出现类似扫码加好友、扫码登录或者扫码支付等功能。SWQRCode 高仿微信扫一扫功能,支持二...

网友评论

    本文标题:扫码 项目总结-v1.1.0

    本文链接:https://www.haomeiwen.com/subject/beaoqktx.html