1、BottomSheetDialog

1.1、创建一个xml布局,用于要展示的BottomSheetDialog样式

1.2、进行实例化,用.show(),将BottomSheetDialog展示出来

val mapBottomSheetDialog = MapBottomSheetDialog(ctx)
mapBottomSheetDialog.show()

1.3、设置BottomSheetDialog的圆角效果

        在style.xml中设置圆角样式:

!--实现BottomSheetDialog圆角效果-->
    <style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
    </style>
    <style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@android:color/transparent</item>
    </style>

       在对应的代码中应用该布局样式:

class MapBottomSheetDialog(
    context: Context,
) : BottomSheetDialog(context, R.style.BottomSheetDialog) {}

1.4、设置固定高度

override fun onStart() {
        super.onStart()
        //拿到系统的 bottom_sheet
        val view: FrameLayout = dialog?.findViewById(R.id.design_bottom_sheet)!!
        //获取behavior
        val behavior = BottomSheetBehavior.from(view)
        //设置弹出高度
        behavior.peekHeight = 350
    }

1.5、去掉背景阴影:

        在style.xml中将backgroundDimEnabled设置为false

<!--实现BottomSheetDialog圆角效果-->
    <style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
<!--去掉背景阴影-->
        <item name="android:backgroundDimEnabled">false</item>
    </style>
    <style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@android:color/transparent</item>
    </style>

2、高德Android地图SDK使用

        Android Studio 配置工程-创建工程-开发指南-Android 地图SDK | 高德地图API

3、权限框架

3.1.1、PermissionsDispatch权限框架

来源参考:GitHub - permissions-dispatcher/PermissionsDispatcher: A declarative API to handle Android runtime permissions.

        在build.gradle(:app)添加依赖:

apply plugin: 'kotlin-kapt'

dependencies {
  implementation "com.github.permissions-dispatcher:permissionsdispatcher:${latest.version}"
  kapt "com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version}"
}

3.1.2、使用@RuntimePermissions 注册一个ActivityorFragment来处理权限

        使用@NeedsPermission 注释需要一个或者多个权限的操作方法

        使用@OnPermissionDenied 注释,如果用户未授权权限则调用该方法

        使用@OnNeverAskAgain 注释,如果用户选择让设备“不再询问”权限则调用该方法

        使用@OnShowRationale 注释,解释为什么需要权限。

3.1.3、委托给生成的函数(第一次未编译前,编译器会报红,直接编译能通过,加注释的方法前不能用private修饰)

@RuntimePermissions
class MainActivity : AppCompatActivity(), View.OnClickListener {

       override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById(R.id.button_camera).setOnClickListener {
           
            showCameraWithPermissionCheck()
        }
    }    

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // NOTE: delegate the permission handling to generated function
        onRequestPermissionsResult(requestCode, grantResults)
    }

@NeedsPermission(Manifest.permission.CAMERA)
    fun showCamera() {
        supportFragmentManager.beginTransaction()
                .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
                .addToBackStack("camera")
                .commitAllowingStateLoss()
    }

    @OnShowRationale(Manifest.permission.CAMERA)
    fun showRationaleForCamera(request: PermissionRequest) {
        showRationaleDialog(R.string.permission_camera_rationale, request)
    }

    @OnPermissionDenied(Manifest.permission.CAMERA)
    fun onCameraDenied() {
        Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show()
    }

    @OnNeverAskAgain(Manifest.permission.CAMERA)
    fun onCameraNeverAskAgain() {
        Toast.makeText(this, R.string.permission_camera_never_askagain, Toast.LENGTH_SHORT).show()
    }
    
}

3.2.1、 ActivityResultLauncher请求权限

3.2.2、在 Activity 或 Fragment 中定义一个 ActivityResultLauncher 对象,并实现 ActivityResultCallback 接口

private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        // 权限已授予
        // 执行相关操作
    } else {
        // 权限被拒绝
        if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.YOUR_PERMISSION)) {
            onNeverAskAgain()
        } else {
            showPermissionRationaleDialog()
        }
    }
}

