Android 状态第三方库

Android 状态第三方库

1.StatefulLayout 2

githup

这是一个支持多种状态的布局库,包括空布局,错误布局和加载布局。此外,该库还提供了自定义状态的选项。

依赖引入
implementation 'cz.kinst.jakub:android-stateful-layout-base:2.0.7'
使用

在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>

在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) 添加新状态和相应的视图

  • setState(String state) 更改当前状态

  • getState() 返回当前视图状态(字符串ID)

  • setOnStateChangeListener(OnStateChangeListener listener) 设置状态更改事件的侦听器

  • setStateController(StateController stateController) 设置状态控制器对象。请参见下文。

StateController

如果您不想直接使用视图进行操作(MVVM/MPV场景),您可以创建StateController的实例并将其绑定到StatefulLayout(例如使用数据绑定)。StateController允许您设置不同的状态以及控制当前状态本身。请参阅下面的示例或示例项目中的DataBindingControllerActivity。

<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);

可扩展(SimpleStatefulLayout)

如果您想拥有一组完全自定义的状态/视图,则应该使用这个基类。您可以在构造函数中继承和添加自定义状态(例如,请参阅SimpleStatefulLayout),也可以直接使用Stateful布局并通过setStateView()在代码中动态添加状态。原始StatefulLayout只包含一个状态-StatefulLayout.state.CONTENT,无论标记的XML内容中有什么子级。

implementation 'cz.kinst.jakub:android-stateful-layout-simple:2.0.7'

SimpleStatefulLayout扩展了Stateful布局,并为大多数应用程序添加了几个有用的状态-State.OFFLINE、State.PROGRESS、State.EMPTY。它为这些状态提供了可自定义的布局占位符,并提供了一种为状态提供完全自定义布局的方式。

您也可以通过扩展类或使用setStateView()添加状态来自定义它。

使用
<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();
可选属性
  • app:offlineText 处于离线状态时显示的自定义文本

  • app:offlineRetryText 离线状态下重试按钮的文本

  • app:emptyText 处于离线状态时显示的自定义文本

  • app:offlineImageDrawable 显示在离线状态文本上方的自定义图像(如果不使用自定义布局)

  • app:emptyImageDrawable 显示在空状态文本上方的自定义图像(如果不使用自定义布局)

  • app:offlineLayout 处于离线状态时显示的自定义布局

  • app:emptyLayout 处于 EMPTY 状态时显示的自定义布局

  • app:progressLayout 处于 PROGRESS 状态时显示的自定义布局

  • app:state 视图的初始状态(内容进度离线空布局

  • app:stateTextAppearance 当不使用自定义布局时,这是在 离线和 空布局状态下设置 TextView 样式的方法。

API
  • showContent()

  • showProgress()

  • showEmpty()

  • showOffline()

  • setEmptyText(String text) 如果使用默认布局,这将设置在 EMPTY 状态下显示的文本

  • setOfflineText(String text) 如果使用默认布局,这将设置在离线状态下显示的文本

  • setOfflineRetryText(String text) 如果使用默认布局,这将设置在离线状态下显示的重试按钮的文本

  • setOfflineRetryOnClickListener(OnClickListener listener) 如果使用默认布局,这会将单击侦听器设置为在离线状态下显示的重试按钮

  • setEmptyImageDrawable(Drawable drawable) 设置不使用自定义布局时显示在空文本上方的自定义图像

  • setEmptyImageResource(int resourceId) 设置不使用自定义布局时显示在空文本上方的自定义图像

  • setOfflineImageDrawable(Drawable drawable) 设置不使用自定义布局时在离线文本上方显示的自定义图像

  • setOfflineImageResource(int resourceId) 设置不使用自定义布局时在离线文本上方显示的自定义图像

  • setTransitionsEnabled(boolean enabled) 启用/禁用状态之间的转换

Android动画之Transition和TransitionManager使用

2.StateView

StateView 一个轻量级的控件, 继承自 View, 吸收了 ViewStub 的一些特性, 初始状态下是不可见的, 不占布局位置, 占用内存少。 当进行操作显示空/重试/加载视图后, 该视图才会被添加到布局中。

githup

引入

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
dependencies {
    implementation 'com.github.nukc:StateView:v3.0.2'
}
使用方法

直接在代码中使用:

  • 注入到 Activity

    mStateView = StateView.inject(Activity activity);
  • 注入到 View

    mStateView = StateView.inject(View view);
  • 注入到 ViewGroup

    mStateView = StateView.inject(ViewGroup parent);

或添加到布局(这种方式可以更灵活):

    <com.github.nukc.stateview.StateView
        android:id="@+id/stateView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
  • 显示空视图: mStateView.showEmpty();

  • 显示加载视图: mStateView.showLoading();

  • 显示重试视图: mStateView.showRetry();

  • 显示内容: mStateView.showContent();

设置重试点击事件:

    mStateView.setOnRetryClickListener(new StateView.OnRetryClickListener() {
        @Override
        public void onRetryClick() {
            //do something, no need to call showLoading()
            //不需要调用showLoading()方法, StateView自会调用
        }
    });

设置自定义视图:

  • 全局设置办法:在自己项目的layout下新建, 名字跟StateView默认layout一样即可(也不用代码设置). 默认layout的名字:base_empty/base_retry/base_loading.

  • 单页面设置:layout名字不一样, 然后再代码设置.

    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)

