Jetpack初尝试 NavController,LiveData,DataBing,ViewModel,Paging

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: Jetpack初尝试 NavController,LiveData,DataBing,ViewModel,Paging

插件配置

  • 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)}'


学习视频

Jetpack Playlist

相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
7月前
|
前端开发 测试技术 API
Jetpack MVVM 七宗罪之六:ViewModel 接口暴露不合理
Jetpack MVVM 七宗罪之六:ViewModel 接口暴露不合理
91 0
|
7月前
|
前端开发 Java API
Jetpack MVVM 七宗罪之五: 在 Repository 中使用 LiveData
Jetpack MVVM 七宗罪之五: 在 Repository 中使用 LiveData
125 0
|
7月前
|
前端开发 JavaScript Android开发
Jetpack MVVM 七宗罪之四: 使用 LiveData/StateFlow 发送 Events
Jetpack MVVM 七宗罪之四: 使用 LiveData/StateFlow 发送 Events
182 0
|
Android开发
Android JetPack组件之LiveData的使用详解
Android JetPack组件之LiveData的使用详解
164 0
Android JetPack组件之LiveData的使用详解
|
编解码
Jetpack 之 ViewModel 组件介绍
ViewModel 是介于 View(视图)和 Model(数据模型)之间的一个东西。它起到了桥梁的作用,使视图和数据既能够分离开,也能够保持通信。
94 0
Jetpack 之 ViewModel 组件介绍
|
Android开发
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
156 0
|
存储 Android开发
Android JetPack组件之ViewModel的使用详解
Android JetPack组件之ViewModel的使用详解
125 0
|
存储 XML 前端开发
Android Jetpack系列之ViewModel
ViewModel的定义:**ViewModel旨在以注重生命周期的方式存储和管理界面相关的数据**。ViewModel本质上是视图(View)与数据(Model)之间的桥梁,想想以前的MVC模式,视图和数据都会写在Activity/Fragment中,导致Activity/Fragment过重,后续难以维护,而ViewModel将视图和数据进行了分离解耦,为视图层提供数据。
215 0