`
wx1568145609
  • 浏览: 17537 次
文章分类
社区版块
存档分类
最新评论

Android——页面Loading控件封装

 
阅读更多

LoadingLayout效果

概述

在Android开发过程中通常在有网络请求的页面,需要设计加载中、加载失败等UI效果,来提升用户体验。本文就此需求实现了一个简单的LoadingLayout控件,可以比较方便的实现加载中、加载失败、网络错误等UI效果,并提供失败点击重试等操作。

实现思路

常用一般有以下几种请求状态:

  • LOADING_STATE 加载中状态
  • LOAD_SUCCESS_STATE 加载成功状态
  • LOAD_FAILURE_STATE 加载失败状态(包含超时、404、接口数据错误等)
  • NULL_DATA_STATE 没有数据状态(接口访问成功,接口没有数据)
  • NET_ERROR_STATE 网络错误状态(未连接网络)

除加载成功状态每种状态都对应着一种UI效果对应各自View,不同状态的View的展示和消失肯定是通过在ViewGroup添加和删除View实现的,所以我们的LoadingLayout需要继承一个ViewGroup,此ViewGroup我们选择为FrameLayout,不同状态的View可以通过自定义属性自由指定,然后通过开放接口实现切换UI的效果就可以了。

代码实现

定义自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="loading_layout_id" type="id" />

    <declare-styleable name="LoadingLayout">
        <attr name="loadingViewDrawable" format="reference"/>
        <attr name="loadFailureViewDrawable" format="reference"/>
        <attr name="netErrorViewDrawable" format="reference"/>
        <attr name="nullDataViewDrawable" format="reference"/>
    </declare-styleable>
</resources>

定义了一个默认ID和四种UI状态的属性。

java代码实现

public class LoadingLayout extends FrameLayout {
    private static final String TAG = LoadingLayout.class.getSimpleName();

    /** 加载中 */
    public static final int LOADING_STATE = 0x001;
    /** 加载成功 */
    public static final int LOAD_SUCCESS_STATE = 0x002;
    /** 网络错误 */
    public static final int NET_ERROR_STATE = 0x003;
    /** 加载失败,超时、接口错误、404等 */
    public static final int LOAD_FAILURE_STATE = 0x004;
    /** 没有数据 */
    public static final int NULL_DATA_STATE = 0x005;

    private View loadingView;       //loading 加载中。。
    private View netErrorView;      //网络错误View
    private View loadFailureView;   //加载失败View
    private View nullDataView;      //没有数据View

    //定义注释限定参数值,可以去掉
    @IntDef({LOADING_STATE, LOAD_SUCCESS_STATE, NET_ERROR_STATE, LOAD_FAILURE_STATE,NULL_DATA_STATE})
    public @interface LoadingState{}

    private final LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

    public LoadingLayout(@NonNull Context context) {
        this(context, null);
    }

    public LoadingLayout(@NonNull  Context context, @NonNull  AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadingLayout(@NonNull  Context context, @NonNull  AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //指定默认ID
        if(getId() == -1){
            setId(R.id.loading_layout_id);
        }

        try{
            TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.LoadingLayout, 0, 0);
            //为每种状态指定默认效果
            int progressViewRes = a.getResourceId(R.styleable.LoadingLayout_loadingViewDrawable,
                    R.layout.default_loading_layout);
            int netErrorViewRes = a.getResourceId(R.styleable.LoadingLayout_netErrorViewDrawable,
                    R.layout.default_net_error_layout);
            int loadFailureViewRes = a.getResourceId(R.styleable.LoadingLayout_loadFailureViewDrawable,
                    R.layout.default_load_failure_layout);
            int nullDataViewRes = a.getResourceId(R.styleable.LoadingLayout_nullDataViewDrawable,
                    R.layout.default_null_data_layout);

            loadingView = LayoutInflater.from(context).inflate(progressViewRes, null, true);
            netErrorView = LayoutInflater.from(context).inflate(netErrorViewRes, null, true);
            loadFailureView = LayoutInflater.from(context).inflate(loadFailureViewRes, null, true);
            nullDataView = LayoutInflater.from(context).inflate(nullDataViewRes, null, true);
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            a.recycle();
        }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        this.layoutParams.width = this.getMeasuredWidth();
        this.layoutParams.height = this.getMeasuredHeight();
    }


    /**
     * 通过状态刷新界面显示
     * @param state 当前状态
     */
    public void refreshView(@LoadingState int state){
        refreshView(state, null);
    }

    /**
     * 通过状态刷新界面显示
     * @param state 当前状态
     * @param onClickListener 点击监听
     */
    public void refreshView(@LoadingState int state, OnClickListener onClickListener){
        switch (state){
            case LOAD_SUCCESS_STATE:
                //加载成功,移除所有状态View
                removeStateView();
                break;
            case LOADING_STATE:
                showLoading();
                break;
            case NET_ERROR_STATE:
                showNetErrorMessage(onClickListener);
                break;
            case LOAD_FAILURE_STATE:
                showLoadFailureMessage(onClickListener);
                break;
            case NULL_DATA_STATE:
                showNullDataMessage();
                break;
            default:
                Log.d(TAG, "state error");
                break;
        }
    }

    /**
     * 显示loading
     */
    public void showLoading(){
        removeStateView();

        if(loadingView != null){
            this.addView(loadingView);
        } else{
            Log.d(TAG, "Please init loadingView first");
        }
    }

    /**
     * 显示网络错误提示
     */
    public void showNetErrorMessage(){
       showNetErrorMessage(null);
    }

    /**
     * 显示网络错误提示
     * @param onClickListener 指定点击效果监听
     */
    public void showNetErrorMessage(OnClickListener onClickListener){
        removeStateView();

        if(netErrorView != null){
            if(onClickListener != null){
                netErrorView.setOnClickListener(onClickListener);
            }
            this.addView(netErrorView);
        } else{
            Log.d(TAG, "Please init netErrorView first");
        }
    }

    /**
     * 显示无数据提示
     */
    public void showNullDataMessage(){
        removeStateView();

        if(nullDataView != null){
            this.addView(nullDataView);
        } else{
            Log.d(TAG, "Please init nullDataView first");
        }
    }

    /**
     * 显示加载失败提示
     */
    public void showLoadFailureMessage(){
        showLoadFailureMessage(null);
    }

    /**
     * 显示加载失败提示
     * @param onClickListener 指定点击效果监听
     */
    public void showLoadFailureMessage(OnClickListener onClickListener){
        removeStateView();

        if(loadFailureView != null){
            if(onClickListener != null){
                loadFailureView.setOnClickListener(onClickListener);
            }

            this.addView(loadFailureView);
        } else{
            Log.d(TAG, "Please init loadFailureView first");
        }
    }

    /**
     * 移除所有状态View
     */
    private void removeStateView(){
        if(loadingView != null){
            this.removeView(loadingView);
        }
        if(netErrorView != null){
            this.removeView(netErrorView);
        }
        if(loadFailureView != null){
            this.removeView(loadFailureView);
        }
        if(nullDataView != null){
            this.removeView(nullDataView);
        }
    }
}

代码注释比较详细了,不过多解释。

默认UI样式

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <ProgressBar
        android:layout_width="50dp"
        android:layout_height="50dp"
        style="@android:style/Widget.ProgressBar.Large"
        android:layout_centerInParent="true"
        android:indeterminateDrawable="@drawable/loading_anim"/>
</RelativeLayout>

loading_anim:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/loading"
    android:duration="100"
    android:fromDegrees="0"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="720" />

其他的样式比较简单,就不一一列举了。

使用

布局:

    <com.zhong.loadingview.view.LoadingLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintBottom_toTopOf="@+id/net_error_btn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

        <ImageView
            android:id="@+id/image_view"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center" />

    </com.zhong.loadingview.view.LoadingLayout>

需要在父布局中引用自定义属性LoadingLayout。

代码实现:

在代码中需要通过loadingLayout.showLoading();,loadingLayout.refreshView(state);等方法来控制状态展示效果。

扩展

在写完这个控件之后发现网上有好多类似的实现,好吧。。。至少说明我的实现思路应该是没问题的。但是这种实现其实并不完美:

  • 每次使用时需要在父布局中使用自定义布局LoadingLayout略显繁琐;
  • 在某些特殊布局中需要为此效果单独添加一层父布局LoadingLayout,会在一定程度上造成资源上的浪费;
  • 因为使用了自定义属性和布局指定默认UI样式,也不利于维护和使用。

源码:LoadingLayout

转载于:https://my.oschina.net/zhongsm/blog/3068932

分享到:
评论

相关推荐

    Android自绘制Loading控件

    "Android自绘制Loading控件"是一个具体的实践示例,它演示了如何在Android应用中创建一个自定义的加载指示器(Loading)控件。这个过程涉及到对Android View系统、绘图基础以及动画原理的理解。 首先,我们要理解自...

    ASP.net始初化页面Loading控件

    本文将深入探讨如何在ASP.NET中创建一个自定义的初始化页面Loading控件,并解释这两个方法的用途。 首先,让我们来看看"页面初始化"(Page_Init)事件。在页面生命周期中,Page_Init是最早触发的事件之一,它发生在...

    Android 自定义全局Loading页面

    在Android应用开发中,为了提供良好的用户体验,我们经常需要在数据加载、网络请求或其它耗时操作时显示全局Loading页面,以告知用户程序正在进行后台处理并提示他们稍等片刻。本篇文章将深入探讨如何在Android中...

    Android 适用各种布局的loading 加载

    本示例着重探讨的是如何在Android应用中实现各种布局的"loading"加载效果,特别是在ListView等滚动视图中,采用类似iOS水滴形态的加载设计,提供更优雅的用户体验。 首先,我们来看`spots-dialog-master`这个项目,...

    可设置加载结果的Loading控件

    在Android开发中,可以使用ProgressBar、SwipeRefreshLayout或者自定义View来创建Loading效果;在iOS开发中,我们可以使用UIActivityIndicatorView、SVProgressHUD等第三方库;而在前端开发中,JavaScript库如jQuery...

    基于Android的Android项目之——页面特效集合(附源码).zip

    "基于Android的Android项目之——页面特效集合(附源码)"提供了一系列的示例代码,旨在帮助开发者,尤其是毕业生,掌握和应用各种动画效果。这个项目不仅包含源码,还兼容了多种开发环境,如Android Studio、...

    解决Android Studio Loading Devices问题

    解决Android Studio Loading Devices问题

    安卓Android源码——Android远程登录含有loading登录效.zip

    本资源“安卓Android源码——Android远程登录含有loading登录效.zip”显然是一个包含Android应用程序源码的压缩包,特别关注的是远程登录功能以及登录过程中的加载效果。这里我们将详细探讨Android远程登录的实现...

    Android源码——远程登录含有loading登录效果源码.zip

    Android源码——远程登录含有loading登录效果源码.zip

    Android项目之——页面特效集合(附源码).zip

    这个"Android项目之——页面特效集合(附源码)"提供了一系列的示例,帮助开发者学习和掌握如何在Android应用中实现各种炫酷的页面效果。下面,我们将深入探讨其中可能包含的一些关键知识点。 1. **动画效果**:...

    安卓Android源码——远程登录含有loading登录效.zip

    这个名为“安卓Android源码——远程登录含有loading登录效”的压缩包文件,显然包含了关于实现远程登录功能并带有加载效果(loading)的源代码示例。远程登录通常涉及到网络通信和数据传输,而loading效果则是提升...

    Android动态点点省略号闪烁效果的等待控件

    在Android开发中,有时我们需要为用户展示一个正在加载或处理数据的状态,这时就需要用到等待控件。"Android动态点点省略号闪烁效果的等待控件"是一种常见的设计,它通过连续显示“...”来表示程序正在进行后台操作...

    安卓Android源码——远程登录含有loading登录效果源码.zip

    这份"安卓Android源码——远程登录含有loading登录效果源码.zip"包含了实现这一功能的具体源代码,以及相关的图片和说明文档。 首先,我们需要理解远程登录的基本原理。远程登录通常涉及到网络通信,如HTTP或HTTPS...

    android 3种自定义 Loading加载

    在Android应用开发中,"加载"(Loading)通常是指一种用户界面元素,它在应用程序执行耗时操作(如网络请求、数据库操作等)时显示,以告知用户程序正在处理数据并提供反馈。自定义Loading可以帮助开发者创建更具...

    Android常用对话框的封装

    本文将详细讲解如何封装Android中的九种常用对话框,并提供实际应用的指导。 首先,我们来看看这九种常见的Android对话框类型: 1. **普通对话框(AlertDialog)**:基础对话框,可以包含标题、内容和按钮,通常...

    WPF 用户控件 Loading 效果

    "WPF 用户控件 Loading 效果"通常指的是设计一个自定义的用户控件,用于在界面操作正在进行时向用户显示一个动画式的加载指示。 在WPF中实现Loading效果,我们可以利用以下几个关键知识点: 1. **动画(Animation)*...

    android自定义loading图片

    在Android应用开发中,自定义loading图片是一种常见的需求,它能提供给用户更友好的交互体验,尤其是在数据加载、网络请求或复杂计算时。自定义loading不仅能够展示应用的个性化,还能通过动态效果缓解用户的等待...

    安卓Android源码——远程登录含有loading登录效果~~完整代码和超级详细注释.zip

    这份"安卓Android源码——远程登录含有loading登录效果~~完整代码和超级详细注释.zip"文件提供了一个完整的示例,帮助开发者了解如何在Android应用中实现带有加载进度条(loading)的登录效果。以下是对这个项目中的...

    Android loading...加载动画

    在Android开发中,用户体验是至关重要的,而加载动画(loading animation)则是提升用户体验的一个关键元素。当应用程序需要处理一些后台任务,如数据加载、网络请求等,加载动画可以提供反馈,让用户知道应用正在...

Global site tag (gtag.js) - Google Analytics