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

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.

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值