LifeCycle

这个以一个定时器为例子,LifeCycle有利于组件和Activity的解耦,不让过多的代码写在Activity中导致Activity过于臃肿

自定义定时器 MyChronometer

public class MyChronometer : Chronometer,LifecycleObserver {
private var elapseTime:Long = 0
constructor( context:Context, attrs: AttributeSet) : super(context,attrs)

  1. _/**<br /> * 绑定到Activity的生命周期上<br /> */<br /> _@OnLifecycleEvent(Lifecycle.Event._ON_RESUME_)<br /> **private fun **startMeter(){<br /> Log.d(**"LIFE-CYCLE"**,**"开始时间设置"**)<br /> setBase(SystemClock.elapsedRealtime() - **elapseTime**)<br /> start()_//Chronometer下的方法<br /> _}
  2. @OnLifecycleEvent(Lifecycle.Event._ON_PAUSE_)<br /> **private fun **stopMeter(){<br /> Log.d(**"LIFE-CYCLE"**,**"返回时间设置"**)<br /> **elapseTime **= SystemClock.elapsedRealtime() - getBase()<br /> stop()<br /> }<br />}<br />�<br />继承Chronometer类,并实现LifecycleObserver接口<br />通过注释的方式 @OnLifecycleEvent 绑定生命周期<br />在Activity开始的Resume阶段设置初始,并开始计时<br />在Pause时更新暂停时间,暂停计时

Activity

class LifeCycleViewActivity : AppCompatActivity() {
private var chronometer: MyChronometer ?= null
override fun
onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_life_cycle_view)
chronometer = findViewById(R.id.chronometer)
getLifecycle().addObserver(chronometer!!)
}
}

可以看到这里只做了两件事,初始化自定义 chronometer 控件,并使用 getLifecycle() 获得这个Activity的生命周期,同时将该自定义组件添加为观察者

ViewModel+LiveData

image.png
ViewModel大家都很熟悉,用来做数据的持久化,因为这是 MVVM 必不可少的,它的好处有很多,比如能保证内存安全,降低耦合度等等,
能保证你Activity状态发生改变时(比如旋转),数据不发生改变
也可以用来解决耗时操作的回调(比如网络请求数据在Activity被销毁后才返回回调)
分担UI Controller 的负担,
这里举两个简单的例子

简单的数字加

自定义ViewModel

承自ViewModel类

public class MyViewModel: ViewModel() {
public var number = 0
private var currentSecond: MutableLiveData ?= null
if(currentSecond == null){
currentSecond = MutableLiveData()
currentSecond?.value = 0
}
return currentSecond!!
}
}

�Activity

这里可以看到myVIewModel是由ViewModelProvider提供的,他需要获取当前所在的Activity,保证该个Activity下只有一个该viewmodel的实例
image.png
image.png

可以看到其将ViewModel存在HashMap中,保证了唯一性

class ViewModelActivity : AppCompatActivity() {
var textView :TextView ?= null
var button
: Button?= null
var myViewModel
: MyViewModel ?= null
override fun
onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
textView = findViewById(R.id.txt_num)
button = findViewById(R.id.btn_plus)
myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
textView?.text = myViewModel?.number.toString()
}
fun plusNumber(view: View) {
//plus和inc不会改变自身的值,但是会返回 +1
myViewModel?.number = myViewModel?.number?.plus(1)!!
Log.d(“VIEW-MODEL”,myViewModel?.number.toString())
textView?.text = myViewModel?.number.toString()
}

Fragment间的通信

以其中一个Fragment为例(因为两个Fragment除了xml其他基本一样)

自定义VIeModel

public class MyViewModel: ViewModel() {** private var progress: MutableLiveData ?= null
public fun
getCurrentSecond():MutableLiveData{
if(currentSecond == null){
currentSecond = MutableLiveData()
currentSecond?.value = 0
}
return currentSecond!!
}
}

Fragment

使用 ViewModelProvider 创建 MyViewModel 实例
这里给 viewmodel 设置观察者,当viewmodel中的数值发生改变的时候触发另一个viewmodel的变化
同时进度条设置了当值发生变化的时候更新ViewModel,因为两个 fragment 在同一个 Activity下,所以用的
viewmodel是同一个,所以内部保存的值一样,出发上文中的观察者

