如何快速实现app网络加载中网络异常、空数据等页面?如何实现下拉刷新和加载更多等功能?

  • 在项目开发中如何快速处理网络加载中错误页面、空数据页面、网络异常等页面?
  • 在项目开发中如何快速实现“上拉刷新”与“加载更多”?
过去做法:
  • NetFragment 处理网络加载的页面
    • 采用“约定优于配置原则”,直接规定好错误页面、空数据页面、网络异常等页面的Id
    • NetFragment负责加载数据、控制错误页面、空数据页面、网络异常页面显示隐藏
  • ListNetFragment处理下拉刷新和加载更多的界面
    • 处理下拉刷新和加载更多的逻辑
    • 控制加载数据、控制错误页面、空数据页面、网络异常页面显示隐藏

存在的问题:
- 下拉刷新如果失败,listview上面的headView需要保留(不能实现)
- 下拉刷新加载过程中,上拉刷新出现崩溃Bug
- 控制逻辑C和页面展示V 没有真正分离,可扩展性差

现在做法:
  • 剥离网络加载控制逻辑
  • 从NetFragment中抽取NetController,处理网络加载控制逻辑
  • 从ListNetFragment中抽取ListNetController,处理下拉刷新和加载更多的逻辑

  • 重写NetFragment,实现NetController与各种页面的绑定

  • 重写ListNetFragment,实现ListNetController与各种页面的绑定

资源下载:
使用教程
1. NetFragment
  • 绘制Xml eg:fragment_exam
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/net_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    //加载中   id为net_progress
    <LinearLayout
        android:id="@+id/net_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical">

        <ProgressBar
            style="?android:attr/progressBarStyle"
            android:layout_width="200dip"
            android:layout_height="wrap_content"
            android:indeterminate="true"
            android:indeterminateOnly="true" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="数据正在加载中……"
            android:textColor="#000000" />
    </LinearLayout>
    
    //数据为空的界面  id为net_no_result
    <TextView
        android:id="@+id/net_no_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="数据为空"
        android:textColor="#000000" />

    //数据错误  id为net_error
    <TextView
        android:id="@+id/net_error"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="数据出错"
        android:textColor="#000000" />
        
     //参数错误  id为net_fail
    <LinearLayout
        android:orientation="vertical"
        android:id="@+id/net_fail"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include  layout="@layout/nonata_2"/>

    </LinearLayout>

    //网络异常 id为net_cannot_access
    <LinearLayout
        android:orientation="vertical"
        android:id="@+id/net_cannot_access"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include  layout="@layout/nonata_2"/>
    </LinearLayout>


     //有数据  id为net_result
    <LinearLayout
        android:orientation="vertical"
        android:id="@+id/net_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include  layout="@layout/nonata_2"/>
    </LinearLayout>

</FrameLayout>
  • 网络加载的Fragment继承NetFragment eg: ExamFragment
public class ExamFragment extends NetFragment<GetSelfOfficialExamNameNetResultInfo> {
    private static final String TAG = ExamFragment.class.getSimpleName();
    @Bind(B.id.nav_go_back)
    ImageView navGoBack;
    @Bind(B.id.nav_tv_title)
    TextView navTvTitle;
    @Bind(B.id.nav_tv_right)
    TextView navTvRight;
    @Bind(B.id.tv_exam_choose_1)
    TextView tvExamChoose1;
    @Bind(B.id.tv_exam_choose_2)
    TextView tvExamChoose2;
    @Bind(B.id.ll_item_0)
    LinearLayout llItem0;
    @Bind(B.id.ll_item_1)
    LinearLayout llItem1;
    @Bind(B.id.tv_item_0_name)
    TextView tvItem0Name;
    @Bind(B.id.tv_item_1_name)
    TextView tvItem1Name;
    private GetSelfOfficialExamNameNetResultInfo resultInfo;//网络请求的数据模型

     /**
     * 获取布局Id
     */
    @Override
    public int getLayoutId() {
        return R.layout.fragment_exam;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = super.onCreateView(inflater, container, savedInstanceState);
        navGoBack.setVisibility(View.INVISIBLE);
        navTvTitle.setText("考点儿");
        navTvRight.setText("考试管理");
        return rootView;
    }

    /**
     * 网络请求
     */
    @Override
    public GetSelfOfficialExamNameNetResultInfo onDoInBackgroundSafely() {
        GetSelfOfficialExamNameNetResultInfo.Request params = new GetSelfOfficialExamNameNetResultInfo.Request();
        return RepositoryCollection.getSelfOfficialExamName(params);
    }

    /**
     *  请求成功时 
     */
    @Override
    protected void onDisplayResult(GetSelfOfficialExamNameNetResultInfo resultInfo) {
        this.resultInfo = resultInfo;
        tvItem0Name.setText(resultInfo.getSelfExamName());
        tvItem1Name.setText(resultInfo.getOfficialExamName());
    }

}

2 ListNetFragment 的使用
  • 绘制XML eg: fragment_teacher
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/net_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <ListView
            android:id="@+id/net_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="@dimen/width_720_1280_20"
            android:layout_marginRight="@dimen/width_720_1280_20"
            android:background="@color/bg_window"
            android:clipToPadding="false"
            android:divider="@color/color_underline"
            android:dividerHeight="@dimen/hs1" />

    </android.support.v4.widget.SwipeRefreshLayout>