利用 OnInflateListener 设置文本图像或者其它操作: 在 view 成功添加到 parent 的时候回调(每个 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>
动画切换

设置视图切换动画:

    // 默认 provider 是 null,即默认不提供动画切换
    // 如果需要,设置一个就可以了
    setAnimatorProvider(AnimatorProvider provider)

动画效果可以自定义,也可以直接使用 animations 这个库,与主库分离,这样不需要的就可以只依赖 library。

    compile 'com.github.nukc.stateview:animations:1.0.1'

目前提供了如下几个动画效果:

  • 渐变缩放: FadeScaleAnimatorProvider

  • 卡片翻转: FlipAnimatorProvider

  • 左右滑动: SlideAnimatorProvider

自定义的话,直接实现 AnimatorProvider接口并提供 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;
    }
}
兼容沉浸式全屏模式

对于是沉浸式全屏模式下的,可以使用此方法补上 statusBar 的 height,从而不覆盖 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

githup

LoadSir是一个高效易用,低碳环保,扩展性良好的加载反馈页管理框架,在加载网络或其他数据时候,根据需求切换状态页面, 可添加自定义状态页面,如加载中,加载失败,无数据,网络超时,如占位图,登录失效等常用页面。可配合网络加载框架,结合返回 状态码,错误码,数据进行状态页自动切换,封装使用效果更佳。

LoadSir的功能及特点

  • ⭐支持Activity,Fragment,Fragment(v4),View状态回调

  • ⭐适配多个Fragment切换,及Fragment+ViewPager切换,不会布局叠加或者布局错乱

  • ⭐利用泛型转换输入信号和输出状态,可根据网络返回体的状态码或者数据返回自动适配状态页,实现全局自动状态切换

  • ⭐无需修改布局文件

  • ⭐只加载唯一一个状态视图,不会预加载全部视图

  • ⭐不需要设置枚举或者常量状态值,直接用状态页类类型(xxx.class)作为状态码

  • ⭐可对单个状态页单独设置点击事件,根据返回boolean值覆盖或者结合OnReloadListener使用,如网络错误可跳转设置页

  • ⭐无预设页面,低耦合,开发者随心配置

  • ⭐可保留标题栏(Toolbar,titile view等)

  • 可设置重新加载点击事件(OnReloadListener)

  • 可自定义状态页(继承Callback类)

  • 可在子线程直接切换状态

  • 可设置初始状态页(常用进度页作为初始状态)

  • 可扩展状态页面,在配置中添加自定义状态页

  • 可全局单例配置,也可以单独配置

开始使用LoadSir

LoadSir的使用,只需要简单的三步

添加依赖
compile 'com.kingja.loadsir:loadsir:1.3.8'
第一步:配置
全局配置方式

全局配置方式,使用的是单例模式,即获取的配置都是一样的。可在Application中配置,添加状态页,设置默认状态页

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LoadSir.beginBuilder()
                .addCallback(new ErrorCallback())//添加各种状态页
                .addCallback(new EmptyCallback())
                .addCallback(new LoadingCallback())
                .addCallback(new TimeoutCallback())
                .addCallback(new CustomCallback())
                .setDefaultCallback(LoadingCallback.class)//设置默认状态页
                .commit();
    }
}
单独配置方式

如果你即想保留全局配置,又想在某个特殊页面加点不同的配置,可采用该方式。

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) {
                // 重新加载逻辑
            }
        });
第二步:注册
在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) {
            // 重新加载逻辑
        }
    });
}}
在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);
        // 重新加载逻辑
    }
});
Ps:
[1]要注册RelativeLayout或ConstraintLayout的子View,如果该子View被其它子View约束,建议在子View外层再包一层布局,参考
acitivy_view.xm和activity_constraintlayout.xml
在Fragment 中使用

由于Fragment添加到Activitiy方式多样,比较特别,所以在Fragment注册方式不同于上面两种,大家先看模板代码:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle
        savedInstanceState) {
    //第一步:获取布局View
    rootView = View.inflate(getActivity(), R.layout.fragment_a_content, null);
    //第二步:注册布局View
    LoadService loadService = LoadSir.getDefault().register(rootView, new Callback.OnReloadListener() {
        @Override
        public void onReload(View v) {
            // 重新加载逻辑
        }
    });
    //第三步:返回LoadSir生成的LoadLayout
    return loadService.getLoadLayout();
}
第三步: 回调
直接回调
protected void loadNet() {
        // 进行网络访问...
        // 进行回调
        loadService.showSuccess();//成功回调
        loadService.showCallback(EmptyCallback.class);//其他回调
    }
转换器回调 (推荐使用)

如果你不想再每次回调都要手动进行的话,可以选择注册的时候加入转换器,可根据返回的数据,适配对应的状态页。

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;
    }
});

回调的时候直接传入转换器指定的数据类型。

loadService.showWithConvertor(httpResult);
自定义回调页

LoadSir为了完全解耦,没有预设任何状态页,需要自己实现,开发者自定义自己的回调页面,比如加载中,没数据,错误,超时等常用页面, 设置布局及自定义点击逻辑

public class CustomCallback extends Callback {
​
    //填充布局
    @Override
    protected int onCreateView() {
        return R.layout.layout_custom;
    }
    //当前Callback的点击事件,如果返回true则覆盖注册时的onReloa(),如果返回false则两者都执行,先执行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;
    }
​
    //是否在显示Callback视图的时候显示原始图(SuccessView),返回true显示,false隐藏
    @Override
    public boolean getSuccessVisible() {
        return super.getSuccessVisible();
    }
​
    //将Callback添加到当前视图时的回调,View为当前Callback的布局View
    @Override
    public void onAttach(Context context, View view) {
        super.onAttach(context, view);
    }
​
    //将Callback从当前视图删除时的回调,View为当前Callback的布局View
    @Override
    public void onDetach() {
        super.onDetach(context, view);
    }
​
}
动态修改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自带便携式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();

4.empty-view

githup

5.EasyRecyclerView

推荐使用这个

猜你喜欢

转载自blog.csdn.net/fromVillageCoolBoy/article/details/131478870
今日推荐