Android 开发一直处于「面向谷歌编程, 下一次又不知道该怎么写」的状态 (指不会 startActivity). 前些天重写了一个东西, 又用到了一些奇奇怪怪的技巧. 遂记录, 供日后直接 copy.
可以省去 findViewById 这种繁琐的操作. (虽然实际上还是调用的 findViewById)
需要先在 build.grade
里, android 条目下加一条:
1
2
3
4
5
6
7
|
android {
...
viewBinding {
enabled = true
}
...
}
|
假如有一个 layout 名为 example_layout.xml
, 编译之后, 它会生成一个驼峰重命名加上 Binding
的类, 如 ExampleLayoutBinding
. 获得实例化的引用以后就可以直接用 id 名获得 view 的引用了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class ExampleActivity : AppCompatActivity() {
private lateinit var binding: ActivityExampleBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i("Home Activity", "onCreate: ")
binding = ActivityHomeBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
fun useBinding() {
binding.text_view_example.setText("Hello View Binding!")
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class ExampleFragment : Fragment() {
private var _binding: FragmentExampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentExampleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
fun useBinding() {
binding.text_view_example.setText("Hello View Binding!")
}
}
|
需要注意的是记得在 onDestroyView()
中清除引用.
没学过 kotlin 不会委托, 反正用 by 写 ViewModel 的实例化很方便, 就记着先.
1
2
3
4
5
6
7
8
9
|
class ExampleActivity : AppCompatActivity() {
private val viewModel : ExampleViewModel by viewModels()
fun useViewModle() {
viewModle.fun()
}
}
|
1
2
3
4
5
6
7
8
9
|
class ExampleFragment : Fragment() {
private val viewModel : ExampleViewModel by activityViewModels()
fun useViewModle() {
viewModle.fun()
}
}
|
ViewModel 中处理数据, Fragment 或者 Activity 中观察数据的变化, 然后重绘 UI. 实现数据处理和 UI 绘制分离的 MVC 模式.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class ExampleViewModel : ViewModel() {
private val userName: MutableLiveData<String> by lazy { MutableLiveData() }
fun setName(name : String) {
userName.postValue(name)
}
fun getUserName() : LiveData<String> {
return userName
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
class ExampleActivity : AppCompatActivity() {
private val viewModel : ExampleViewModel by viewModels()
fun observeUserName() {
viewModel.getUserName.observe(this) {
Toast.makeText(requireContext(), "$it", Toast.LENGTH_LONG).show()
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
class ExampleFragment : Fragment() {
private val viewModel : ExampleViewModel by activityViewModels()
fun observeUserName() {
viewModel.getUserName.observe(viewLifecycleOwner) {
Toast.makeText(requireContext(), "$it", Toast.LENGTH_LONG).show()
}
}
}
|
这玩意是之前遇到了一个问题: Fragment 恢复 (按下返回的那种) 时无论 MutableLiveData 中的变量是否改变 observer 都会获得通知.
然后找到了个大佬重写了个只会通知一次的 SingleLiveEvent.kt.
用法和 MutableLiveData 一样.
Fragment 的导航, 快速实现 Fragment 的跳转, 非常非常非常方便, 还有可视化.
先在 build.grade
的 dependencies 中加:
1
2
3
4
|
dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
}
|
在 res 文件夹下新建一个 Android Resourse Directory, 并选择 type 为 navigation
然后在 navigation 下新建 Navigation Resourse File, 同时在 layout 中的 activity.xml 中添加 NavHostFragment 视图. 并选择 navGraph 为刚刚新建的 navigation.xml. 这个 NavHostFragment 是 “用以导航的 Fragment”.
之后就可以去 navigation.xml 中可视化编辑了. 点击绿色加号的东西, 添加一个 destination, 一般为 Fragment. 然后可以非常直觉地添加跳转 action.
在 Fragment 中执行跳转 action 也非常方便:
1
|
Navigation.findNavController(binding.fragmentOne).navigate(R.id.action_fragmentOne_to_fragmentTwo)
|
其中 findNavController
的参数是当前 Fragment View, navigate
的参数是 action 的 id.
导航使用的是压栈式跳转. 可以设置 popUpTo 指定弹栈到某个 Fragment, 而不是只弹一个. 这样可以实现 “过渡页面” 的操作 — — 只需要把 A -> B -> C 中 B -> C 的 action 设置一下 popUpTo A. 这样 C 返回的时候就直接返回到 A 了.
1
2
|
requireActivity().onBackPressedDispatcher.addCallback(this) {
}
|
什么都不写就是禁用了. 暂时没搞懂监听事件的过程, 是不是像其他监听事件那样返回 true 或者 false 实现拦截或继续上一级监听. 正好当时做的需要禁用, 先不管了