Loading different states is often needed in projects, such as loading state during network request, loading failure state, no network state and no data state, etc. The previous practice in projects is to add several different state layouts to the layout file of activities or fragments that need state switching. Then hide and display each status interface accordingly, but in the case of more than one interface, repeated operation will appear very tedious.
After for countless such tedious operation, some can’t stand, can just thinking of the several states are encapsulated into the same View, when need to display different state only need to call the corresponding state methods can switch, so much more than a kind of method is simple, ha ha, this is can, of course, Let’s talk about NetworkStateView.
NetworkStateView inherits from The LinearLayout, which defines load success, load in, load error (here only unified definition of network error, of course, the use of which error mode can be decided by you), no network, no data five states, MCurrentState is used to remember the current displayed state. The corresponding variable value is as follows:
// Current loading status
private int mCurrentState;
private static final int STATE_SUCCESS = 0;
private static final int STATE_LOADING = 1;
private static final int STATE_NETWORK_ERROR = 2;
private static final int STATE_NO_NETWORK = 3;
private static final int STATE_EMPTY = 4;Copy the code
Then you need to customize the attributes, used to pass in the corresponding state layout file, in the same state if you need to have different interface display, you can pass in the corresponding layout file, so that it is convenient to expand
<declare-styleable name="NetworkStateView">
<! -- Loading layout id -->
<attr name="loadingView" format="reference" />
<! -- load wrong layout id -->
<attr name="errorView" format="reference" />
<! -- loading wrong layout image -->
<attr name="nsvErrorImage" format="reference" />
<! -- loading wrong layout text -->
<attr name="nsvErrorText" format="string" />
<! -- Layout id without data -->
<attr name="emptyView" format="reference" />
<! -- Layout image without data -->
<attr name="nsvEmptyImage" format="reference" />
<! -- Layout text without data -->
<attr name="nsvEmptyText" format="string" />
<! -- No network layout id -->
<attr name="noNetworkView" format="reference" />
<! -- Layout image without data -->
<attr name="nsvNoNetworkImage" format="reference" />
<! -- Layout text without data -->
<attr name="nsvNoNetworkText" format="string" />
<! -- Refresh ImageView image id -->
<attr name="nsvRefreshImage" format="reference"/>
<! -- Text size -->
<attr name="nsvTextSize" format="dimension" />
<! -- Text color -->
<attr name="nsvTextColor" format="color" />
</declare-styleable>Copy the code
After you define the property, you need to look up the value of the property using TypedArray in the constructor of NetworkStateView. Note that you need to look up the ID of the layout file when you get it, and then display the layout file ID with the corresponding inflate, After the search, you can set some basic properties, such as LayoutParams and BackgroundColor
public NetworkStateView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NetworkStateView, defStyleAttr, 0);
mLoadingViewId = typedArray.getResourceId(R.styleable.NetworkStateView_loadingView.R.layout.view_loading);
mErrorViewId = typedArray.getResourceId(R.styleable.NetworkStateView_errorView.R.layout.view_network_error);
mNoNetworkViewId = typedArray.getResourceId(R.styleable.NetworkStateView_noNetworkView.R.layout.view_no_network);
mEmptyViewId = typedArray.getResourceId(R.styleable.NetworkStateView_emptyView.R.layout.view_empty); . typedArray.recycle(); mInflater =LayoutInflater.from(context);
params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT.ViewGroup.LayoutParams.MATCH_PARENT);
setBackgroundColor(getResources().getColor(R.color.white));
}Copy the code
When using properties, you can set them directly in the NetworkStateView layout file or in the Styles file
-
Declare it directly in the layout file
<com.zht.networkstateview.ui.widget.NetworkStateView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nsv_state_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:orientation="vertical" android:visibility="visible" app:emptyView="@layout/view_empty" app:errorView="@layout/view_network_error" app:loadingView="@layout/view_loading" app:noNetworkView="@layout/view_no_network" app:nsvTextColor="@color/gray_text_default" app:nsvTextSize="16sp"> </com.zht.networkstateview.ui.widget.NetworkStateView>Copy the code
-
Set in the styles file
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <! -- Customize your theme here. --> <item name="styleNetworkStateView">@style/NetworkStateViewTheme</item>.</style> <style name="NetworkStateViewTheme" parent="NetworkStateView.Style"> <item name="nsvTextSize">16sp</item> <item name="nsvTextColor">#ffffff</item>.</style>Copy the code
As the loading fails (network error), define a showError method that starts with the current state as STATE_NETWORK_ERROR, followed by the inflate layout, notice that the public view view is synchronized. AddView is required after the inflate to add the View in the failed load (network error) state to NetworkStateView so that the corresponding show and hide action can be performed
public void showError() {
mCurrentState = STATE_NETWORK_ERROR;
if (null == mErrorView) {
mErrorView = mInflater.inflate(mErrorViewId, null);
addView(mErrorView, 0.params);
}
showViewByState(mCurrentState);
}Copy the code
The showViewByState method switches the View based on the current state
private void showViewByState(int state) {// If the current state is loading successfully, hide this View, otherwise display this.setVisibility(state== STATE_SUCCESS ? View.GONE : View.VISIBLE); if (null ! = mLoadingView) { mLoadingView.setVisibility(state== STATE_LOADING ? View.VISIBLE : View.GONE); } if (null ! = mErrorView) { mErrorView.setVisibility(state== STATE_NETWORK_ERROR ? View.VISIBLE : View.GONE); } if (null ! = mNoNetworkView) { mNoNetworkView.setVisibility(state== STATE_NO_NETWORK ? View.VISIBLE : View.GONE); } if (null ! = mEmptyView) { mEmptyView.setVisibility(state== STATE_EMPTY ? View.VISIBLE : View.GONE); }}Copy the code
B: well… So far, that’s about it, but there’s still a problem. What if you need to refresh the network after a load failure? Ha ha, of course, this can also be solved, we just need to define a refresh button, and provide an external interface to call, the corresponding interface and method is
public void setOnRefreshListener(OnRefreshListener listener) {
mRefreshListener = listener;
}
public interface OnRefreshListener {
void onRefresh();
}Copy the code
So showError can be modified as follows
public void showError(a) {
mCurrentState = STATE_NETWORK_ERROR;
if (null == mErrorView) {
mErrorView = mInflater.inflate(mErrorViewId, null);
View errorRefreshView = mErrorView.findViewById(R.id.error_refresh_view);
if (null! = errorRefreshView) { errorRefreshView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (null! = mRefreshListener) { mRefreshListener.onRefresh(); }}}); } addView(mErrorView,0, params);
}
showViewByState(mCurrentState);
}Copy the code
Well, that way you can wrap multiple states of a View in one View. We can include NetworkStateView in the Activity layout file. We can then switch states by simply calling NetworkStateView’s methods
You can check the detailed code and Sample on github. If you think it is ok, you can try Star or Follow
There is an old saying that a programmer who is not lazy is not a good programmer. Some people think that this method is only slightly easier than the first method, but it also needs to be added in the layout file of each interface through the include tag, and the findViewById operation and then call the related method. Can we make uniform Settings? You don’t need to include it in every interface, you can call the related methods after setting it. Of course, this is also possible. We can set it in BaseActivity, so that the subclass Activity only needs to call the BaseActivity method. Please wait for the next part…