持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第十天,点击查看活动详情
Fragment优雅的申明权限的几种方式
前言
Fragment是在开发中除了Activity最常见的一种界面,Fragment可以定义和管理自己的布局,具有自己的生命周期。在开发中经常使用Fragment,在我们不使用第三方框架的情况下,我们应该如何进行对权限的申明呢?
前期准备
权限
我们在动态申明权限的时候,我们需要在AndroidManifest.xml清单中进行静态的申明,我们就以常见的读写权限为例
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
为什么需要限制在SDK32的版本?
在SDK33的版本中,
READ_EXTERNAL_STORAGE的权限已经被细分成READ_MEDIA_IMAGE、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO
本篇将以在Android12(SDK32)的情况下为例,进行对读写权限的申明
简单封装一下ViewBinding
为方便代码的编写,我们先简单的封装一下ViewBinding
- 在
build.gradle(app)中添加一下以下代码以表使用ViewBinding
android {
...
buildFeatures {
viewBinding true //使用viewbinding
}
...
}
- 简单封装一下BaseFragment
package com.softdrink.emotional.myapplication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseFragment<VB : ViewBinding> : Fragment() {
lateinit var _vb: VB
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_vb = initVB()
return _vb.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initSome()
}
abstract fun initVB(): VB
abstract fun initSome()
}
- MainActivity的代码,进行对OneFragment的显示
package com.softdrink.emotional.myapplication
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.softdrink.emotional.myapplication.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var _vb: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_vb = ActivityMainBinding.inflate(layoutInflater)
setContentView(_vb.root)
initView()
}
lateinit var currentFragment: Fragment
fun initView() {
//三种方式的对应的Fragment
val one = OneFragment()
val two = TwoFragment()
val three = ThreeFragment()
//通过transaction添加fragment
val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.id.frameLayout, one, "one")
transaction.add(R.id.frameLayout, two, "two")
transaction.add(R.id.frameLayout, three, "three")
//默认显示OneFragment
transaction.hide(two)
transaction.hide(three)
transaction.show(one)
transaction.commit()
currentFragment = one
//注册点击事件进行切换
_vb.button.setOnClickListener {
val transaction = supportFragmentManager.beginTransaction()
if (currentFragment != one) {
transaction.hide(currentFragment)
transaction.show(one)
transaction.commit()
currentFragment = one
}
}
_vb.button2.setOnClickListener {
val transaction = supportFragmentManager.beginTransaction()
if (currentFragment != two) {
transaction.hide(currentFragment)
transaction.show(two)
transaction.commit()
currentFragment = two
}
}
_vb.button3.setOnClickListener {
val transaction = supportFragmentManager.beginTransaction()
if (currentFragment != three) {
transaction.hide(currentFragment)
transaction.show(three)
transaction.commit()
currentFragment = three
}
}
}
}
- MainActivity的布局文件代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
//显示Fragment
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
//三种方式的切换
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="方式一"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="方式二"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button3"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="方式三"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button2" />
</androidx.constraintlayout.widget.ConstraintLayout>
通过以上代码我们可以看到如下的效果
方式一
我们知道Fragment是不能独立显示,必须要依附在Activity才能显示,所以我们可以在Fragment进去请求权限,借助Activity来进行对权限的结果的监听,并把结果返回给Fragment
- OneFragment 进行权限的请求
package com.softdrink.emotional.myapplication
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.softdrink.emotional.myapplication.databinding.FragmentOneBinding
class OneFragment : BaseFragment<FragmentOneBinding>() {
companion object {
//权限请求码
const val permissionCode = 100
}
override fun initVB(): FragmentOneBinding = FragmentOneBinding.inflate(layoutInflater)
override fun initSome() {
_vb.btnStart.setOnClickListener {
requestPermission()
}
}
//申请的权限
val allPermission = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
fun requestPermission() {
//判断权限是否已经同意
if (ContextCompat.checkSelfPermission(
this.requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
this.requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
)
!= PackageManager.PERMISSION_GRANTED
) {
//请求权限
requireActivity().requestPermissions(allPermission, permissionCode)
} else {
Toast.makeText(requireContext(), "OneFragment Permissions granted", Toast.LENGTH_SHORT).show()
}
}
/**
* 权限的结果回调,这里是给mainActivity调用
*
* @param isOk 返回结果
*/
fun permissionResultCall(isOk: Boolean) {
Toast.makeText(
requireContext(),
if (isOk) " OneFragment Permissions granted" else "OneFragment Permissions not granted",
Toast.LENGTH_SHORT
).show()
}
}
我们还需要在MainActivity中添加一下代码
//重写权限的结果回调
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == OneFragment.permissionCode) {// 判断请求码
//调用OneFragment的permissionResultCall
one.permissionResultCall(grantResults[0] == PackageManager.PERMISSION_GRANTED)
}
}
效果图:
方式二
我们在方法一中,我们借助了Activity来进行权限的请求,那么我们有什么办法不需要Activity来进行权限的请求呢?当然有,那么我们直接看代码
package com.softdrink.emotional.myapplication
import android.Manifest
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.softdrink.emotional.myapplication.databinding.FragmentTwoBinding
class TwoFragment : BaseFragment<FragmentTwoBinding>() {
override fun initVB(): FragmentTwoBinding {
return FragmentTwoBinding.inflate(layoutInflater)
}
override fun initSome() {
_vb.btnTwo.setOnClickListener {
requestPermission()
}
}
val twoCode = 100
val allPermission = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
fun requestPermission() {
if (ContextCompat.checkSelfPermission(
this.requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
this.requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
)
!= PackageManager.PERMISSION_GRANTED
) {
//请求权限 已经被遗弃了
requestPermissions(allPermission, OneFragment.permissionCode)
} else {
Toast.makeText(requireContext(), "TwoFragment Permissions granted", Toast.LENGTH_SHORT)
.show()
}
}
//重写回调
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == twoCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
requireContext(),
"TwoFragment Permissions granted",
Toast.LENGTH_SHORT
)
.show()
} else {
Toast.makeText(
requireContext(),
"TwoFragment Permissions not granted",
Toast.LENGTH_SHORT
)
.show()
}
}
}
}
我们发现方法一与方法二的区别在于:
方法一:调用的是Activity的requestPermissions
方法二:调用是Fragment的requestPermissions
这两种方式都不推荐使用,官方推荐使用方式三
方式三
该方式的是官方的推荐的一种方式,具体请看
该方式是通过一种回调的方式来进行获取
package com.softdrink.emotional.myapplication
import android.Manifest
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import com.softdrink.emotional.myapplication.databinding.FragmentThreeBinding
class ThreeFragment : BaseFragment<FragmentThreeBinding>() {
override fun initVB(): FragmentThreeBinding {
return FragmentThreeBinding.inflate(layoutInflater)
}
override fun initSome() {
_vb.btnThree.setOnClickListener {
requestPermission()
}
}
fun requestPermission() {
if (ContextCompat.checkSelfPermission(
this.requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
Toast.makeText(requireContext(), "ThreeFragment WRITE Permissions granted", Toast.LENGTH_SHORT)
.show()
}
}
//以接口的形式来进行回调的监听
val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
Toast.makeText(
requireContext(),
"ThreeFragment Permissions Granted",
Toast.LENGTH_SHORT
)
.show()
} else {
Toast.makeText(
requireContext(),
"ThreeFragment Permissions Not Granted",
Toast.LENGTH_SHORT
)
.show()
}
}
}