因为有7个Fragment,所以先对页面进行分析,去抽取一个基类。
基类作用
- 对常规方法进行封装;
- 对常规属性,常规方法进行封装;
BaseFragment抽取前分析
一、视图显示
Fragment共性–>页面共性–>视图的展示
任何应用其实只有4种类型
① 加载页面
② 错误页面
③ 空页面
④ 成功页面
①②③三种页面一个应用基本是固定的
每一个fragment对应的页面④就不一样
进入应用的时候显示①
②③④需要加载数据之后才知道显示哪个
二、数据加载
① 触发加载 进入页面开始加载/点击某一个按钮的时候加载
② 异步加载数据 –>显示加载视图
③ 处理加载结果
① 成功–>显示成功视图
② 失败
① 数据为空–>显示空视图
② 数据加载失败–>显示加载失败的视图
分析完之后,开始写BaseFragment,首先New出一个LoadingPager类
Loadingpager
一个专门的类,也是一个核心的类,负责视图显示,数据加载
此类中,先写一个带一个参数的构造方法,构造方法中创建一个初始化常规视图的方法
public LoadingPager(Context context) {
super(context);
initCommonView();
}
/**
* @des 初始化常规视图
* @call LoadingPager初始化视图的时候调用
*/
private void initCommonView() {
// ① 加载页面
mLoadingView = View.inflate(UIUtils.getContext(),
R.layout.pager_loading, null);
this.addView(mLoadingView);
// ② 错误页面
mErrorView = View.inflate(UIUtils.getContext(), R.layout.pager_error,
null);
this.addView(mErrorView);
// ③ 空页面
mEmptyView = View.inflate(UIUtils.getContext(), R.layout.pager_empty,
null);
this.addView(mEmptyView);
refreshUI();
}
将常规视图加入到这个LoadingPager之后,之后在创建一个更新UI的方法。这个方法要求根据不同的状态显示不同的View
/**
* @des 根据不同的状态显示不同的View
* @call 1.LoadingPager初始化视图的时候调用
* @call 2.真正加载数据数据执行完成的时候
*/
private void refreshUI() {
// 控制loading视图显式与隐藏
mLoadingView.setVisibility(mCurState == STATE_LOADING ? View.VISIBLE
: View.GONE);
// 控制error视图显式与隐藏
mErrorView.setVisibility(mCurState == STATE_ERROR ? View.VISIBLE
: View.GONE);
// 控制empty视图显式与隐藏
mEmptyView.setVisibility(mCurState == STATE_EMPTY ? View.VISIBLE
: View.GONE);
if (mSuccessView == null && mCurState ==STATE_SUCCESS) {
//创建成功视图
mSuccessView = initSuccessView();
this.addView(mSuccessView);
}
if (mSuccessView!=null) {
//控制success视图的显式与隐藏
mSuccessView.setVisibility((mCurState == STATE_SUCCESS) ? 0 : 8);
}
}
之后,开始对数据加载流程进行逻辑的书写
/**
* @des 触发加载数据
* @call 暴露给外界调用,就是外界触发加载数据
*/
public void loadData(){
new Thread(new LoadDataTask()).start();
}
class LoadDataTask implements Runnable{
@Override
public void run() {
// //② 异步加载数据(此时这个基类不知道具体怎么加载数据,所以要用抽象方法)
LoadedResult tempState = initData();
//处理加载结果(只有得到结果才能去刷新UI)
mCurState = tempState.getState();
//刷新UI
UIUtils.postTaskSafely(new Runnable() {
@Override
public void run() {
refreshUI();
}
});
}
}
加载数据要返回一个状态值,值得注意的是,加载数据的方法需要用抽象方法,因为在基类中,不知道具体怎么加载数据,而要交给子类去实现。
创建完抽象方法initData();需要在LoadingPager被调用的构造方法创建的时候进行修改,因为抽象类不能被new出来,所以在new的时候,在参数后加上一个括号,实现其未实现的抽象方法。而在BaseFragment中,具体初始化数据也不知道,所以需要写一个同名的抽象方法。
这里是BaseFragment的部分代码
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
LoadingPager loadingPager = new LoadingPager(UIUtils.getContext()) {
@Override
public LoadedResult initData() {
return BaseFragment.this.initData();
}
@Override
public View initSuccessView() {
return BaseFragment.this.initSuccessView();
}
};
loadingPager.loadData();//模拟触发加载
return loadingPager;
}
/**
* @des 必须实现但是BaseFragment不知道怎么实现,定义成抽象方法,让子类具体实现。
* @des 它是LoadingPager的同名方法
* @call LoadData()方法调用的时候被调用
*/
public abstract LoadedResult initData();
之后再具体实现的HomeFragment界面,对initData模拟执行一些耗时操作
@Override
public LoadedResult initData() {
// 真正的加载数据,执行耗时操作
SystemClock.sleep(2000);
// 随机返回3种状态的一种
LoadedResult[] arr = { LoadedResult.SUCCESS, LoadedResult.EMPTY,
LoadedResult.ERROR };
Random random = new Random();
int index = random.nextInt(arr.length);
return arr[index];
}
此耗时操作执行完,会再次刷新UI,第二次调用refreshUI方法的时候,创建成功视图,但是成功视图也是需要在子类中去实现,所以定义成抽象方法,也需要在BaseFragemen里去创建一个同名的抽象方法。
在具体实现的子类中
@Override
public View initSuccessView() {
// 返回成功的视图
TextView tv = new TextView(UIUtils.getContext());
tv.setText(this.getClass().getSimpleName());
return tv;
}```
另外在具体实现类HomeFragment里的初始化方法里用到对返回状态进行一些包装,用枚举将三种状态封装。
封装的方法是写在LoadingPager里
public enum LoadedResult{
SUCCESS(STATE_SUCCESS), ERROR(STATE_ERROR), EMPTY(STATE_EMPTY);
int state;
public int getState() {
return state;
}
private LoadedResult(int state){
this.state = state;
}
}
___
总结拖得有点久,这么下去不行,我要效率起来
___
####数据触发加载的时机
上边的逻辑书写好之后,需要优化一些地方,首先是加载数据的触发,将触发的方法写在MainActivity里,给PagerSlidingStrip增加一个页面滚动的监听,当滑动到对应页面,就去加载对应页面逻辑里加载数据的方法。
private void initListener() {
mTabs.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// 完成触发加载
BaseFragment fragment = FragmentFactory.getFragment(position);
if (fragment !=null) {
System.out.println(fragment);
System.out.println(fragment.getLoadingPager());
fragment.getLoadingPager().loadData();
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
___
####之后会发现,刚进应用的时候,主页不加载,即LoadingPager首页未加载,然后需要修改PagerSlidingTabStrip的源码,
if (delegatePageListener != null) {
delegatePageListener.onPageSelected(0);
}
加上这么一句,就相当于在首页也默认加载
####第二个需优化的地方,LoadPager重复创建就是不断的滑动界面,会不断地在BaseFragment里边加载 创建 页面的方法,即new很多次
mLoadingPager = new LoadingPager(UIUtils.getContext())
,所以需要加一个判断
if (mLoadingPager ==null) {
mLoadingPager = new LoadingPager(UIUtils.getContext()) {
@Override
public LoadedResult initData() {
return BaseFragment.this.initData();
}
@Override
public View initSuccessView() {
return BaseFragment.this.initSuccessView();
}
};
}else{//第二次执行
((ViewGroup)mLoadingPager.getParent()).removeView(mLoadingPager);
}
return mLoadingPager;
####保证每次执行的时候一定是加载中试图,而不是上一次的mCurState
只需要在LoadingPager中,在加载数据的方法里先把加的状态赋值给当前状态。
public void loadData(){
//保证每次执行的时候一定是加载中试图,而不是上一次的mCurState
int state = STATE_LOADING;
mCurState = state;
refreshUI();
new Thread(new LoadDataTask()).start();
}
___
####如果加载成功,无需再次加载,如果加载中无需再次加载
只需要在loadData()方法中,加一个判断,并且将默认的状态改一下
___
public int mCurState = STATE_NONE;
public void loadData(){
//如果加载成功,无需再次加载,如果加载中无需再次加载
if (mCurState!= STATE_SUCCESS&&mCurState!=STATE_LOADING) {
LogUtils.sf(“*开始加载数据”);
//保证每次执行的时候一定是加载中试图,而不是上一次的mCurState
int state = STATE_LOADING;
mCurState = state;
refreshUI();
new Thread(new LoadDataTask()).start();
}
}
___
####最后一个需要优化的地方,错误页面点击重新加载
只需要在加载错误页面的View中,找到按钮,然后设置点击事件
___
// ② 错误页面
mErrorView = View.inflate(UIUtils.getContext(), R.layout.pager_error,
null);
mErrorView.findViewById(R.id.error_btn_retry).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//重新加载数据
loadData();
}
});
this.addView(mErrorView);
“`
到此为止,差不多就完成框架的搭建