android客户端界面加载处理方法

本文介绍了一种自定义Android View的方法,用于处理应用中常见的加载状态显示问题,包括加载中、加载成功、加载失败等场景,并提供了详细的实现步骤。

Knowledge is a treasure but practice is the key to it.

在android的前端开发过程中,对于前端的加载效果,界面友好是很重要的。

需要在界面加载过程中的变化中友好的显示:

1:加载中
2:加载成功
3:加载失败
4:网络异常
5:请求数据接口暂时没有数据

etc,所以,需要我们自己书写相关的界面加载类,来做一个统一的整理

在刚开始遇到这样的问题时,就个人而言,用的最笨的办法也是最耗性能。在原有的正常UI界面中添加 include 标签,然后根据逻辑进行隐藏显示。很不自在。之后在优化中总结经验,创建自己的界面类。

正题:

step1:自定义View extends FrameLayout
step2:创建enum来确定相应的状态
step3:创建相应界面的layout

package com.welive.defineview;

import ···


/**
* author:wedfrend
* email:wedfrend@yeah.net
* create:2017/8/29 11:11
* desc: 在手机端加载过程中,会出现很多的状况
 *
 *      1:加载中
 *      2:数据出错
 *      3:网络异常
 *      4:重新加载
 *
 *      为了方便的统一管理,所以自定义View来进行相应的加载状态方法
 *
*/

public class NavFrameLayout extends FrameLayout {

    private FrameLayout wholeFrameLayout;
    //布局中的子页面
    private View mContentView;
    private LayoutInflater layoutInflater;
    //不同的状态
    private View statusView;

    private EnumContent.statusPage statusPage = EnumContent.statusPage.LOADING;

    public EnumContent.statusPage getStatusPage() {
        return statusPage;
    }

    public void setStatusPage(EnumContent.statusPage statusPage) {
        this.statusPage = statusPage;
    }

/**
*  默认情况下的三种布局界面
*/
    private final int loadingLayoutId = R.layout.z_loading_layout;//加载中
    private final int dateErrorLayoutId = R.layout.z_daterror_layout;//数据异常
    private final int netErrorLayoutId = R.layout.z_neterror_layout;//网络异常

    public NavFrameLayout(Context context) {
        super(context,null);
    }

    public NavFrameLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public NavFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        InitDefault(context, attrs, defStyleAttr);
    }
    //初始化
    public void InitDefault(Context context, AttributeSet attrs, int defStyleAttr){
        wholeFrameLayout = new FrameLayout(context);
        layoutInflater = LayoutInflater.from(context);
        wholeFrameLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        //该方法可以在父类中查看,是直接将该布局放置在子布局的第一个位置
        addView(wholeFrameLayout);
        //加载界面的显示
        setViewLoading(loadingLayoutId);

    }

