This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

One, foreword

When the page (especially the list page) data is empty, the page may be blank, which can affect the appearance and even affect the normal use of the function. Therefore, a default page is displayed to prompt the user whether the current lack of data is due to network problems or other reasons.

As shown in the following figure, the blank page is generally composed of an icon, a title, a subtitle and a button. These controls can be selected to display one or more. The empty page is roughly the same throughout the project, changing nothing more than ICONS, text, and the presentation state of each control. It may seem simple, but repeating something 10 times is a lot of work, so it is necessary to encapsulate an empty page component.

Two, demand analysis

Before starting a project, it is important to know the purpose, breakdown and analysis of the steps to be achieved. For a blank page component, we want it to:

  1. Simple to add; (Preferably one line of code can turn it on or off.)
  2. High configurable capability; (Each control can be used as the base TextView/ImageView control.)
  3. Can automatically show and hide according to the page data change; (The content is displayed when there is data, and the blank page is displayed when there is no data.)
  4. Can display the corresponding state UI according to the reason that the data is empty; (Breakdown of the reason for empty data, server exception, empty content, network disconnected, network unavailable can all display different UI)
  5. You can set a global common style for each empty state, and the other pages only need to change the difference. (Global configuration once, all pages use this configuration UI style, special pages only need to change different Settings)

In short, it can meet the needs of the project, but also to use simple!!

Three, display

Talk about it. Do it

1. Add dependencies,The project address

dependencies {
    implementation "com.github.runnchild.Feature:emptyview:$latest_version" 
}
Copy the code

2. Configure the global style

/ / configure global network not connected style DefaultEmptyConfig configNetDisconnectBuilder {icon {setImageResource (R.m ipmap. Empty_no_net) layoutParams = ViewGroup.LayoutParams(150.dp, 150.dp)} tip {text = "Failed network connection" textSize = 17f setTextColor(color.gray)} subTip {text = "Please check your network Settings and refresh"} refreshBtn { background = ContextCompat.getDrawable(context, R.d rawable. Round_background) text = "refresh"}} / / configure global empty data style DefaultEmptyConfig configEmptyDataBuilder {... } / / configure global network, not with style DefaultEmptyConfig configNetUnavailableBuilder {... }Copy the code

In this way, the blank page will look roughly like the UI above. The same is true for empty data and unavailable networks.

3. Set parameters

(Taking ListAbility in Feature as an example here, ListAbility realizes the association between data subscription and empty page components; otherwise, this part of function needs to be implemented by yourself. This is the introduction of ListAbility)

class RepoSearchFragment : BaseFragment<FragmentListBinding, RepoSearchViewModel>, IRecyclerHost { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { registerAbility(ListAbility(viewModel, this)) } override fun setupEmptyView(builder: EmptyBuilder) {// set empty data only, otherwise use the default builder. WhenDataIsEmpty {tip {// compared to the global configuration above, Text = "no repository found"}} builder. WhenDisconnect {... }.whenUnavailable { ... } // Override fun providerEmptyView(context: context): IEmptyView? { // default is EmptyView return super.providerEmptyView(context) } }Copy the code

Four,,

In fact, there is no need to elaborate what, too simple I am embarrassed to say, just look at the source code to understand. It is important to note that this is a DataBinding dependent component, otherwise it will not work properly.

Component class structure:

  • EmptyView
    • IEmptyView – Empty page UI interface
    • EmptyView — Empty page UI
    • EmptyViewConfig — Empty page data configuration class
    • EmptyState — Empty page state (cause)
    • DefaultEmptyConfig – Stores the global default configuration
    • EmptyBuilder — the EmptyViewConfig builder

Class key code

    1. IEptyView: Empty page View needs to implement the interface used for set/getConfig()
interface IEmptyView {
    var config: EmptyViewConfig?
}
Copy the code
    1. EmptyView: a simple custom View that contains several controls needed for empty pages
class EmptyView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), IEmptyView {
    
    private var binding = EmptyViewBinding.inflate(LayoutInflater.from(context), this, true)
    
    override var config: EmptyViewConfig?
        get() = binding.config
        set(value) {
            binding.config = value
        }
}
Copy the code
    1. EmptyViewConfig: UI status configuration. The configuration will be automatically updated to EmptyView
class EmptyViewConfig {
    var refreshBuilder = ObservableField<TextView.() -> Unit>()
    var tipBuilder = ObservableField<TextView.() -> Unit>()
    var subTipBuilder = ObservableField<TextView.() -> Unit>()
    var iconBuilder = ObservableField<ImageView.() -> Unit>()
}
Copy the code

The realization principle can be summarized in one sentence:

When the condition that the data is empty is triggered, (1) record the current empty data status, and the component selects the corresponding global default configuration (2) based on the current empty state, (3) pass the final EmptyViewConfig to the page for differential processing (3), and (4) pass the final EmptyViewConfig to the EmptyView for changing the UI status (4).

(1) : Emptystate. EMPTY_DATA is easy to understand when the data is empty. For example, in ListAbility, when the request succeeds and the data is empty, the state is emptystate. EMPTY_DATA. Emptystate.empty_net_disconnect or emptystate.empty_NET_UNAVAILABLE when the request error occurs, otherwise the server error emptystate.empty_service is displayed

fun <T> Resource<List<T>? >.emptyState(block: (EmptyState) -> Unit) { when (status) { Status.SUCCESS -> { if (data.isNullOrEmpty()) { block(EmptyState.EMPTY_DATA) } }  Status.ERROR -> { AppExecutors.diskIO().execute { if (! NetworkUtils.isConnected()) { block(EmptyState.EMPTY_NET_DISCONNECT) } else if (! NetworkUtils.isAvailable()) { block(EmptyState.EMPTY_NET_UNAVAILABLE) } else { if (data.isNullOrEmpty()) { block(EmptyState.EMPTY_SERVICE) } } } } }Copy the code

Select * from EmptyState; select * from EmptyState;

val defaultBuilder = when (state) { EmptyState.EMPTY_NET_DISCONNECT -> DefaultEmptyConfig.noNetBuilder EmptyState.EMPTY_NET_UNAVAILABLE -> DefaultEmptyConfig.netUnavailableBuilder else -> DefaultEmptyConfig.emptyDataBuilder  } val emptyBuilder = EmptyBuilder(state).apply(defaultBuilder)Copy the code

③ : the default configuration is passed to the page to continue processing

listHost.setupEmptyView(emptyBuilder)
Copy the code

④ : At this time emptyBuilder is the final empty configuration, through the Builder method will finally be passed to EmptyView to do UI changes

emptyConfig.builder(emptyBuilder)
Copy the code

Five, the summary

I’ve worked on a lot of projects, but none of them have been able to decently encapsulate a simple, reusable component like a blank page, which is either too complicated to implement or too cumbersome to configure, or can’t be applied with the slightest change of requirements. Maybe people think that big things are not trivial, the biggest is more copy and paste operation. Maybe I’m lazy. Writing less code is my goal. So I wrote my own minimalist MVVM framework Feature, of which emptyView is a component. More components interested friends can see this framework, if you feel a little interesting trouble to help me point a Star.

Again, what I share is simple, more of an idea. If you have a good suggestion or comment or have a better plan, please feel free to comment. Learning from each other is also the reason why people create and open source.

MVVM framework building series

MVVM Framework Building — How to Optimize the Coupling Problem caused by Multi-layer Inheritance (part 1)

MVVM Framework Building — Efficient Implementation of List Page (2)

More to come, welcome to follow…