Android-Notes

安卓笔记

Posted by Ryan Yim on 2021-01-21
Estimated Reading Time 6 Minutes
Words 1.2k In Total
Viewed Times

Android

Activity跳转Fragment

1
2
3
4
5
6
binding.fab.setOnClickListener { view ->
// 跳转fragment界面(方法1) 这个只能跳转界面,没有实际功能
setContentView(R.layout.fragment_qr_scanner)
// 跳转fragment界面(方法2 建议的) 这个可以跳转代码(当你fragment从依赖包继承的时候)
supportFragmentManager.beginTransaction().add(android.R.id.content, QrScannerFragment()).commit()
}

使用XXPermissions请求权限

https://github.com/getActivity/XXPermissions

首先添加远程仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dependencyResolutionManagement {
repositories {
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
}
}

# 如果是settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// JitPack 远程仓库:https://jitpack.io
maven {
url = uri("https://jitpack.io")
}
}
}

然后引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
android {
// 支持 JDK 1.8 及以上
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
// 权限请求框架:https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:18.5'
}

# build.gradle.kts
// 权限请求框架:https://github.com/getActivity/XXPermissions
implementation("com.github.getActivity:XXPermissions:18.5")

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
XXPermissions.with(this)
// 申请单个权限
.permission(Permission.CAMERA)
.request(object : OnPermissionCallback {
override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
if (!allGranted) {
println("获取部分权限成功,但部分权限未正常授予")
return
}
println("获取相机权限成功")
}

override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
if (doNotAskAgain) {
println("被永久拒绝授权,请手动授予相机权限")
// 如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.startPermissionActivity(applicationContext, permissions)
} else {
println("获取相机权限失败")
}
}
})

安卓启用储存

1
2
3
4
5
6
7
8
9
直接在main activity中:
protected void onCreate(Bundle savedInstanceState) 中:
// 权限申请
val permission = arrayOf(
"android.permission.INTERNET",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE"
)
Permissions.checkPermission(this, permission)

Permissions.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package cn.rmshadows.weatherpost

import android.app.Activity
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat

object Permissions {
fun checkPermission(activity: Activity, permission: Array<String?>) {
val noPermission = arrayOfNulls<String>(permission.size)
val j = 0
for (i in permission.indices) {
if (activity.checkSelfPermission(permission[i]!!) == PackageManager.PERMISSION_DENIED) {
if (permission[i] != null) {
requestPermission(
arrayOf(
permission[i]
), activity
)
}
}
}
}

private fun requestPermission(s: Array<String?>, activity: Activity) {
ActivityCompat.requestPermissions(activity, s, 100)
}
}

启用视图绑定(Data Binding)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kotlin项目:
build.gradle.kts
android {
...
viewBinding {
isEnabled = true
}
}

或者build.gradle中:
android {
...
viewBinding {
enable = true
}
}

二维码扫描CameraScan / ZXing-Lite

https://github.com/jenly1314/CameraScan

https://github.com/jenly1314/ZXingLite

1
2
// CameraScan
implementation("com.github.jenly1314:camera-scan:1.0.1")

使用(TODO):