/**
进行界面的判断,在layout下该自定义VIEW下必须有且只有一个子布局Ui,但是实际上我们在代码中添加一个View,layout中一个,所以判断必须为2
*/
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if(getChildCount() != 2){
            //抛出运行时异常
            throw new RuntimeException(NavFrameLayout.class.getSimpleName() + "必须有且只有一个子控件");
        }
        mContentView = getChildAt(1);
        //完成之后将该View先进行隐藏
        mContentView.setVisibility(View.GONE);
    }

    /**
     * 正在加载
     * @param layoutResID
     */
    protected SwipeRefreshLayout swipeRefreshLayout;
    public void setViewLoading(@LayoutRes int layoutResID){
    //赋值状态
        statusPage = EnumContent.statusPage.LOADING;

    //判断是否statusView是否已经存在,存在的话先做一次清除,因为没有必要一直存在
        if (statusView != null && statusView.getParent() != null) {
            ViewGroup parent = (ViewGroup) statusView.getParent();
            parent.removeView(statusView);
        }
        //接下来做自己界面流程
        statusView = layoutInflater.inflate(layoutResID,null);
        if (statusView != null) {
            statusView.setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            wholeFrameLayout.addView(statusView);
        }
        statusView.setVisibility(View.VISIBLE);
        swipeRefreshLayout = ((SwipeRefreshLayout) statusView.findViewById(R.id.refresh_loading));
        swipeRefreshLayout.setProgressViewOffset(true, 50, 100);
        //设置下拉圆圈的大小,两个值 LARGE, DEFAULT
        swipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT);
        //下拉刷新调用的一个接口
        swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary, R.color.colorPrimaryDark, R.color.colorPrimaryDarkContentActivity);
        //这里直接调用swipeRefrehLayout的加载状态
        swipeRefreshLayout.post(() -> swipeRefreshLayout.setRefreshing(true));
    }

    /**
     * 设置界面正常加载的情况下,对于不同状态需要做的处理如下,在自己主逻辑程序中只需调用方法,相应的处理状态交给该方法执行
     */
    public void setNormal(){
        if(statusView.getVisibility() == View.VISIBLE){
            //异常界面隐藏
            statusView.setVisibility(View.GONE);
            //正常界面显示
            mContentView.setVisibility(View.VISIBLE);
        }
        //处理不同的情况
        if(statusPage.equals(EnumContent.statusPage.NONE)){
        }
        //正在加载中
        if(statusPage.equals(EnumContent.statusPage.LOADING)){
            //加载界面显示
            swipeRefreshLayout.post(() -> swipeRefreshLayout.setRefreshing(false));
            return;
        }
        //数据出现异常
        if(statusPage.equals(EnumContent.statusPage.DATEERROR)){

            return;
        }
        //网络出现异常
        if(statusPage.equals(EnumContent.statusPage.NETERROR)){

            return;
        }
        setStatusPage(EnumContent.statusPage.NONE);


    }

    /**
     *
     * @param status 异常状态
     * @param imgId  图片ID
     * @param StringId 提示字段ID
     */
    public void setException(EnumContent.statusPage status,int imgId,int StringId){
        if(statusPage.equals(EnumContent.statusPage.NONE)){
            T.show(R.string.NetError,0);
        }
        statusPage = status;
        if(statusView.getVisibility() == View.GONE){
            //正常界面隐藏
            mContentView.setVisibility(View.GONE);
            //异常界面显示
            statusView.setVisibility(View.VISIBLE);
        }
        //处理不同的情况
        if(status.equals(EnumContent.statusPage.NONE)){

        }
        //正在加载中
        if(status.equals(EnumContent.statusPage.LOADING)){
            //加载界面显示
            setViewLoading(loadingLayoutId);
            return;
        }
        //数据出现异常
        if(status.equals(EnumContent.statusPage.DATEERROR)){
            setViewDateError(dateErrorLayoutId);
            return;
        }
        //网络出现异常
        if(status.equals(EnumContent.statusPage.NETERROR)){
            setViewNetError(netErrorLayoutId);
            return;
        }

    }


    /**
     * 数据异常
     * @param layoutResID
     */
    public void setViewDateError(@LayoutRes int layoutResID){

        if (statusView != null && statusView.getParent() != null) {
            ViewGroup parent = (ViewGroup) statusView.getParent();
            parent.removeView(statusView);
        }
        statusView = layoutInflater.inflate(layoutResID,null);
        if (statusView != null) {
            statusView.setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            wholeFrameLayout.addView(statusView);
        }
        //下面要做些事情

    }

    /**
     * 网络异常
     * @param layoutResID
     */
    public void setViewNetError(@LayoutRes int layoutResID){

        if (statusView != null && statusView.getParent() != null) {
            ViewGroup parent = (ViewGroup) statusView.getParent();
            parent.removeView(statusView);
        }
        statusView = layoutInflater.inflate(layoutResID,null);
        if (statusView != null) {
            statusView.setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            wholeFrameLayout.addView(statusView);
        }
        statusView.findViewById(R.id.tv_reloading).setOnClickListener(v ->
        {
            setException(EnumContent.statusPage.LOADING,0,0);
            loadingListener.onLoading();
        });
    }

    //这里还需要一个接口的触发时间,来证实界面的可行性
    public interface LoadingListener{
        void onLoading();
    }

    private LoadingListener loadingListener;

    public LoadingListener getLoadingListener() {
        return loadingListener;
    }

    public void setLoadingListener(LoadingListener loadingListener) {
        this.loadingListener = loadingListener;
    }
}

知识要点:

1:该类中需要注意的就是一个onFinishInflate()方法的判断以及我们在原有布局中添加一个布局。
2:addView()方法调用父类的方法

/**
     * <p>Adds a child view. If no layout parameters are already set on the child, the
     * default parameters for this ViewGroup are set on the child.</p>
     *
     * <p><strong>Note:</strong> do not invoke this method from
     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
     *
     * @param child the child view to add
     *
     * @see #generateDefaultLayoutParams()
     */
    public void addView(View child) {
        addView(child, -1);
    }

所以此时getChildAt(int i)中,角标0表示我们添加的View,而角标为1的表示在layout中的View

debug模式下的流程分析

3:对于各种状态的布局以及状态发生变化的时候先清空之前的内容,在加载新的界面。

由于不同公司的定制,如果将该类作为aar文件来进行引用反而会带来更加复杂的封装,所以还是由每个开发自己去写一套适用于自己公司的类更为合适。

最终在界面绘制完成,跟布局中有两个子布局

跟布局下的子布局

相关实践代码请在Github中进行下载:

https://github.com/wedfrendwang/NavLayout


Everyone gets tired.No one can take the pain for you. You have to go through it and grow up.

心烦事比较多,激励一下自己。希望从事编程行业的同仁们加油。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值