Android state third-party library
1.StatefulLayout 2
githup
This is a layout library that supports multiple states, including empty layout, error layout and loading layout. Additionally, the library provides options for customizing states.
Dependency import
implementation 'cz.kinst.jakub:android-stateful-layout-base:2.0.7'
use
use in xml
<cz.kinst.jakub.view.StatefulLayout android:id="@+id/stateful_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!--Your Content Here--> </cz.kinst.jakub.view.StatefulLayout>
Set the layout corresponding to the corresponding state in onCreate
// in onCreate() statefulLayout.setStateView(STATE_NO_PERSMISSION, LayoutInflater.from(this).inflate(R.layout.state_no_permission, null)); statefulLayout.setStateView(STATE_PROGRESS, LayoutInflater.from(this).inflate(R.layout.state_progress, null));
API
-
setStateView(String state, View view)
Add new state and corresponding view -
setState(String state)
change current status -
getState()
Returns the current view state (string id) -
setOnStateChangeListener(OnStateChangeListener listener)
Set up a listener for state change events -
setStateController(StateController stateController)
Sets the state controller object. See below.
StateController
If you don't want to manipulate directly with views (MVVM/MPV scenario), you can create an instance of StateController and bind it to StatefulLayout (using databinding for example). StateController allows you to set different states as well as control the current state itself. See the example below or the DataBindingControllerActivity in the example project.
<cz.kinst.jakub.view.StatefulLayout android:layout_width="match_parent" android:layout_height="match_parent" app:stateController="@{viewModel.stateController}"> </cz.kinst.jakub.view.StatefulLayout> stateController = StatefulLayout.StateController.create() .withState(STATE_NO_PERSMISSION, LayoutInflater.from(this).inflate(R.layout.state_no_permission, null)) .withState(STATE_PROGRESS, LayoutInflater.from(this).inflate(R.layout.state_progress, null)) .build(); //... stateController.setState(STATE_PROGRESS);
Extensible (SimpleStatefulLayout)
This base class should be used if you want to have a completely custom set of states/views. You can inherit and add custom states in the constructor (see SimpleStatefulLayout for example), or use Stateful layout directly and add states dynamically in code via setStateView(). The original StatefulLayout contains only one state - StatefulLayout.state.CONTENT, no matter what children are in the markup's XML content.
implementation 'cz.kinst.jakub:android-stateful-layout-simple:2.0.7'
SimpleStatefulLayout extends Stateful layout and adds several useful states for most applications - State.OFFLINE, State.PROGRESS, State.EMPTY. It provides customizable layout placeholders for these states and a way to provide fully custom layouts for states.
You can also customize it by extending the class or adding states with setStateView().
use
<cz.kinst.jakub.view.SimpleStatefulLayout android:id="@+id/stateful_view" android:layout_width="match_parent" android:layout_height="match_parent"> <!--Your Content Here--> </cz.kinst.jakub.view.SimpleStatefulLayout> // in onCreate() statefulLayout.showProgress(); // load data statefulLayout.showContent();
optional attributes
-
app:offlineText
Custom text to display when offline -
app:offlineRetryText
The text of the retry button when offline -
app:emptyText
Custom text to display when offline -
app:offlineImageDrawable
A custom image displayed above the offline status text (if not using a custom layout) -
app:emptyImageDrawable
Custom image displayed above empty status text (if not using custom layout) -
app:offlineLayout
Custom layout displayed when offline -
app:emptyLayout
Custom layout shown when in EMPTY state -
app:progressLayout
Custom layout shown when in PROGRESS state -
app:state
The initial state of the view (内容
,进度
,离线
,空布局
) -
app:stateTextAppearance
When not using a custom layout, here's how to style a TextView in an offline and empty layout state.
API
-
showContent()
-
showProgress()
-
showEmpty()
-
showOffline()
-
setEmptyText(String text)
If using the default layout, this will set the text displayed in the EMPTY state -
setOfflineText(String text)
This will set the text displayed offline if using the default layout -
setOfflineRetryText(String text)
If using the default layout, this will set the text of the retry button displayed while offline -
setOfflineRetryOnClickListener(OnClickListener listener)
If using the default layout, this will set the click listener to the retry button displayed offline -
setEmptyImageDrawable(Drawable drawable)
Set custom image displayed above empty text when not using custom layout -
setEmptyImageResource(int resourceId)
Set custom image displayed above empty text when not using custom layout -
setOfflineImageDrawable(Drawable drawable)
Set custom image displayed above offline text when not using custom layout -
setOfflineImageResource(int resourceId)
Set custom image displayed above offline text when not using custom layout -
setTransitionsEnabled(boolean enabled)
Transition between enabled/disabled states
Use of Transition and TransitionManager for Android animation
2.StateView
StateView is a lightweight control that inherits from View
and absorbs ViewStub
some features of . It is invisible in the initial state, does not occupy the layout position, and occupies less memory. The view will not be added to the layout until the action displays an empty/retry/load view.
introduce
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
dependencies { implementation 'com.github.nukc:StateView:v3.0.2' }
Instructions
Use directly in code:
-
Inject into Activity
mStateView = StateView.inject(Activity activity);
-
Inject into View
mStateView = StateView.inject(View view);
-
Inject into ViewGroup
mStateView = StateView.inject(ViewGroup parent);
or add to layout (this way can be more flexible):
<com.github.nukc.stateview.StateView android:id="@+id/stateView" android:layout_width="match_parent" android:layout_height="match_parent"/>
-
Show an empty view:
mStateView.showEmpty();
-
Show loading view:
mStateView.showLoading();
-
Show retry view:
mStateView.showRetry();
-
Display content:
mStateView.showContent();
Set retry click event:
mStateView.setOnRetryClickListener(new StateView.OnRetryClickListener() { @Override public void onRetryClick() { //do something, no need to call showLoading() //No need to call showLoading() method, StateView will call it automatically } });
Set up a custom view:
-
Global setting method: Create a new one under the layout of your own project, and the name can be the same as the default layout of StateView (no code setting is required). The name of the default layout:
base_empty
// .base_retry
base_loading
-
Single page setting: the layout name is different, and then the code is set.
setEmptyResource(@LayoutRes int emptyResource) setRetryResource(@LayoutRes int retryResource) setLoadingResource(@LayoutRes int loadingResource) // v2.1 setEmptyView(View view) setRetryView(View view) setLoadingView(View view) // v3.0.0 setView(viewType: Int, view: View) // eg: set empty view setView(mStateView.getEmptyResource(), emptyView) // set any view setView(1, view) // show view show(viewType: Int)
Use to OnInflateListener
set the text image or other operations: callback when the view is successfully added to the parent (only one callback per viewType)
mStateView.setOnInflateListener(new StateView.OnInflateListener() { @Override public void onInflate(@StateView.ViewType int viewType, View view) { if (viewType == StateView.EMPTY) { // set text or other ViewGroup emptyView = (ViewGroup) view; TextView tvMessage = (TextView) emptyView.findViewById(R.id.tv_message); ImageView ivState = (ImageView) emptyView.findViewById(R.id.iv_state); tvMessage.setText("custom message"); ivState.setImageResource(R.drawable.retry); } else if (viewType == StateView.RETRY) { // ... } } });
Custom Attribute
<resources> <declare-styleable name="StateView"> <attr name="emptyResource" format="reference" /> <attr name="retryResource" format="reference" /> <attr name="loadingResource" format="reference" /> </declare-styleable> </resources>
animation switch
Set view switching animation:
// The default provider is null, that is, animation switching is not provided by default. // If necessary, set one. setAnimatorProvider(AnimatorProvider provider)
The animation effect can be customized, or you can directly use the animations library, which is separated from the main library, so that you can only rely on the library if you don't need it.
compile 'com.github.nukc.stateview:animations:1.0.1'
The following animation effects are currently provided:
-
Gradient scaling:
FadeScaleAnimatorProvider
-
Card Flip:
FlipAnimatorProvider
-
Swipe left and right:
SlideAnimatorProvider
If you customize it, you can directly implement AnimatorProvider
the interface and provide it .Animator
public class FadeScaleAnimatorProvider implements AnimatorProvider { @Override public Animator showAnimation(View view) { AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(view, "alpha", 0f, 1f), ObjectAnimator.ofFloat(view, "scaleX", 0.1f, 1f), ObjectAnimator.ofFloat(view, "scaleY", 0.1f, 1f) ); return set; } @Override public Animator hideAnimation(View view) { AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(view, "alpha", 1f, 0f), ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.1f), ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.1f) ); return set; } }
Compatible with immersive fullscreen mode
For immersive full-screen mode, you can use this method to make up the height of the statusBar, so as not to cover the toolbar
/** * @return statusBarHeight */ private int getStatusBarHeight() { int height = 0; int resId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resId > 0) { height = getResources().getDimensionPixelSize(resId); } return height; } ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mStateView.getLayoutParams(); layoutParams.topMargin += getStatusBarHeight()
3.LoadSir
LoadSir
It is an efficient, easy-to-use, low-carbon, environmentally friendly, and scalable loading feedback page management framework. When loading network or other data, switch the status page according to your needs. You can add custom status pages, such as loading, loading failure, and no data , network timeout, such as placeholder images, login failure and other commonly used pages. It can cooperate with the network loading framework, combine the return status code, error code, and data to automatically switch the status page, and the packaging effect is better.
Functions and features of LoadSir
-
⭐Support Activity, Fragment, Fragment (v4), View state callback
-
⭐ Adapt to multiple Fragment switching, and Fragment+ViewPager switching, without layout overlap or layout disorder
-
⭐Use generics to convert input signals and output states, and automatically adapt the status page according to the status code or data returned by the network to achieve global automatic state switching
-
⭐ No need to modify layout files
-
⭐ Only one state view is loaded, not all views are preloaded
-
⭐No need to set enumeration or constant status value, directly use status page class type (xxx.class) as status code
-
⭐Click events can be set separately for a single status page, overwritten according to the returned boolean value or used in conjunction with OnReloadListener, such as a network error can jump to the settings page
-
⭐No preset pages, low coupling, developers can configure as they like
-
⭐You can keep the title bar (Toolbar, title view, etc.)
-
Can set reload click event (OnReloadListener)
-
Customizable status page (inherited from Callback class)
-
The state can be switched directly in the child thread
-
The initial status page can be set (the progress page is commonly used as the initial status)
-
Extensible status page, add custom status page in configuration
-
It can be configured globally as a single instance or individually
Get started with LoadSir
The use of LoadSir only needs three simple steps
add dependencies
compile 'com.kingja.loadsir:loadsir:1.3.8'
Step 1: Configuration
global configuration
The global configuration method uses the singleton mode, that is, the obtained configurations are all the same. Can be configured in Application, add status page, set default status page
public class App extends Application { @Override public void onCreate() { super.onCreate(); LoadSir.beginBuilder() .addCallback(new ErrorCallback())//Add various status pages.addCallback (new EmptyCallback()) .addCallback (new LoadingCallback()) .addCallback(new TimeoutCallback()) .addCallback(new CustomCallback()) .setDefaultCallback(LoadingCallback.class)//Set default status page.commit (); } }
Separate configuration
This method can be used if you want to keep the global configuration and add some different configurations to a special page.
LoadSir loadSir = new LoadSir.Builder() .addCallback(new LoadingCallback()) .addCallback(new EmptyCallback()) .addCallback(new ErrorCallback()) .build(); loadService = loadSir.register(this, new Callback.OnReloadListener() { @Override public void onReload(View v) { // 重新加载逻辑 } });
Step 2: Register
Use in Activity
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_content); // Your can change the callback on sub thread directly. LoadService loadService = LoadSir.getDefault().register(this, new Callback.OnReloadListener() { @Override public void onReload(View v) { // 重新加载逻辑 } }); }}
Use in View
ImageView imageView = (ImageView) findViewById(R.id.iv_img); LoadSir loadSir = new LoadSir.Builder() .addCallback(new TimeoutCallback()) .setDefaultCallback(LoadingCallback.class) .build(); loadService = loadSir.register( imageView, new Callback.OnReloadListener() { @Override public void onReload(View v) { loadService.showCallback(LoadingCallback.class); // reload logic } }); Ps: [1] To register a child View of RelativeLayout or ConstraintLayout , if the sub-View is constrained by other sub-Views, it is recommended to wrap another layer of layout outside the sub-View, refer to acitivy_view.xm and activity_constraintlayout.xml
Use in Fragment
Since Fragment is added to Activitiy in various ways, it is quite special, so the registration method in Fragment is different from the above two, let’s look at the template code first:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //Step 1: Get layout View rootView = View.inflate(getActivity(), R.layout.fragment_a_content, null); //Step 2: Register layout View LoadService loadService = LoadSir.getDefault().register(rootView, new Callback.OnReloadListener() { @Override public void onReload(View v) { // Reload logic } }); // Step 3: Return the LoadLayout generated by LoadSir return loadService.getLoadLayout(); }
Step 3: callback
direct callback
protected void loadNet() { // Network access... // Callback loadService.showSuccess();//Successful callback loadService.showCallback(EmptyCallback.class);//Other callbacks }
Converter callback (recommended)
If you don't want to manually perform each callback, you can choose to add a converter when registering, and adapt the corresponding status page according to the returned data.
LoadService loadService = LoadSir.getDefault().register(this, new Callback.OnReloadListener() { @Override public void onReload(View v) { // 重新加载逻辑 }}, new Convertor<HttpResult>() { @Override public Class<? extends Callback> map(HttpResult httpResult) { Class<? extends Callback> resultCode = SuccessCallback.class; switch (httpResult.getResultCode()) { case SUCCESS_CODE://成功回调 if (httpResult.getData().size() == 0) { resultCode = EmptyCallback.class; }else{ resultCode = SuccessCallback.class; } break; case ERROR_CODE: resultCode = ErrorCallback.class; break; } return resultCode; } });
When calling back, directly pass in the data type specified by the converter.
loadService.showWithConvertor(httpResult);
Custom callback page
In order to be completely decoupled, LoadSir does not preset any status pages and needs to be implemented by itself. Developers can customize their own callback pages, such as loading, no data, error, timeout and other common pages, set the layout and customize the click logic
public class CustomCallback extends Callback { // Fill the layout @Override protected int onCreateView() { return R.layout.layout_custom; } //Click event of the current Callback, if it returns true, it will overwrite the registration onReloa(), if it returns false Then execute both, first execute onReloadEvent(). @Override protected boolean onReloadEvent(final Context context, View view) { Toast.makeText(context.getApplicationContext(), "Hello buddy! :p", Toast.LENGTH_SHORT).show(); (view.findViewById(R.id. iv_gift)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context.getApplicationContext(), "It's your gift! :p", Toast.LENGTH_SHORT).show(); } } ); return true; } // Whether to display the original image when displaying the Callback view (SuccessView ), return true to display, false to hide @Override public boolean getSuccessVisible() { return super.getSuccessVisible(); } // Callback when adding Callback to the current view, View is the layout View of the current Callback @Override public void onAttach( Context context, View view) { super.onAttach(context, view); } // The callback when the Callback is removed from the current view, and the View is the layout View of the current Callback @Override public void onDetach() { super.onDetach(context, view); } }
Dynamically modify Callback
loadService = LoadSir.getDefault().register(...); loadService.setCallBack(EmptyCallback.class, new Transport() { @Override public void order(Context context, View view) { TextView mTvEmpty = (TextView) view.findViewById(R.id.tv_empty); mTvEmpty.setText("fine, no data. You must fill it!"); } });
LoadSir comes with a portable Callback
ProgressCallback loadingCallback = new ProgressCallback.Builder() .setTitle("Loading", R.style.Hint_Title) .build(); HintCallback hintCallback = new HintCallback.Builder() .setTitle("Error", R.style.Hint_Title) .setSubTitle("Sorry, buddy, I will try it again.") .setHintImg(R.drawable.error) .build(); LoadSir loadSir = new LoadSir.Builder() .addCallback(loadingCallback) .addCallback(hintCallback) .setDefaultCallback(ProgressCallback.class) .build();