- 在项目开发中如何快速处理网络加载中错误页面、空数据页面、网络异常等页面?
- 在项目开发中如何快速实现“上拉刷新”与“加载更多”?
过去做法:
-
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;
}
}
网友评论