插件配置
- classpath “androidx.navigation:navigation-safe-args-gradle-plugin:2.2.0”
- apply plugin: ‘androidx.navigation.safeargs.kotlin’
- dataBinding { enabled = true}
- implementation ‘android.arch.paging:rxjava2:1.0.1’
- implementation ‘android.arch.paging:runtime:1.0.1’
- …
NavController 使用
1. 创建xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/nav_host" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:navGraph="@navigation/nav_garden"/> </FrameLayout> </layout>
2. 创建Activity
class MainActivity2 : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView<ActivityMain2Binding>(this, R.layout.activity_main2) } }
3. res 创建navigation/nav_garden和说明
<navigation 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" app:startDestination="@id/view_pager_fragment"> <fragment android:id="@+id/view_pager_fragment" android:name="com.gctech.androidstudydemo.jetpack.HomeViewPagerFragment" tools:layout="@layout/fragment_view_pager"> <action android:id="@+id/action_view_pager_fragment_to_plant_detail_fragment" app:destination="@id/plant_detail_fragment" app:enterAnim="@anim/slide_in_right" app:exitAnim="@anim/slide_out_left" app:popEnterAnim="@anim/slide_in_left" app:popExitAnim="@anim/slide_out_right" /> </fragment> <fragment android:id="@+id/plant_detail_fragment" android:name="com.gctech.androidstudydemo.jetpack.EmptyFragment" android:label="详情标题"> <argument android:name="id" app:argType="string" /> </fragment> </navigation>
startDestination
destination
action
通过id指向目标
传参数 argument name
获取参数 如果传入的有参数就会生成对应的Args 然后获取到
private val args: EmptyFragmentArgs by navArgs()
流程
nav_garden->startDestination->id->name
action->destination->id->name
ViewModel 负责页面的数据
包含 LiveData和ObservableField 和其他数据
LiveData onChanged
观察者模式->订阅数据的变化
Observable addOnPropertyChangedCallback
数据和view双向绑定
创建viewmodle
ViewModelProvider.Factory
创建viewmodle 可以用SavedStateHandle存储和读取数据
AbstractSavedStateViewModelFactory(@NonNull SavedStateRegistryOwner owner, @Nullable Bundle defaultArgs)
DataBinding
页面用viewmodel item用实体类
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="hasPlantings" type="boolean" /> <import type="android.view.View"/> <variable name="vm" type="com.gctech.androidstudydemo.jetpack.viewmodels.EmptyDataViewModel" /> </data> <FrameLayout android:id="@+id/fl_content" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{vm.datas.title}" android:visibility="@{vm.datas.isGone() ? View.VISIBLE : View.INVISIBLE}" /> </FrameLayout> </layout>
class EmptyFragment : Fragment() { private val args: EmptyFragmentArgs by navArgs() private lateinit var binding: FragmentEmptyBinding private val viewModel: EmptyDataViewModel by viewModels { InjectorUtils.provideCommonViewModelFactory("") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentEmptyBinding.inflate(inflater, container, false) binding.tvContent.setOnClickListener { ToastUtils.showShort("========") viewModel.datas.get()?.title ="6666" viewModel.datas.notifyChange() } viewModel.datas.set(A("1", true, "434")) binding.vm = viewModel binding.lifecycleOwner = viewLifecycleOwner binding.hasPlantings = true return binding.root } }
API接口使用
定义 接口
interface ApiService { /** * 1.测试第一个接口 */ @GET("xxx/testOne") suspend fun testOne(): HttpResult<List<Country>> /** * 1.测试第二 个接口 */ @GET("xxx/testTwo") suspend fun testTwo(@Query("list") countries: Int): HttpResult<List<Country>> /** * 1.测试分页接口 */ @GET("xxx/testPage") fun testPage(@Query("page") page: Int, @Query("pageSize") pageSize: Int= 20): Call<HttpResult<Tx>> }
viewmodel中使用
class EmptyDataViewModel( private val params: Any? ) : ViewModel() { val datas = ObservableField<A>() val firstName = ObservableField<String>() val lastName = ObservableField<String>() val isShowDialog = MutableLiveData<Boolean>() val allName = object : ObservableField<String>(firstName, lastName) { override fun get(): String? { return "${firstName.get()} ${lastName.get()}" } } //调用一个接口 fun country() = liveData(Dispatchers.IO) { isShowDialog.postValue(true) try { emit( Api.createService(ApiService::class.java) .testOne() ) isShowDialog.postValue(false) } catch (e: Exception) { isShowDialog.postValue(false) } } //顺序调用接口 fun country2() = liveData(Dispatchers.IO) { isShowDialog.postValue(true) val d = Api.createService(ApiService::class.java) .testOne() val d2 = Api.createService(ApiService::class.java) .testTwo(d.data!!.size) try { emit(d2) isShowDialog.postValue(false) } catch (e: Exception) { isShowDialog.postValue(false) } } //合并多个接口 fun country3() = liveData(Dispatchers.IO) { isShowDialog.postValue(true) val d = Api.createService(ApiService::class.java) .testOne() val d2 = Api.createService(ApiService::class.java) .testTwo(3) try { emit(Triple(d, d2, null)) isShowDialog.postValue(false) } catch (e: Exception) { isShowDialog.postValue(false) } } val title = MutableLiveData<String>() fun addData() { viewModelScope.launch { //调用接口 } datas.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable?, propertyId: Int) { } }) } } // fragment中使用 binding.tvContent.setOnClickListener { ToastUtils.showShort("========") viewModel.country3().observe(viewLifecycleOwner) { viewModel.datas.get()?.title = "${it.first.data?.size}" viewModel.datas.notifyChange() } } viewModel.isShowDialog.observe(viewLifecycleOwner) { if (it) { LoadDialogUtil.showWaitDialog(activity as Context) } else { LoadDialogUtil.dismissWaitDialog() } }
Paging使用
自定义PageKeyedDataSource
class CustomPageDataSource() : PageKeyedDataSource<Int, A>() { override fun loadInitial( params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, A> ) { loge("loadInitial size : ${params.requestedLoadSize} ") fetchData(LoadParams(1, 10)) { callback.onResult(it.tx, null, 2) } } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, A>) { loge("loadAfter size : ${params.requestedLoadSize} page:${params.key}") fetchData(params) { if (params.key == 3) { callback.onResult(mutableListOf(), params.key + 1) } else { callback.onResult(it.tx, params.key + 1) } } } override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, A>) { loge("loadBefore size : ${params.requestedLoadSize} page:${params.key}") fetchData(params) { callback.onResult(it.tx, params.key - 1) } } private fun fetchData(params: LoadParams<Int>, pagingCallback: (Tx) -> Unit) { Api.createService(ApiService::class.java) .testPage(params.key, params.requestedLoadSize) .enqueue(object : Callback<HttpResult<Tx>> { override fun onFailure(call: Call<HttpResult<Tx>>, t: Throwable) { t.printStackTrace() } override fun onResponse( call: Call<HttpResult<Tx>>, response: Response<HttpResult<Tx>> ) { val data = response.body() data?.let { pagingCallback(it.data!!) } } }) } }
自定义 DataSource.Factory
class CustomPageDataSourceFactory() : DataSource.Factory<Int, A>() { override fun create(): DataSource<Int, A> { return CustomPageDataSource() } }
viewmodle中使用
class CommonListDataViewModel( private val params: Any?, private val savedStateHandle: SavedStateHandle ) : ViewModel() { val moreLoading = MutableLiveData<Boolean>()//上拉加载更多状态 val liveDataMerger = MediatorLiveData<Any>() val data2 = LivePagedListBuilder( CustomPageDataSourceFactory(), PagedList.Config.Builder() .setPageSize(10) .setEnablePlaceholders(true) .setInitialLoadSizeHint(10) .build() ).build() val datas = MutableLiveData(MutableList(10) { A("@it", false, "标题$it") }) fun refresh() { data2.value?.let { (it.dataSource as PageKeyedDataSource).invalidate() } } fun addData() { // liveDataMerger.addSource(refreshing, Observer { // liveDataMerger.value = it // }) // liveDataMerger.addSource(moreLoading, Observer { // liveDataMerger.value = it // }) viewModelScope.launch { //调用接口 } } } // fragment中使用 private fun subscribeUi(adapter: CustomAdapter, binding: FragmentListLoadmoreBinding) { //观察多个liveData viewModel.data2.observe(viewLifecycleOwner) { adapter.submitList(it) Handler().postDelayed({ binding.isLoading = false }, 2000) } binding.btnClick.setOnClickListener { viewModel.refresh() } }
自定义属性
自定义方法
@BindingAdapter("visibleGone") fun showHide(view: View, show: Boolean) { view.visibility = if (show) View.VISIBLE else View.GONE }
xml中使用
<TextView android:id="@+id/tv_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="isLoading..." app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:visibleGone="@{isLoading}" />
拼接字符串
<string name="person_number">Person:%1$s人</string> android:text="@{@string/person_number(viewModel.field3)}" android:text='@{String.format("%s - %s", viewModel.field4, viewModel.field5)}'