</FrameLayout>
  • 绘制异常界面的Xml 作为ListView的item eg:net_list_abnormal_layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/net_no_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="@dimen/hs200">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:clickable="false"
            android:gravity="center"
            android:text="@string/net_no_result"
            android:textColor="#000000" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/net_error"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="@dimen/hs200">

        <include layout="@layout/nonata_2" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/net_fail"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ndroid:orientation="vertical"
        android:paddingTop="@dimen/hs200">

        <include layout="@layout/nonata_2" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/net_cannot_access"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="@dimen/hs200">
        <include layout="@layout/nonata_2" />
    </LinearLayout>

</FrameLayout>

  • 继承ListNetFragment
public class TeacherFragment extends ListNetFragment<AppArticleModel> {
    private static final String TAG = TeacherFragment.class.getSimpleName();
    @Bind(B.id.nav_go_back)
    ImageView navGoBack;
    @Bind(B.id.nav_tv_title)
    TextView navTvTitle;
    @Bind(B.id.iv_teacher_photo)
    ImageView ivTeacherPhoto;
    @Bind(B.id.tv_teacher_name_1)
    TextView tvTeacherName1;
    @Bind(B.id.tv_teacher_name_2)
    TextView tvTeacherName2;
    @Bind(B.id.tv_teacher_name_3)
    TextView tvTeacherName3;
    @Bind(B.id.ll_log_status)
    LinearLayout llLogStatus;
    private int teacherId;
    private DisplayImageOptions options;

    private AppTeacherModel teacherModel;

    @Override
    public int getLayoutId() {
        return R.layout.fragment_teacher;
    }
  // 获取网络异常 空数据 等界面布局
    @Override
    public int getAbnormalViewLayoutId() {
        return R.layout.net_list_abnormal_layout;
    }

  
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = super.onCreateView(inflater, container, savedInstanceState);

      //  mNetController.setPageSize(20);        //   设置每次加载个数  默认为10
        mNetController.addHeadView(headView);  //  添加头布局  没有可以不写
        mNetController.setListAdapter();       // 注: 在addHeadView(headView)之后,
      //  mNetController.setRefreshEnable(false); //能否下拉刷新  默认为true
      //  mNetController.setLoadMoreEnable(false);//能否加载更多  默认为true
        
        return rootView;
    }

    @Override
    protected ListNetResultInfo<AppArticleModel> onDoInBackgroundSafely(int i, int i1) {
        GetTeacherInfoNetResultInfo.Request params = new GetTeacherInfoNetResultInfo.Request();
        PageModel pageModel = new PageModel();
        pageModel.setStartIndex(i);
        pageModel.setPageSize(i1);
        params.setPage(pageModel);
        params.setTeacherId(teacherId);
        return RepositoryCollection.getTeacherInfo(params);
    }

  // list item任意一TextView的Id
  
    @Override
    public int getItemTextViewResourceId() {
        return R.id.tv_content;
    }

  //list item的布局
    @Override
    public int getItemLayoutId() {
        return R.layout.item_list_teacher;
    }

    //用于 处理list的item
    @Override
     public View bindView(int i, View view, ViewGroup viewGroup) {
         ViewHolder holder;
         if (view == null) {
             view = View.inflate(getContext(), R.layout.item_list_teacher, null);
         }
         holder = (ViewHolder) view.getTag();
         if (holder == null) {
             holder = new ViewHolder(view);
             view.setTag(holder);
         }
         final AppArticleModel model = getItem(i);
         
         holder.tvContent.setText(model.getTitle());
         String shareTime = TimeUtil.longToString(model.getCreateDate(), TimeUtil.FORMAT_DATE);
         holder.tvShareTime.setText(shareTime);
         holder.tvPreview.setText("" + model.getViewNum());
        
         return view;
     }
 
     class ViewHolder {
         @Bind(B.id.tv_title)
         TextView tvTitle;
         @Bind(B.id.tv_content)
         TextView tvContent;
         @Bind(B.id.tv_share_time)
         TextView tvShareTime;
         @Bind(B.id.tv_preview)
         TextView tvPreview;
         @Bind(B.id.tv_item_teacher_zambia)
         TextView tvItemTeacherZambia;
         @Bind(B.id.ll_item)
         LinearLayout ll_item;
 
         ViewHolder(View view) {
             ButterFork.bind(this, view);
         }
     }

}
解决SwipeRefreshLayout与ViewPager 的滑动冲突

通过重写SwipeRefreshLayout的onInterceptTouchEvent方法

public class BannerSwipeRefreshLayout extends SwipeRefreshLayout {
    public BannerSwipeRefreshLayout(Context context) {
        super(context);
    }

    public BannerSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    float lastx = 0;
    float lasty = 0;
    boolean ismovepic = false;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            lastx = ev.getX();
            lasty = ev.getY();
            ismovepic = false;
            return super.onInterceptTouchEvent(ev);
        }

        final int action = MotionEventCompat.getActionMasked(ev);

        int x2 = (int) Math.abs(ev.getX() - lastx);
        int y2 = (int) Math.abs(ev.getY() - lasty);

        //滑动图片最小距离检查
        if (x2 > y2) {
            if (x2 >= 100) ismovepic = true;
            return false;
        }

        //是否移动图片(下拉刷新不处理)
        if (ismovepic) {
            return false;
        }
        boolean isok = super.onInterceptTouchEvent(ev);

        return isok;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,062评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,658评论 22 664
  • 1、网络沟通6人 2、产品咨询2人 个人体会 大家对微量元素里锌有了解过吗?今天给大家分享一下缺锌给我带来的...
    葆婴USANA廖瑜阅读 57评论 0 0
  • ​— 01 — 我觉得这个世界上对姑娘们最不公平的一句话就是:干得好不如嫁得好!虽然我也很认同婚姻这件事情对一个姑...
    夕言细语阅读 787评论 2 15