Android kotlin相冊和照相功能

開發工具:Android Studio3.3.1 sdk=28.0.0 gradle=gradle-4.10.1-all.zip

時間:2019/2/28

編程語言:Kotlin

功能:拍照和從相冊選擇相片

測試手機:華爲榮耀8,Android version 8.0.0

參考文獻:

https://www.cnblogs.com/zhangqie/p/7562959.html
https://www.jianshu.com/p/41b093d213fb

GitHub: https://github.com/SAKURA96/PhotoCameraTest2

效果圖:

簡述:三個主要部分是Android動態權限申請,獲取相冊和拍照功能。未使用第三方庫。

個人吐槽:我搜了很多的拍照的代碼都是直接閃退,都是因爲權限部分沒有寫好。或者寫了但是不太理想,有時候一個權限拒絕後麪點擊按鈕就直接閃退,第三方庫也不太適用,總是隻有一個成功的回調方法。可是我這裏有兩個功能啊,我水平有限寫不出一個成功回調能獨立解決兩個事件響應。所以就自己寫權限的申請,已經被申請就立即執行相關功能。這個代碼不夠成熟,在活動請求的回調中,有兩處警告,望各位懂的大佬可以指正一下。


GO:

1 Manifest中加入權限,和provider

    <!--讀寫內存塊權限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--調用相機權限-->
    <uses-permission android:name="android.permission.CAMERA"/>
 <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="com.photocameratest2.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
            <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
        </provider>

 

2 app/res/xml 中新建file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

 

3 新建工具類GetPhotoFromAlbum

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

// Content:從相冊內獲取照片轉化工具類

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

    }

}

4 主佈局 activity_main,兩個Button,一個ImageView.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical">

    <LinearLayout android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="horizontal">

        <Button
                android:id="@+id/btn_album"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:text="調用相冊"/>

        <Button
                android:id="@+id/btn_camera"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:text="調用相機"/>

    </LinearLayout>

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

        <ImageView
                android:id="@+id/iv_image_view"
                android:layout_gravity="center"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="15dp"
                android:scaleType="fitXY"
                android:adjustViewBounds="true"
                tools:src="@mipmap/ic_launcher"/>

    </LinearLayout>

</LinearLayout>

5 在MainActivity中

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v4.content.FileProvider
import android.support.v7.app.AppCompatActivity
import android.widget.ImageView
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File


class MainActivity : AppCompatActivity() {

    //設置權限請求和活動請求的請求碼requestCode
    companion object {
        private const val PERMISSIONS_REQUEST_ALBUM = 1
        private const val PERMISSIONS_REQUEST_CAMERA = 2

        private const val ACTIVITY_REQUEST_ALBUM = 3
        private const val ACTIVITY_REQUEST_CAMERA = 4
    }

    lateinit var cameraSavePath: File
    lateinit var uri: Uri
    lateinit var imageView: ImageView

    //拍照需要的兩個權限
    private val permissionList = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
    //存儲用戶拒絕授權的權限
    var permissionTemp: ArrayList<String> = ArrayList()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        imageView = findViewById(R.id.iv_image_view)

        //拍照的文件存儲位置
        cameraSavePath =
            File(Environment.getExternalStorageDirectory().path + "/" + System.currentTimeMillis() + ".jpg")

        initListener()
    }

    //設置兩個button的監聽
    private fun initListener() {

        //相冊監聽,檢查一個權限
        btn_album.setOnClickListener {
            //檢查版本是否大於M
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (ContextCompat.checkSelfPermission(
                        this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                    ) != PackageManager.PERMISSION_GRANTED
                ) {
                    ActivityCompat.requestPermissions(
                        this,
                        arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                        PERMISSIONS_REQUEST_ALBUM
                    )
                } else {
                    //權限已經被授權,開啓相冊
                    goAlbum()
                }
            }
        }

        //拍照監聽,檢查兩個權限
        btn_camera.setOnClickListener {
            permissionTemp.clear()
            for (i in permissionList.indices) {
                if (ContextCompat.checkSelfPermission(
                        this,
                        permissionList[i]
                    ) != PackageManager.PERMISSION_GRANTED
                ) {
                    permissionTemp.add(permissionList[i])
                }
            }
            if (permissionTemp.isEmpty()) {
                //未授予的權限爲空,表示都授予了,開啓照相功能
                goCamera()
            } else {//請求權限方法
                val permissions = permissionTemp.toTypedArray()//將List轉爲數組
                ActivityCompat.requestPermissions(
                    this,
                    permissions,
                    PERMISSIONS_REQUEST_CAMERA
                )
            }
        }
    }

    //權限結果回調
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {

        when (requestCode) {

            //相冊權限請求結果
            PERMISSIONS_REQUEST_ALBUM -> {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    goAlbum()
                } else {
                    Toast.makeText(this, "你拒絕了讀取相冊權限", Toast.LENGTH_SHORT).show()
                }
            }

            //拍照權限請求結果
            PERMISSIONS_REQUEST_CAMERA -> {
                //用於判斷是否有未授權權限,沒有則開啓照相
                var isAgree = true
                for (i in grantResults.indices) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        //檢查到有未授予的權限
                        isAgree = false
                        //判斷是否勾選禁止後不再詢問
                        val showRequestPermission =
                            ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])
                        if (showRequestPermission) {
                            Toast.makeText(this, "你拒絕了拍照相關權限", Toast.LENGTH_SHORT).show()
                        }
                    }
                }
                //isAgree沒有被置爲false則表示權限都已授予,開啓拍照
                if (isAgree) {
                    goCamera()
                }
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    //相冊功能
    private fun goAlbum() {
        val intent = Intent()
        intent.action = Intent.ACTION_PICK
        intent.type = "image/*"
        startActivityForResult(intent, ACTIVITY_REQUEST_ALBUM)
    }

    //拍照功能
    private fun goCamera() {

        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            uri = FileProvider.getUriForFile(this, "com.photocameratest2.fileprovider", cameraSavePath)
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

        } else {
            uri = Uri.fromFile(cameraSavePath)
        }
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
        this.startActivityForResult(intent, ACTIVITY_REQUEST_CAMERA)
    }

    //活動請求的回調,用requestCode來匹配
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

        //圖片路徑
        var photoPath: String = ""

        //相冊
        if (requestCode == ACTIVITY_REQUEST_ALBUM && resultCode == Activity.RESULT_OK) {

            photoPath = GetPhotoFromAlbum.getRealPathFromUri(this, data!!.data)!!
            imageView.setImageURI(Uri.parse(photoPath))

            //拍照
        } else if (requestCode == ACTIVITY_REQUEST_CAMERA && resultCode == Activity.RESULT_OK) {

            photoPath = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                cameraSavePath.toString()
            } else {
                uri.encodedPath
            }
            imageView.setImageURI(Uri.parse(photoPath))
        }
        super.onActivityResult(requestCode, resultCode, data)
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章