读书破万卷下笔如有神

作为入门级的android码农的我,还是需要多多研读开源代码

下面继续接着上一篇的分析,这一篇主要介绍第一个tab,ReviewFragment的分析,界面看起来简单,背后的逻辑处理还是有很多值得学习的地方的。主要有下几点

  • 下拉刷新
  • 不同状态的页面
  • 数据缓存
  • 一些细节

下拉刷新

下拉刷新常见的有俩大类,Android-PullToRefreshandroid-Ultra-Pull-To-Refresh 前者是国外的一大神写的出现的比较早,不过好像已经停止维护了,后者是国内的一个大神写的功能很强大,这个项目使用的是后者(MaterialHeader类型刷新),首先这里又封装了一层BasePutToRefreshFragment 用来进行下拉刷新的操作

下面简单介绍下这个控件的使用,首先是布局文件中fragment_review.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
    >

    <com.vv.androidreview.ui.view.LoadingLayout
        android:id="@+id/ly_loading"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <in.srain.cube.views.ptr.PtrFrameLayout
        android:id="@+id/layout_refresh"
        xmlns:cube_ptr="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        cube_ptr:ptr_duration_to_close="200"
        cube_ptr:ptr_duration_to_close_header="1000"
        cube_ptr:ptr_keep_header_when_refresh="true"
        cube_ptr:ptr_pull_to_fresh="false"
        cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2"
        cube_ptr:ptr_resistance="1.7">

        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:cacheColorHint="#00000000"
            android:divider="@color/list_divider"
            android:dividerHeight="1px"
            android:fadingEdge="none"
            android:fastScrollEnabled="false"
            android:footerDividersEnabled="false"
            android:headerDividersEnabled="false"
            android:scrollbars="none"
            android:smoothScrollbar="true"/>

    </in.srain.cube.views.ptr.PtrFrameLayout>
</RelativeLayout>

在BasePutToRefreshFragment.java中的initDoRefreshView()中开始使用该控件

 private void initDoRefreshView() {
        //ptr控件的使用
        mPtrFrameLayout = (PtrFrameLayout) mRootView.findViewById(R.id.layout_refresh);
        // header
        MaterialHeader header = new MaterialHeader(getContext());
        int[] colors = getResources().getIntArray(R.array.google_colors);
        header.setColorSchemeColors(colors);
        header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2));
        header.setPadding(0, (int) TDevice.dpToPixel(15), 0, (int) TDevice.dpToPixel(10));
        header.setPtrFrameLayout(mPtrFrameLayout);

        mPtrFrameLayout.setLoadingMinTime(1000);
        mPtrFrameLayout.setDurationToCloseHeader(1500);
        mPtrFrameLayout.setHeaderView(header);
        mPtrFrameLayout.addPtrUIHandler(header);
        mPtrFrameLayout.setPinContent(true);

        mPtrFrameLayout.disableWhenHorizontalMove(true);

        mPtrFrameLayout.setPtrHandler(new PtrHandler() {
            @Override
            public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
                return PtrDefaultHandler.checkContentCanBePulledDown(frame, mListView, header);
            }

            @Override
            public void onRefreshBegin(final PtrFrameLayout frame) {
                Logger.e("list is load data by net");
                //先判断是否是自动刷新
                if (frame.isAutoRefresh()) {
                    if (mPtrFrameLayout.isRefreshing()) {
                        mPtrFrameLayout.refreshComplete();
                    }

                    //加载刷新布局
                    mLoadingLayout.setLoadingLayout(LoadingLayout.NETWORK_LOADING);
                }
                sPutUpState = PULL_UP_STATE_NONE;
                // 这里做下拉刷新操作
                requestDataByNet(REFRESH_TYPE_PULL);
            }
        });

    }

详细的使用可以参见如下链接详细使用 不过使用开源项目还是需要尽量搞清楚其中实现的机制。

这里有俩个关键函数

  • mLoadingLayout.setLoadingLayout(LoadingLayout.NETWORK_LOADING);

    加载刷新布局,这在下面会介绍
  • requestDataByNet(REFRESH_TYPE_PULL);

    网络请求数据,这里还指明了类型用来区分是上拉还是下拉,在这个基类中将这个方法声明为抽象方法留在继承类中实现
   public abstract void requestDataByNet(int actionType);
  • listview的高效加载

    通常的方式就是在滑动进行监听 onScrollStateChanged方法中对不同状态判断其中主要有三个状态

    SCROLL_STATE_FLING,SCROLL_STATE_TOUCH_SCROLL,SCROLL_STATE_IDLE,一般是在滑动停止时(SCROLL_STATE_IDLE)才加载listview的数据
       @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                    //在滑动停止时加载
                    case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                        if (isItemfullScreen) {
                            if (sPutUpState == PULL_UP_STATE_NONE && isLoadMore) {
                                Logger.e("list is load more data");
                                pullUpLoadData();
                            }
                        }
                        break;
                }
            }