3.2.3、 在需要请求权限的地方,调用 requestPermissionLauncher.launch(permission) 来发起权限请求。其中permission 为请求的具体权限

3.2.4、当用户做出选择后,系统将调用 ActivityResultCallback 接口中的回调方法。在回调方法中,根据 isGranted 参数判断权限是否被授予。如果 isGranted 为 true,表示权限已授予;如果为 false,表示权限被拒绝

4、GSON解析

4.1、将对象转列表

private fun parseToList(json: String): List<String> {
        val gson = ScaffoldConfig.getGson()
        val a: List<String> = gson.fromJson(json, object : TypeToken<MutableList<String>>() {}.type)
       return a
}

4.2、将字符串转换为对象

fun main() {
    val jsonString = "{\"name\":\"John\",\"age\":30}"

    val gson = Gson()
    val person = gson.fromJson(jsonString, Person::class.java)
}

4.3、将对象转为字符串

data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("John", 30)

    val gson = Gson()

    // 将对象转换为 JSON 字符串
    val jsonString = gson.toJson(person)

}

5、Retrofit使用

5.1、添加依赖

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // 如果要使用 Gson 进行 JSON 数据的解析

5.2、创建API接口

interface MyApi {
    @GET("users/{id}")
    fun getUser(@Path("id") userId: String): Call<User>
}

5.3、创建Retrofit实例

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create()) // 使用Gson进行JSON数据解析
    .build()

 5.4、 创建API实例

val myApi = retrofit.create(MyApi::class.java)

 5.5、发起网络请求

val call = myApi.getUser("123")
call.enqueue(object : Callback<User> {
    override fun onResponse(call: Call<User>, response: Response<User>) {
        if (response.isSuccessful) {
            val user = response.body()
            // 处理响应结果
        } else {
            // 处理错误情况
        }
    }

    override fun onFailure(call: Call<User>, t: Throwable) {
        // 处理请求失败情况
    }
})

6、打开手机自带的选择器

val getContentIntent = Intent(Intent.ACTION_GET_CONTENT)
getContentIntent.type = "image/*" // 设置要获取的内容类型

startActivityForResult(getContentIntent, REQUEST_CODE_GET_CONTENT)

        在onActivityResult回调中处理选择文件返回的结果

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == FILE_SELECT_CODE && resultCode == Activity.RESULT_OK) {
            data?.data?.let { uri ->
                selectedFileUri = uri
                val fileName = getAudioFileName(uri)
                binding.tvResult.text = "Selected File: $fileName"
            }
        }
    }

7、协程

7.1、使用suspend标记支持协程的方法

interface VoiceToTextService {
    @Headers(
        "Domain-Name: $VOICE_TO_TEXT_KEY",
        "Authorization: Bearer $VOICE_TO_TEXT_TOKEN",
        "Content-Type: application/octet-stream"
    )
    @POST("models/XXX/XXXX")
    suspend fun voiceConvertText(@Body requestBody: RequestBody): String
}

7.2、使用协程调用接口方法

private fun convertAudio() {
    CoroutineScope(Dispatchers.IO).launch {
            try {
                val mediaTypeBinary = "application/octet-stream".toMediaTypeOrNull()
                val file = File(uriToPath(mContext, selectedFileUri).toString())
                if (file.exists()){
                    val requestBody: RequestBody =
                        RequestBody.create(mediaTypeBinary, file.readBytes())
                    withContext(Dispatchers.Main){
                        val result = voiceToService.voiceConvertText(requestBody)
                        val gson = Gson()
                        val apiResponse = gson.fromJson(result, VoiceToTextResponse::class.java)
                        if (apiResponse != null) {
                            val text = apiResponse.text
                            binding.tvResult.text = text
                        }
                    }
                }
            }catch (e: Throwable) {
                e.printStackTrace()
            }
        }
}