小菜鸟起跑-对模仿《谷歌电子市场》项目前两天的总结(二)

本文介绍了一个针对Android应用中Fragment的通用基类封装方法,通过创建LoadingPager类来统一管理加载、错误、空和成功四种视图状态,并实现了数据加载流程的优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为有7个Fragment,所以先对页面进行分析,去抽取一个基类。


基类作用

  1. 对常规方法进行封装;
  2. 对常规属性,常规方法进行封装;

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);

“`
到此为止,差不多就完成框架的搭建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_Boss_Hao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值