1
2
3
4
5
```

## Fragment之间的通讯

>https://developer.android.com/guide/fragments/communicate?hl=zh-cn#fragment-result
1
2
3
4

## Fragment Dialog返回值到Fragment

DialogFragment中设置接口,然后在fragment实现接口以及传入context

class InputPortNumberDialogFragment : DialogFragment() {
// 在这里定义接口,在要显示DialogFragment的Fragment中实现这个接口
interface OnInputPortReceived {
fun onInputPortReceived(input: String)
}
// 接口
private var onInputReceived: OnInputPortReceived? = null

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    //为了样式统一和兼容性,可以使用 V7 包下的 AlertDialog.Builder
    val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
    // 设置主题的构造方法
    val inflater: LayoutInflater = requireActivity().getLayoutInflater()
    val view: View = inflater.inflate(R.layout.fragment_input_dialog, null)
    builder.setView(view)
        .setMessage("请输入端口号(输入0默认54300):")
        .setPositiveButton("确定") { dialog, which ->
            val et = view.findViewById<EditText>(R.id.inputEditText)
            var portInput:String = et.text.toString()
            if (! et.text.isEmpty()) {
                if (portInput.equals("0")){
                    portInput = "54300"
                }
                // 调用接口
                onInputReceived?.onInputPortReceived(portInput)
                dismiss() // 关闭对话框
            }
        }
        .setNegativeButton("取消", null)
    return builder.create()
}

// 接收从Fragment中传过来的接口
fun setOnInputListener(listener: OnInputPortReceived) {
    onInputReceived = listener
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.fragment_input_dialog, container, false)
}

companion object {
    const val TAG = "getPortDialog"
}

}

1
2

Fragment:

package cn.rmshadows.textsend.fragments

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import cn.rmshadows.textsend.R
import cn.rmshadows.textsend.databinding.FragmentServerBinding
import cn.rmshadows.textsend.viewmodels.ServerFragmentViewModel
import cn.rmshadows.textsend.viewmodels.TextsendViewModel
import kotlinx.coroutines.launch
import utils.Constant
import java.util.LinkedList

/**

  • A simple [Fragment] subclass as the second destination in the navigation.
    */
    class ServerFragment : Fragment(), InputPortNumberDialogFragment.OnInputPortReceived,
    InputIpAddressDialogFragment.OnInputIpReceived {
    private var _binding: FragmentServerBinding? = null
    private val TAG = Constant.TAG

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!
    private lateinit var adapter: ArrayAdapter

    // https://developer.android.com/codelabs/basic-android-kotlin-training-shared-viewmodel?hl=zh-cn#4
    private val tsviewModel: TextsendViewModel by activityViewModels()
    private lateinit var viewModel: ServerFragmentViewModel

    // 实现接口
    override fun onInputIpReceived(input: String) {

     val oips = viewModel.uiState.value.netIps
     oips.removeLast()
     oips.add(input)
     oips.add(viewModel.CUSTOM_INPUT_FLAG)
     viewModel.update(input, null, oips, null, null)
    

    }

    override fun onInputPortReceived(input: String) {

     viewModel.update(null, input, null, null, null)
    

    }

    override fun onCreateView(

     inflater: LayoutInflater, container: ViewGroup?,
     savedInstanceState: Bundle?
    

    ): View {

     _binding = FragmentServerBinding.inflate(inflater, container, false)
     tsviewModel.update(true, null, null)
     viewModel = ViewModelProvider(this).get(ServerFragmentViewModel::class.java)
     return binding.root
    

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

     super.onViewCreated(view, savedInstanceState)
    
     binding.serverStartBtn.setOnLongClickListener {
         // 长按修改端口号
         val df = InputPortNumberDialogFragment()
         // 传入接口实现
         df.setOnInputListener(this)
         df.show(childFragmentManager, InputPortNumberDialogFragment.TAG)
         true
     }
    

    }

    override fun onDestroyView() {

     super.onDestroyView()
     _binding = null
    

    }
    }

    1
    2

    `repeatOnLifecycle(Lifecycle.State.STARTED)`报错`The repeatOnLifecycle API should be used with viewLifecycleOwner`
     lifecycleScope.launch {
         repeatOnLifecycle(Lifecycle.State.STARTED) {
             viewModel.uiState.collect {
                 binding.clientIpAddress.setText(it.serverIp)
                 binding.clientPort.setText(it.serverPort.toString())
             }
         }
     }
    
    1
    2

    解决:`lifecycleScope.launch`=>`viewLifecycleOwner.lifecycleScope.launch`

    viewLifecycleOwner.lifecycleScope.launch {

         repeatOnLifecycle(Lifecycle.State.STARTED) {
             viewModel.uiState.collect {
                 binding.clientIpAddress.setText(it.serverIp)
                 binding.clientPort.setText(it.serverPort.toString())
             }
         }
     }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    # Kotlin

    ## Kotlin类变量

    >https://www.baeldung.com/kotlin/companion-object
    >
    >https://medium.com/@ansujain/kotlin-how-to-create-static-members-for-class-543d0f126f7c

    ```kotlin
    class ClassName {
    companion object {
    const val propertyName: String = "Something..."
    fun funName() {
    //...
    }
    }
    }

    class ClassName {
    companion object {
    const val propertyName: String = "Something..."
    fun funName() {
    //...
    }
    }
    }

If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !