Continuously to maintain the company’s business, will find that the pace of evolution of technology is more and more can’t keep up with, as the growth of the age, enthusiasm for new technology also gradually faded, sporadic spare time is gradually learn from reading dead house, with his girlfriend, with Eva, rest, good interpersonal relationship, also gradually found a thigh is much more important than improving what nonsense…




A list,

Jetpack, officially released in 2018, is a collection of warehouses with four major components: Architecture, Foundation, Behavior, and UI components.

Architecture basic components

The base component provides horizontal functionality, such as backward compatibility, testing, and Kotlin language support. Contains KTX extension library, AppCompat downgrade log and so on

Foundation【 architectural components 】

The ones we use the most, including LifeCycles, LiveData, Room, ViewModel, etc

Behavior【 Behavior component 】

Includes three-party room collaboration, better compatibility with CameraX, as well as notifications, permissions, etc

UI [interface]

Interface components provide widgets and assistive programs that make your application not only easy to use, but also a pleasant experience. Learn about Jetpack Compose, which helps simplify interface development, including animations and transitions, emojis, fragments, layouts, and more.

The declarative build layout of Jetpack Compose is basically the same as that of Flutter. It also facilitates the seamless switch of our technology.

With the exception of the newly released component libraries, most of which are familiar, the dataBinding is a core feature that you’ve been using for a long time. Google’s package only reinforces the standards and uniformity of development

For more details, go to the official website




Second, architecture components

Large and introduces the concept of online posts a dime a dozen, to search, or suggest the website, to prevent missing some matters needing attention, some pit also can only remember, is when you meet I don’t have a more thorough research, only through a complete example, several commonly used components encapsulation, easier to use.

But both KTX and Jetpack are currently designed to simplify, standardize development so that you write less code and make fewer mistakes, but compile the same amount of code because the principles don’t change.

My project address

You can also skip the introduction below and look at the code directly. If you have suggestions or better code design methods, please make an issue or leave a comment, and make progress together


At the same time, you can refer to the official KT +Jetpack official demo, which is a schedule project, demo level, so the source code is not so rigorous and standard, but a good demonstration of the use of KT, there is also the use of a lot of Jetpack skills.

The project is a small app for browsing pictures of beautiful women. The data comes from the API of Ganan. IO and the data I crawled by myself. For example, if you search for beautiful women in Baidu and list beautiful websites that you think are good, you can parse HTML and turn them into JSON list rendering, using Jsoup.

viewModel

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
Copy the code

The ViewModel stores and manages UI-related data in a life-cycle manner. Function:

1. In MVVM mode, separate Model from View.

2. Store data.

3. Responsible for preparing data for UI.

Note:

1. The ViewModel has a longer lifetime than an Activity or Fragment, so the ViewModel cannot hold objects in the Context, otherwise it will leak memory.

2. The Activity may fire onCreate() several times during its life cycle, and the ViewModel is created only on the first onCreate() before the Activity is destroyed at the end

The sample

open class BaseViewModel<Any> : ViewModel(), ItemClick<Any> {

    override fun onClick(body: Any) {
        itemData.postValue(body)
    }

    val itemData: MutableLiveData<Any> by lazy {
        MutableLiveData<Any>()
    }

    val list = ArrayList<Any>()

    val data: MutableLiveData<List<Any>> by lazy {
        MutableLiveData<List<Any>>()
    }

    val errorData: MutableLiveData<Int> by lazy {
        MutableLiveData<Int>()
    }

    fun launch(block: suspend () -> Unit, error: suspend (Throwable) -> Unit) = viewModelScope.launch {
        try {
            block()
        } catch (e: Throwable) {
            error(e)
            errorData.postValue(0)
        }
    }
}

class GankViewModel(private val repository: GankRepository) : BaseViewModel<GankMeiziBody>() {

    fun getGankList(pageNum: Int, page: Int) {
        launch({
                val result = repository.getGankList(pageNum, page).results

                    if (page == 1) {
                        list.clear()
                    }
                    list.addAll(result)

                    data.postValue(result)

                    return@launch
                },{})
    }
}
Copy the code

In a project, the image request (business) is centralized in the viewModel, in the UI (Act or Fragment) only need to call the viewModel and listen for data changes

class GankFragment : BaseRequestListFragment<GankMeiziBody>() { private val viewModel by lazy { ViewModelProviders.of(this, InjectorUtil.getGankModelFactory()).get(GankViewModel::class.java) } override fun getViewModel(): BaseViewModel<GankMeiziBody> = viewModel override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) mAdapter? . OntItemClick = viewModel viewModel. ItemData. Observe (this, the Observer {/ / to monitor data changes})}}Copy the code
Initialization of the viewModel

Just val viewModel = ViewModelProviders. Of (this). The get (BaseViewModel: : class. Java)

In projects, further encapsulation is used to pump business into repository


LiveData

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
Copy the code

LiveData is a life-cycle aware and observable data holder class.

Function: Persistently observes changes and changes in data and notifies UI of updates.

Features:

1. Sense the life cycle of the corresponding Activity. Only when the life cycle is onStart and onResume and LiveData is active, the updated data will be notified to the corresponding Activity.

2. When the life cycle is onStop or onPause, data updates are not called back until the life cycle is onResume.

3. When the life cycle is in onDestory, the observer is automatically deleted to prevent memory overflow.

4. Share resources. You can use the singleton pattern to extend LiveData objects to wrap system services so that they can be shared across applications. The LiveData object is connected to the system service once, and then any observer that needs the resource can view the LiveData object only.

In the wrapper above, declare your data contents in the viewModel

val list = ArrayList<Any>()
val data: MutableLiveData<List<Any>> by lazy {
    MutableLiveData<List<Any>>()
}
Copy the code

Because the list of beautiful women pictures is paginated, the total data is maintained in the list. The data pulled in each page is defined as data, and the livedata. postValue is notified after the data changes

data.postValue(result)
Copy the code

So the listener can get notified and do UI rendering

getViewModel().data.observe(viewLifecycleOwner, this) override fun onChanged(list: List<T>?) { list? .takeIf { ! it.isEmpty() }? .apply { binding.commonRecyclerView.visibility = View.VISIBLE binding.commonPageErrorLayout.visibility = View.GONEif(page == 1) { mAdapter? .notifyItemChanged(0, getViewModel().list.size) }else{ mAdapter? .notifyItemRangeInserted(getViewModel().list.size - list.size, getViewModel().list.size) } loadFinish =true

            if (binding.commonSwipeRefresh.isRefreshing) {
                binding.commonSwipeRefresh.isRefreshing = false
            }
            mIsRefreshing = false

        } ?: let {
            hasMoreData = false}}Copy the code


dataBinding

The Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically.
Copy the code

This thing has been around for a long time, and has been used independently, and can reduce a lot of UI assignment code, especially if your list is extremely complex and requires you to write a lot of holders.

Now I just give you a wrapped bindingAdapter without inheritance

open class CommonBindAdapter<DB : ViewDataBinding, T>(layoutId: Int, dataList: MutableList<T>) : RecyclerView.Adapter<CommonBindAdapter.CommonViewHolder<DB>>() {

    public var ontItemClick: ItemClick<T>? = null
        get() = field
        set(value) {
            field = value
        }

    private var dataList: MutableList<T> = ArrayList()

    private var layoutId: Int = 0

    init {
        this.dataList = dataList
        this.layoutId = layoutId
    }

    fun getDataList(): MutableList<T> {
        return Collections.unmodifiableList(this.dataList)
    }

    open fun bindView(viewHolder: CommonViewHolder<DB>, position: Int) { viewHolder.bindView.setVariable(BR.body, getDataList()[position]) ontItemClick? .let { viewHolder.bindView.setVariable(BR.itemClick, it) } viewHolder.bindView.executePendingBindings() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder<DB> {return CommonViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), layoutId, parent, false))
    }

    override fun onBindViewHolder(holder: CommonViewHolder<DB>, position: Int) {
        bindView(holder, position)
    }

    override fun getItemCount(): Int = dataList.size

    class CommonViewHolder<DB : ViewDataBinding>(var bindView: DB) : RecyclerView.ViewHolder(bindView.root)
}
Copy the code

Call:

var mAdapter: CommonBindAdapter<ViewDataBinding, T>? = null
mAdapter = CommonBindAdapter(R.layout.xxx, getViewModel().list)
Copy the code

All you need to do is pass in an XML file, and your XML file will automatically generate the corresponding ViewDataBinding. Note the XML addition

<data>
    <variable
        name="body"
        type="com.moon.beautygirlkotlin.doubanmeizi.model.DoubanMeiziBody" />

    <variable
        name="itemClick"
        type="com.moon.beautygirlkotlin.listener.ItemClick" />
</data>
Copy the code

If your list is of type multiType, you can encapsulate it further, but you need to override the viewType method to tell the Adapter exactly what layout you want

abstract class BaseBindAdapter(dataList: MutableList<Any>) : RecyclerView.Adapter<BaseBindAdapter.BaseHolder<Any>>() {

    interface ViewTypeCallBack {
        fun createViewType(position: Int): Int
    }

    var ontItemClick: ItemClick<Any>? = null
        get() = field
        set(value) {
            field = value
        }

    private var dataList: MutableList<Any> = ArrayList()

    init {
        this.dataList = dataList
    }

    fun removeAtIndex(index: Int) {
        dataList.removeAt(index)
    }

    fun getDataList(): MutableList<Any> {
        return Collections.unmodifiableList(this.dataList)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder<Any> {
        return BaseHolder<Any>(DataBindingUtil.inflate<ViewDataBinding>(LayoutInflater.from(parent.context), viewType, parent, false)).create()
    }

    override fun onBindViewHolder(holder: BaseHolder<Any>, position: Int) {
        holder.bindTo(dataList[position])
    }

    abstract fun createViewType(position: Int): Int

    override fun getItemViewType(position: Int): Int {
        return createViewType(position)
    }

    override fun getItemCount(): Int = dataList.size

    class BaseHolder<T>(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bindTo(data: T) {
            binding.setVariable(BR.body, data)
            binding.executePendingBindings()
        }

        fun create(): BaseHolder<T> {
            return BaseHolder(binding)
        }
    }
}
Copy the code

Continued use

class MyFavoriteAdapter( dataList: MutableList<Any>) : BaseBindAdapter(dataList ,viewType)
        , ItemMoveListener {

    override fun createViewType(position: Int): Int {
        val t = getDataList()[position]
        if (t is FavoriteBean) return R.layout.item_favourite
        if (t is FavoriteBeanOther) return R.layout.item_favourite_other
        return0}}Copy the code

If you don’t want to inherit a multitype adapter, add a callBack to the constructor

 class BaseBindAdapter(dataList: MutableList<Any> , viewType: ViewTypeCallBack) : RecyclerView.Adapter<BaseBindAdapter.BaseHolder<Any>>() {

    interface ViewTypeCallBack {
        fun createViewType(position: Int): Int
    }

    var ontItemClick: ItemClick<Any>? = null
        get() = field
        set(value) {
            field = value
        }

    private var viewTypeCallBack : ViewTypeCallBack? = null

    private var dataList: MutableList<Any> = ArrayList()

    init {
        this.dataList = dataList
        this.viewTypeCallBack = viewType
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder<Any> {
        return BaseHolder<Any>(DataBindingUtil.inflate<ViewDataBinding>(LayoutInflater.from(parent.context), viewType, parent, false)).create()
    }

    override fun onBindViewHolder(holder: BaseHolder<Any>, position: Int) {
        holder.bindTo(dataList[position])
    }

    override fun getItemViewType(position: Int): Int {
        returnthis.viewTypeCallBack? .createViewType(position) ? : 0 } override fun getItemCount(): Int = dataList.size class BaseHolder<T>(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) { funbindTo(data: T) {
            binding.setVariable(BR.body, data)
            binding.executePendingBindings()
        }

        fun create(): BaseHolder<T> {
            return BaseHolder(binding)
        }
    }
}
Copy the code

Directly instantiated for use

val adapter = BaseBindAdapter(viewModel.list , viewType = object : BaseBindAdapter.ViewTypeCallBack {

        override fun createViewType(position: Int): Int {
            val t = viewModel.list[position]
            if (t is FavoriteBean) return R.layout.item_favourite
            if (t is FavoriteBeanOther) return R.layout.item_favourite_other
            return0}}Copy the code


If applied to commercial projects, further encapsulation is needed to meet the changes of business at any time.