class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
/*
当拖动进度条的时候值发生变化,设置监听触发viewmodel值的变化
因为viewmodel设置了监听这时候会自动变另一个进度条的值
/
var root = inflater.inflate(R.layout.fragment_first, container, false)
var seekBar = root.findViewById(R.id.seekBar)
//ViewModelProvider传入的owner保证在同一个context下的viewmodel是一样的,保证了数据的安全性
var viewModel: MyViewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
viewModel.getProgress().observe(requireActivity(), Observer {
seekBar.setProgress(it)
})
seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
viewModel.getProgress().value = p1
Log.d(“==VIEWMODEL==”,“通知ViewModel更改 ${viewModel.getProgress().value}”)
}

  1. **override fun **onStartTrackingTouch(p0: SeekBar?) {<br /> }
  2. **override fun **onStopTrackingTouch(p0: SeekBar?) {<br /> }
  3. })<br /> **return **root<br /> }<br />}

DataBinding�

DataBinding是数据绑定,将数据的处理从Activity中移到xml中,让Activity更加健壮,同时省去了部分findViewById的情况
看着非常神奇,只需要通过DataBindingUtil下的方法传入当前Activity和xml文件,同时将xml转成databinding的格式,就将ActivityXX的实例与xml绑定了,只要修改XX的属性就可以实时改变里面的值
原理就是DataBinding库会为每个布局文件生成一个binding类,生成的binding类将布局中的View与布局变量链接起来image.png
在Gradle里面允许运行dataBinding

DataBinding使用

需要一个类存需要绑定的数据属性

public class DataBinding {
public var text = “测试成功”
}
Activity中,绑定xml,且将属性的实例传给DatabindingActivity 的实例

class DBActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//ActivityDbBinding类的名字是自动根据xml生成
//因为xml是activity_db,所以名字是ActivityDbBinding
var activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_db)
val db = DataBinding()
activityMainBinding.dataBinding = db
}
}
xml中

<layout 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
>
//绑定的数据在这里声明
<data>
<variable
name=”DataBinding”
type=”com.example.components.DataBinding”
/>
</data>

  1. <**androidx.constraintlayout.widget.ConstraintLayout<br /> android:layout_width="match_parent"<br /> android:layout_height="match_parent"<br /> tools:context=".Activity.DBActivity"**>
  2. <**TextView<br /> android:id="@+id/txt_databinding"<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> android:text="@{DataBinding.text }"<br /> android:textSize="24sp"<br /> app:layout_constraintBottom_toBottomOf="parent"<br /> app:layout_constraintEnd_toEndOf="parent"<br /> app:layout_constraintStart_toStartOf="parent"<br /> app:layout_constraintTop_toTopOf="parent" **/><br /> </**androidx.constraintlayout.widget.ConstraintLayout**><br /></**layout**><br />

�DataBindingAdapter

使用注解,将类中的属性绑定给视图
在执行到xml里发现自定义参数的时候会去寻找对应的有该value的注解
并执行其下的函数(参数定死,一般为两个第一个为view第二个为自定义),只要将databinding在xml中给予变量就能传入该函数中进行相应的操作
Adapter中
public object DBAdapter {
//这里的value是和xml里的对应的app:image: xxx;

  1. @BindingAdapter(**"image"**)<br /> @JvmStatic<br /> **public fun **setImage( imageView: ImageView, url: String){<br /> **if**(!url._isEmpty_()){<br /> **var **options = RequestOptions()<br /> .centerCrop()<br /> .placeholder(R.drawable._ic_launcher_background_)<br /> Glide.with(imageView._context_).load(url).apply(options).into(imageView)<br /> }<br /> **else **{<br /> imageView.setBackgroundColor(Color._GRAY_)<br /> }<br /> }<br /> }

XML中
<layout 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
>

  1. <**data**><br /> <**variable<br /> name="networkImage"<br /> type="String" **/><br /> </**data**>
  2. <**androidx.constraintlayout.widget.ConstraintLayout<br /> android:layout_width="match_parent"<br /> android:layout_height="match_parent"<br /> tools:context=".Activity.DataBindingAdapterActivity"**>
  3. <**ImageView<br /> android:id="@+id/imageView"<br /> app:image = "@{networkImage}" //自定义了一个参数,并由databinding传入了那个注解的函数中<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> app:layout_constraintBottom_toBottomOf="parent"<br /> app:layout_constraintEnd_toEndOf="parent"<br /> app:layout_constraintStart_toStartOf="parent"<br /> app:layout_constraintTop_toTopOf="parent"<br /> tools:srcCompat="@tools:sample/avatars" **/><br /> </**androidx.constraintlayout.widget.ConstraintLayout**><br /><br />Activity中<br />�**class **DataBindingAdapterActivity : AppCompatActivity() {<br /> **override fun **onCreate(savedInstanceState: Bundle?) {<br /> **super**.onCreate(savedInstanceState)<br /> **var **activityMainBinding:ActivityDataBindingAdapterBinding = DataBindingUtil.setContentView(**this**,R.layout._activity_data_binding_adapter_)<br /> _//直接可以调用属性<br /> _activityMainBinding._networkImage _= **"https://img1.baidu.com/it/u=744376135,811454640&fm=26&fmt=auto"<br /> **}<br />}<br /><br />