若要实现listview的加载更多还需要重写onScroll方法,这里通过一个标志位isItemfullScreen来标识

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                //firstVisibleItem -屏幕上显示的第一个item
                //visibleItemCount -屏幕上一共显示item的数目
                //totalItemCount -目前listview 一共有多少个itme
                if (totalItemCount > visibleItemCount) {
                    isItemfullScreen = true;
                } else {
                    isItemfullScreen = false;
                }

            }

其中加载更多需要重新定义一个布局,利用listview.addFooterView(layout)来实现,

不同状态的页面

这个非常常见,比如没有网络时页面显示一个样式,正在刷新显示一个样式,在上面的下来刷新中已经体现在mLoadingLayout.setLoadingLayout(LoadingLayout.NETWORK_LOADING); 可以看出,这种需求的实现是通过定义一个自定义控件protected LoadingLayout mLoadingLayout;,该自定义控件通过add(view)方式将自己加到页面中,其中在容器(viewgroup子类)中增加布局使用add,在widget中使用set这个要注意。这个控件会根据当前传入的状态通过switch来选择自己展现的状态,具体在LoadingLayout.java中,比较简单就不贴代码了。

这里通过单独控件的形式将各类状态分离出来,比较好操作

数据缓存

核心思想是通过序列化将数据存入到file文件中

- 读取缓存,核心类ReadCacheAsyncTask

读取是在ReviewFragment中进行处理的,首先实现了BasePutToRefreshFragment.java中的抽象方法public abstract void readCache();该方法作在fragment创建时候会调用。

  • 存储缓存,核心类SaveCacheAsyncTask

    存储是在下来刷新具体实现时调用,在请求数据requestPointByUnits

    其中使用CacheHelper.java中的readObject和saveObject与之配合使用。

首先这俩个核心类的名称就可以看出这是使用AsynTask来进行处理,为了防止内存泄漏,这里都做了弱引用

先来看存储缓存

public class SaveCacheAsyncTask extends AsyncTask<Void, Void, Void> {
    private WeakReference<Context> mContext;
    private Serializable seri;
    private String cacheKey;

    public SaveCacheAsyncTask(Context context, Serializable seri, String cacheKey) {
        mContext = new WeakReference<Context>(context);
        this.seri = seri;
        this.cacheKey = cacheKey;
    }

    @Override
    protected Void doInBackground(Void... params) {
        CacheHelper.saveObject(mContext.get(), seri, cacheKey);
        return null;
    }
}

其中CacheHelp中的saveObject就是做了一下存储

public static boolean saveObject(Context context, Serializable ser,
                                     String file) {
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            fos = context.openFileOutput(file, Context.MODE_PRIVATE);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(ser);
            oos.flush();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                oos.close();
            } catch (Exception e) {
            }
            try {
                fos.close();
            } catch (Exception e) {
            }
        }
    }

同理保存过程原理一致,关键在于CacheHelp中的判断是否缓存失效函数,根据文件的两次时间戳比对是否失效,在DetailActvity中选择是否沿用缓存

 /**
     * 判断缓存是否已经失效
     */
    public static boolean isCacheDataFailure(Context context, String cachefile) {
        File data = context.getFileStreamPath(cachefile);
        if (!data.exists()) {

            return false;
        }
        long existTime = System.currentTimeMillis() - data.lastModified();
        boolean failure = false;
        if (TDevice.getNetworkType() == TDevice.NETTYPE_WIFI) {
            failure = existTime > Settings.getInt(Settings.CACHE_OVERTIME_WIFI,30) * 60 * 1000 ? true : false;
        } else {
            failure = existTime > Settings.getInt(Settings.CACHE_OVERTIME_OTHER,2) * 24 * 60 * 60 * 1000 ? true : false;
        }
        return failure;
    }

一些细节

这个tab使用时listview中嵌套gridview的方式,这里就需要重写gridview的onMreasure的高度否则就会显示不全,重写也比较简单

public class GridViewEx extends GridView {
    public GridViewEx(Context context) {
        this(context, null);
    }

    public GridViewEx(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GridViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
}

ok,写不动了额

开源项目AndroidReview学习小结(2)的更多相关文章

  1. 开源项目AndroidReview学习小结(1)

    多看多学涨姿势 最近学习了一个开源项目,感觉收获颇多,这里做下简要的记录,首先感谢作者的开源.先看个大概图 感觉框架非常简单,界面也很一般,不过底层的处理的一些处理还是有很多可圈可点之处,代码的处理一 ...

  2. Android 开源项目及其学习

    Android 系统研究:http://blog.csdn.net/luoshengyang/article/details/8923485 Android 腾讯技术人员博客 http://hukai ...

  3. 开源项目live555学习心得

      推荐:伊朗美女找丈夫比找工作难女人婚前一定要看清三件事 × 登录注册   疯狂少男-IT技术的博客 http://blog.sina.com.cn/crazyboyzhaolei [订阅][手机订 ...

  4. 一个toolkit或者一个开源项目如何学习它并使用它

    一个toolkit或者一个开源项目如何学习它并使用它 一般一个流行的toolkit和开源项目,一般都会被广泛地被应用: 那么,我们如何学习它,如何应用它在自己的业务场景中呢? 答案就是:学习源码并借鉴 ...

  5. iOS及Mac开源项目和学习资料【超级全面】

    UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITable ...

  6. iOS开发--iOS及Mac开源项目和学习资料

    文/零距离仰望星空(简书作者)原文链接:http://www.jianshu.com/p/f6cdbc8192ba著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 原文出处:codecl ...

  7. Android开源项目SlidingMenu学习(二)

    前一篇SlidingMenu学习(一)文章中了解了导入SlidingMenu到我们项目经常出现的问题,下面我们正式学习. 先看一个效果: 看到两幅图片的差别了吗,左边的一栏时可以滑动的,可以隐藏掉,现 ...

  8. 分享海量 iOS 及 Mac 开源项目和学习资料

    UI 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITable ...

  9. Github搭建属于自己的开源项目-androd学习之旅(72)

    Github是一个基于的git的版本控制工具,分为私人代码管理和开源代码管理,下面我们简单的介绍一下 首先登陆官网https://github.com 完成注册,随后首页会有四个模块,第一个是安装的指 ...

随机推荐

  1. EBS销售(OE)模块常用表

     select * from ra_customers 客户 select * from ra_addresses_all 地址 select * from ra_site_uses_all 用户 ...

  2. sh里的变量 $0 $1 $$ $#

    $0就是该bash文件名 $?显示最后命令的退出状态.0表示没有错误,其他任何值表明有错误. $*所有位置参数的内容:就是调用调用本bash shell的参数. $@基本上与上面相同.只不过是 &qu ...

  3. 【java多线程系列】java中的volatile的内存语义

    在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...

  4. Linux2.6--虚拟文件系统

          虚拟文件系统(有时也称作虚拟文件交换,更常见的是简称做VFS)作为内核子系统,为用户空间程序提供了文件和文件系统相关的接口.系统中的所有文件系统不但依赖VFS共存,而且也依赖VFS系统协同 ...

  5. 由源代码编译SpriteBuilder最新版本1.5.0搭配最新的Cocos2D 3.4.9

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 大家知道SpriteBuilder版本停留在1.4.9已经很久 ...

  6. 使用Spring+Junit4.4进行测试

    http://nottiansyf.iteye.com/blog/345819 使用Junit4.4测试 在类上的配置Annotation @RunWith(SpringJUnit4ClassRunn ...

  7. Nginx创建密码保护目录

    nginx 的根目录 为:/home/undoner/nginx-wwwnginx 访问地址 为:http://127.0.0.1本文实现对nginx根目录文件访问的权限控制 (1)nginx指定密码 ...

  8. Android优化之ArrayMap

    ArrayMap的介绍 官方对ArrayMap也有说明:它不是一个适应大数据的数据结构,相比传统的HashMap速度要慢,因为查找方法是二分法,并且当你删除或者添加数据时,会对空间重新调整,在使用大量 ...

  9. 如何在Cocos2D游戏中实现A*寻路算法(六)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  10. Java数组的应用2:数组的最大,最小,求和,平均值,倒置

    import java.util.Scanner; public class HelloWorld { public static void main(String[] args){ // Scann ...