Jetpack 是一個由多個庫組成的套件,可幫助開發者遵循最佳作法,減小樣板代碼並編寫可在各類 Android 版本和設備中一致運行的代碼,讓開發者精力集中編寫重要的代碼。java
Android Architecture Component (AAC)。android
請注意,每一個組件僅依賴於其下一級的組件。例如,Activity 和 Fragment 僅依賴於視圖模型。存儲區是惟一依賴於其餘多個類的類;在本例中,存儲區依賴於持久性數據模型和遠程後端數據源。後端
ViewModel應該能夠算是JetPack組建中最重要的組件之一。架構
ViewModel是專門用於存放與界面相關的數據。app
ViewModel的生命週期貫穿於Activity整個生命週期,只有Activity銷燬時,ViewModel纔會銷燬。ide
implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01' implementation 'androidx.activity:activity-ktx:1.2.0-beta01' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.3.0-beta01' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'
class ViewModelOne : ViewModel() {}
val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class, { viewModelStore }, { defaultViewModelProviderFactory })
val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }
非懶加載模式必需要等Activity 在OnCreate後才能初始化,不然運行報錯。函數
val viewModelPro = ViewModelProvider( viewModelStore, defaultViewModelProviderFactory ).get(ViewModelOne::class.java)
onClear回調函數處理資源回收closeWithRuntimeException佈局
ViewModel絕對不能引用View、Lifecycle或任何可能包含對Activity上下文的引用的類gradle
class VmFactory(private val count: Int) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(ViewModelOne::class.java)) { return ViewModelOne() as T } else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) { return ViewModelTwo(count) as T } else { throw ClassNotFoundException("class $modelClass ViewModel沒有註冊到工廠類") } } }
ViewModelActivityui
class ViewModelActivity : AppCompatActivity() { val TAG = this.javaClass.simpleName val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class, { viewModelStore }, { defaultViewModelProviderFactory }) val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory } //android view model val viewModelTwo by viewModels<ViewModelTwo> { VmFactory(10) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_view_model) val viewModelPro = ViewModelProvider( viewModelStore, defaultViewModelProviderFactory ).get(ViewModelOne::class.java) Log.d(TAG, "viewModelOne ${viewModelOne.getNow()}") Log.d(TAG, "viewModelOnes ${viewModelOnes.getNow()}") Log.d(TAG, "viewModelPro ${viewModelPro.getNow()}") Log.d(TAG, "viewModelTwo ${viewModelTwo.getNow()}") btn_again.setOnClickListener { startActivity(Intent(this, ViewModelActivity::class.java)) } } }
ViewModelKt
class ViewModelOne : ViewModel() { val TAG = this.javaClass.simpleName var counter = 0 init { Log.d(TAG, "ViewModelOne建立") } fun getNow(): String { return "ViewModelOne: ${System.currentTimeMillis()}" } override fun onCleared() { super.onCleared() Log.d(TAG, "ViewModelOne銷燬") } } class ViewModelTwo(count: Int) : ViewModel() { val TAG = this.javaClass.simpleName var counter = 0 init { Log.d(TAG, "ViewModelTwo建立") counter = count } fun getNow(): String { return "ViewModelTwo: ${System.currentTimeMillis()} ${this.counter}" } override fun onCleared() { super.onCleared() Log.d(TAG, "ViewModelTwo銷燬") } }
VmFactory
class VmFactory(private val count: Int) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(ViewModelOne::class.java)) { return ViewModelOne() as T } else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) { return ViewModelTwo(count) as T } else { throw ClassNotFoundException("class $modelClass ViewModel沒有註冊到工廠類") } } }
ViewModel.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_again" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="啓動界面" /> </LinearLayout> </LinearLayout>
//在module的build.gradle中 apply plugin: 'kotlin-kapt'//必須 android{ //AS 4.0 如下, dataBinding{ enabled true } //AS 4.1以後 bindingFeature{ dataBinding = true // for view binding : // viewBinding = true } }
<layout> <data class=""> </data> <!-- 原有的UI的xml佈局放在layout標籤內便可。data標籤內存放用於xml的數據變量,類型 --> <LinearLayout> </LinearLayout> </layout>
val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>( this, R.layout.activity_data_binding )
variable聲明變量;import導入類型;對於xml的特殊符號須要轉義相似&
綁定xml與data格式:@{}、@={}(雙向綁定)
??判空
?:三目運算符
@string/str_name資源引用,可用佔位符format
+拼接字符,使用**``**反引號
default設置默認值
include綁定
點擊事件
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha06'
<androidx.recyclerview.widget.RecyclerView app:adapter="@{adapter}" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:itemCount="5" tools:listitem="@layout/item_data_binding" />
@BindingConversion
若是屬性不能匹配類型參數將自動根據類型參數匹配到該註解修飾的方法來轉換
@BindingConversion fun str2Color(str: String): Drawable { return when (str) { "red" -> { ColorDrawable(Color.RED) } "blue" -> ColorDrawable(Color.BLUE) else -> { ColorDrawable(Color.YELLOW) } } }
@BindingAdapter
設置自定義屬性. 能夠覆蓋系統原有屬性
@BindingAdapter("android:textColor", requireAll = false) fun getColor(view: TextView, type: Int) { val color = when (type) { 0 -> R.color.colorAccent 1 -> R.color.colorPrimaryDark 2 -> android.R.color.holo_red_dark 3 -> android.R.color.holo_orange_dark else -> R.color.colorPrimary } view.setTextColor(view.context.resources.getColor(color)) }
@Bindable
設置數據刷新視圖. 自動生成BR的ID
註解纔會自動在build目錄BR類中生成entry, 要求方法名必須以get開頭
DatabindingActivity
class DataBindingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>( this, R.layout.activity_data_binding ) binding.apply { name = null address = "TianJin 天津" observeName = ObservableField("observeName") user = User() observeUser = ObserveUser() observeFieldUser = ObserveFieldUser() btnChange.setOnClickListener { name = "姚鑫" address = null user?.name = "孫悟空" observeUser?.age = 18 observeUser?.name = "張曼玉" observeFieldUser?.name?.set("林志玲") } adapter = DataBindingAdapter() info = ItemBean(2,"Activity Item") } } }
DatabindingActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<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="name"
type="String" />
<variable
name="address"
type="String" />
<variable
name="observeName"
type="androidx.databinding.ObservableField<String>" />
<variable
name="user"
type="com.yx.androidseniorpreparetest.fifth.User" />
<variable
name="observeUser"
type="com.yx.androidseniorpreparetest.fifth.ObserveUser" />
<variable
name="observeFieldUser"
type="com.yx.androidseniorpreparetest.fifth.ObserveFieldUser" />
<variable
name="adapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="info"
type="com.yx.androidseniorpreparetest.fifth.ItemBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="演示DataBinding"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name??`Null Name`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{address,default=`tianjin`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name==null?`null`:`nonull`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{@string/str_name(name)}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{observeName}"
android:textColor="#000000"
android:textSize="20sp" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeName}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={user.name}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{observeUser.name + observeUser.age}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeUser.age+``}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeFieldUser.name}"
android:textColor="#000000"
android:textSize="20sp" />
<Button
android:id="@+id/btn_change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@{`pink`}"
android:gravity="center"
android:text="改變值"
android:textColor="#000000"
android:textSize="20sp" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:adapter="@{adapter}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="5"
tools:listitem="@layout/item_data_binding" />
<include
info="@{info}"
layout="@layout/item_data_binding" />
</LinearLayout>
</layout>
user
class User { var name = "姚鑫" var age = 30 var desc = "DataBinding真好用!" } class ObserveUser : BaseObservable() { var age = 31 var name = "" set(value) { notifyPropertyChanged(BR.name) field = value + "yao" } @Bindable get() { return "$field 姚鑫Name" } var desc = "這裏是ObserveUser描述" set(value) { notifyPropertyChanged(BR.desc) field = value + "xin" } @Bindable get() { return "$field 姚鑫Desc" } var str = "" set(value) { field = value notifyPropertyChanged(BR.str) } @Bindable get() { return "age $age name $name desc $desc" } } class ObserveFieldUser { var name = ObservableField("姚鑫") var age = ObservableInt(30) var desc = ObservableField("DataBinding真好用!") var str = "age $age name $name desc $desc" }
ItemBean
data class ItemBean(val type: Int, val text: String)
DataBindingAdapter
class DataBindingAdapter : RecyclerView.Adapter<DataBindingAdapter.DataBindingViewHolder>() { private val mList = mutableListOf<ItemBean>() init { for (i in 0..5) { mList.add(ItemBean(i, "明細 $i")) } } class DataBindingViewHolder(private val binding: ItemDataBindingBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(bean: ItemBean) { binding.info = bean binding.executePendingBindings() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBindingViewHolder { return DataBindingViewHolder( ItemDataBindingBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) } override fun onBindViewHolder(holder: DataBindingViewHolder, position: Int) { holder.bind(mList[position]) } override fun getItemCount(): Int = mList.size }
ItemDataBinding.xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.yx.androidseniorpreparetest.fifth.DataBindingUtilsKt" /> <variable name="info" type="com.yx.androidseniorpreparetest.fifth.ItemBean" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="@{DataBindingUtilsKt.getTitle(info.text)}" android:textColor="@{info.type}" android:textSize="20sp" tools:text="content" /> </LinearLayout> </layout>
DataBindingUtils
@BindingConversion fun str2Color(str: String): Drawable { return when (str) { "red" -> { ColorDrawable(Color.RED) } "blue" -> ColorDrawable(Color.BLUE) else -> { ColorDrawable(Color.YELLOW) } } } fun getTitle(type: String): String { return "type:$type" } @BindingAdapter("android:textColor", requireAll = false) fun getColor(view: TextView, type: Int) { val color = when (type) { 0 -> R.color.colorAccent 1 -> R.color.colorPrimaryDark 2 -> android.R.color.holo_red_dark 3 -> android.R.color.holo_orange_dark else -> R.color.colorPrimary } view.setTextColor(view.context.resources.getColor(color)) }
項目代碼