在Android Studio上使用GSON+VOLLEY,秒处理网络数据成集合。感受框架的力量。搭配RecyclerView和SwipeRefreshLayout,实现底端加载更多,下拉刷新。

【致谢,引用,声明,前言】

关于GSON和VOLLEY,我百度了很多资料,个人感觉有两篇博客介绍的特别好,附上链接咯:

  GSON: http://blog.csdn.net/lk_blog/article/details/7685169 

VOLLEY:http://blog.csdn.net/guolin_blog/article/details/17482165 .

使用的数据源url是根据徐大神在慕课网的视频里提到的url,不知道能不能在这里发,就不单独发了,反正会在代码出现的。

关于在Android Studio中导入jar包,貌似方法有很多,我导入VOLLEY和GSON是采用复制到lib目录下,然后右键->As a Library,选中自己的Module:

如图 (源自网络)


======================================================

【废话】

我这篇比较基础和入门,主要是融合了GSON VOLLEY搭配V7 V4 包里的RecyclerView和SwipeRefreshLayout,做一个简单的DEMO。实现加载网络数据,并显示。利用两个新控件做一个ListVIew的上下拉刷新的效果。

后续会不断的完善!欢迎各位提意见和BUG。

为什么要用Android Studio,就不用说了吧,好吧 我说一下,因为我最近在换工作,70%的公司都要求使用Android Stuido。不过其实AS用习惯了还是很爽的,我现在都不想在Eclipse上码代码了。

效果图:

======================================================

【构建实体类DataBean.java】

以前我加载网络数据一般会用到HttpUrlConnection,然后根据返回的InputStream,读取并处理成一串String数据(就是返回的json数据)。

然后再将这串String解析成JSONObject,再根据json的格式不断的解析JSONArray...JSONObject...,最终得到我们的目标数据集合List<DataBean> mDatas。

经常一个粗心就哪里写错了,而且代码重复量实在太大。利用GSON和VOLLEY,我只想说一句,秒         处理。

直接看代码:

首先是根据url返回的json,构建一个实体类: 我们这里返回的json格式为:

{
    "status": 1,
    "data": [
        {
            "id": 1,
            "name": "Tony老师聊shell——环境变量配置文件",
            "picSmall": "http://img.mukewang.com/55237dcc0001128c06000338-300-170.jpg",
            "picBig": "http://img.mukewang.com/55237dcc0001128c06000338.jpg",
            "description": "为你带来shell中的环境变量配置文件",
            "learner": 12312
        },
        {
            "id": 2,
            "name": "数学知识在CSS动画中的应用",
            "picSmall": "http://img.mukewang.com/55249cf30001ae8a06000338-300-170.jpg",
            "picBig": "http://img.mukewang.com/55249cf30001ae8a06000338.jpg",
            "description": "数学知识与CSS结合实现酷炫效果",
            "learner": 45625
        },

。。。。。。。。。

我们根据"data"数组里的数据格式,构建DataBean.java: (这个是跟GSON解析相关的)

package com.mcxtzhang.demo.windrecyclerdemo;

/**
 * Created by zhangxutong on 2015/12/28.
 */
public class DataBean {
    private String name;
    private String picSmall;

    public String getName() {
        return name;
    }

    public String getPicSmall() {
        return picSmall;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPicSmall(String picSmall) {
        this.picSmall = picSmall;
    }

  /*  @Override
    public String toString() {
        return "DataBean{" +
                "name='" + name + '\'' +
                ", picSmall='" + picSmall + '\'' +
                '}';
    }*/
}

这里说一个我自己当小白鼠验证出来的东西,经过测试,使用GSON,toString() 不是一定要写的,然后也不一定要写出json数据里的所有字段,只要根据需要就可以了,例如:我这里需要name和picSmall,则我的DataBean只有两个属性。

字段(属性)的命名,我是跟json数据里的字段保持一致的。OK,实体类构建完毕,继续。

======================================================

【使用VOLLEY】

下面就是使用VOLLEY了,请添加网络访问权限!请添加网络访问权限!请添加网络访问权限! 重要的事说三遍,有的同学没添加,不停的报错/运行结果不正确,

<uses-permission android:name="android.permission.INTERNET" />

然后,根据郭神blog所述,VOLLEY使用三部曲:

1. 创建一个RequestQueue对象。

//Volley begin
private RequestQueue mQueue;
private static final String url = "http://www.imooc.com/api/teacher?type=4&num=30";
//Volley end




2. 创建一个JsonRequest对象。 (摘自郭神blog,重点标红:类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。)

本例url返回的是JSONObject,所以用JsonObjectRequest对象。

//Volley begin
//GET 方式的http
/*StringRequest stringRequest = new StringRequest(url,
        new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(TAG, response);
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        Log.e(TAG, error.getMessage(), error);
    }
});

mQueue.add(stringRequest);*/

//json 四个参数分别是:url, JSONObject对象这里为null,一个请求成功的Listener,和一个请求失败的Listener:
//
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>() {
    @Override
    public void onResponse(JSONObject jsonObject) {
       // Log.i(TAG,jsonObject.toString());
        if(null!=jsonObject){
            try {
                //传入的"data" 是根据json返回字符串得来的
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                //上一句代码可能报错,确认不报错再创建下面的对象
                //注释掉的是 原本不使用GSON的解析方法
                /*mDatas = new ArrayList<DataBean>();
                for (int i=0;i<jsonArray.length();i++){
                    JSONObject jsonData = jsonArray.getJSONObject(i);
                    DataBean data = new DataBean();
                    data.setName(jsonData .getString("name"));
                    data.setPicSmall(jsonData.getString("picSmall"));
                    mDatas.add(data);
                }*/

                //使用GSON加载 begin
                String dataString = jsonArray.toString();
                Gson gson = new Gson();
                mDatas = gson.fromJson(dataString, new TypeToken<List<DataBean>>() {}.getType());
                //使用GSON加载 end
                //数据加载完后 再加载适配器
                init();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
        Log.i(TAG,volleyError.getMessage(),volleyError);
    }
});
 



3. 将JsonRequest对象添加到RequestQueue里面。

mQueue.add(jsonObjectRequest);
//Volley end

 

忽略init();里面是适配器的设置和下拉刷新的代码。这里不用care。

======================================================

【GSON】:

上述 步骤 2,里已经包括GSON的使用代码:

这里是将json数据转化为带泛型的List 集合。


private List<DataBean> mDatas;


核心就是三句话:

得到json字符串,

String dataString = jsonArray.toString();

new一个Gson对象,

 Gson gson = new Gson();

利用gson.fromJson(json字符串,new TypeToken<List<DataBean>>() {}.getType()),返回值就是传入到TypeToken里的集合类型

;
new Response.Listener<JSONObject>() {
    @Override
    public void onResponse(JSONObject jsonObject) {
        if(null!=jsonObject){
            try {
                //传入的"data" 是根据json返回字符串得来的
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                //上一句代码可能报错,确认不报错再创建下面的对象
                //使用GSON加载 begin
                String dataString = jsonArray.toString();
                Gson gson = new Gson();
                mDatas = gson.fromJson(dataString, new TypeToken<List<DataBean>>() {}.getType());
                //使用GSON加载 end
                //数据加载完后 再加载适配器
                init();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
}

======================================================

至此,我们已经完成数据源的get!并且存放在mDatas里了。我们可以尽情的折腾这些数据了~未完待续

下面,大家就和我一起愉快的体验一把RecyclerView和SwipeRefreshLayout,感受新控件的魅力,其实我个人感觉RecyclerView最爽的还是瀑布流的实现,很简单,还有就是RecyclerView的增删动画,也是杠杠的,并且只要一行代码。废话不多扯,先说RecyclerView。

======================================================

【导入RecyclerView和SwipeRefreshLayout】:

关于在Android Studio中使用support包 就很简单了,(我在Eclipse里就被虐过很多次)

选中Project Structure:


选择自己的Modules,然后在右边的选项卡里点击Dependences:



点击右边的绿色+号:


好了,在这里愉快的搜索RecyclerView和SwipeRefreshLayout,并且把他们都加入进来就好了。

======================================================

【RecyclerView】:先看一下布局文件:RecyclerView和ListView没区别:而SwipeRefreshLayout是用ViewGroup实现的,所以可以包裹其他的控件,用它包裹住RecyclerView即可,没有啥好说的。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.mcxtzhang.demo.windrecyclerdemo.MainActivity">


    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipelayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <!--        <com.mcxtzhang.demo.windrecyclerdemo.RefreshView
                    android:id="@+id/recyclerview"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />-->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v4.widget.SwipeRefreshLayout>


</RelativeLayout>
private RecyclerView mRecyclerView;
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerview);
//如果item的内容不改变view布局大小,那使用这个设置可以提高RecyclerView的效率
mRecyclerView.setHasFixedSize(true);

mLayoutManager = new LinearLayoutManager(MainActivity.this,LinearLayoutManager.VERTICAL,false);
mRecyclerView.setLayoutManager(mLayoutManager);

这里要说的就是LayoutManager了,RecyclerView之所以能实现这么多的效果(ListVIew GridView 瀑布流),主要是由于它解耦的很好,只要改变LayoutManager或者Adapter里的一些代码就可以实现很多改变。

我们这里用的是最简单的LinearLayoutManager,传入的三个参数分别是 context,布局的方向,和是否将数据反向。      

若使用LinearLayoutManager.HORIZONTAL ,则是水平方向的ListView,
若第三个参数为true,则会反向显示整个mDatas里的数据。

大家一试便知效果。

下面再给RecyclerView设置一个适配器Adapter ,便可以让它显示数据了。

======================================================

【Adapter】:

代码里有详细注释:

/**
 * RecyclerView 的Adapter强制我们使用ViewHolder模式,对于我这种熟读徐老师《文艺Adapter》的青年来说,也是很轻松就接受了,看来Google也是强制我们每个程序员都成为一个文艺的青年。
 * 这里回想一下以前ListView的BaseAdapter,在getView()里还要判断convertView是否为空来判断是否是复用的,通过setTag,getTag放入ViewHolder对象。
 * 现在不用了,RecyclerView.Adapter<DataViewHolder>,将getView方法分解成两个过程,
 * 一个是public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType),它专注于ViewHolder的创建。
 * 另一个是 public void onBindViewHolder(final DataViewHolder holder, int position),它专注于ViewHolder的绑定,数据的显示,通过LOG查看,该方法时在每次显示item都被调用的(废话,但是我比较喜欢验证:))
 */

/**
 * ViewHolder的创建很简单,
 * 一:打开item的布局layout,里面有啥控件,就在这里写啥。
 * (所以如果用到Volley的NetworkImageView,那这里就要用它替换ImageView,稍后会用到。)
 * 二:在构造方法里,完成Item View 和ViewHolder里属性的绑定,
 * 其中findViewById里的id值 就是item的布局layout里的值(好像太啰嗦了)
 *
 */

package com.mcxtzhang.demo.windrecyclerdemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;

import java.util.List;


/**
 * RecyclerView 的Adapter强制我们使用ViewHolder模式,对于我这种熟读徐老师《文艺Adapter》的青年来说,也是很轻松就接受了,看来Google也是强制我们每个程序员都成为一个文艺的青年。
 * 这里回想一下以前ListView的BaseAdapter,在getView()里还要判断convertView是否为空来判断是否是复用的,通过setTag,getTag放入ViewHolder对象。
 * 现在不用了,RecyclerView.Adapter<DataViewHolder>,将getView方法分解成两个过程,
 * 一个是public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType),它专注于ViewHolder的创建。
 * 另一个是 public void onBindViewHolder(final DataViewHolder holder, int position),它专注于ViewHolder的绑定,数据的显示,通过LOG查看,该方法时在每次显示item都被调用的(废话,但是我比较喜欢验证:))
 */
public class DataAdapter extends RecyclerView.Adapter<DataViewHolder> {
    private List<DataBean> mDatas;
    private LayoutInflater mInflater;
    //利用Volley 加载网络图片用到
    private RequestQueue mQueue;

    //加载网络图片2 利用ImageLoader    begin  //有软缓存 没有硬缓存
    ImageLoader mImageLoader;
    //加载网络图片2 利用ImageLoader    end  //有软缓存 没有硬缓存

    //加载网络图片使用NetworkImageView  //有软缓存 没有硬缓存  begin
    private ImageLoader.ImageCache mImageCache;
    //加载网络图片使用NetworkImageView  //有软缓存 没有硬缓存  end


    public DataAdapter(Context context, List<DataBean> mDatas, RequestQueue mQueue) {
        this.mDatas = mDatas;
        this.mInflater = LayoutInflater.from(context);
        this.mQueue = mQueue;
        //加载网络图片使用NetworkImageView   begin
        mImageCache = new WindImageCache();
        //加载网络图片使用NetworkImageView   end

        //加载网络图片2 利用ImageLoader    begin  //有软缓存 没有硬缓存
        //构建ImageLoader,传入RequestQueue和ImageCache对象
        mImageLoader = new ImageLoader(mQueue, mImageCache);
        //加载网络图片2 利用ImageLoader    end  //有软缓存 没有硬缓存
    }

    /**
     * @param parent   其实就是RecyclerView
     * @param viewType 据我看到,模仿ListView底部加载更多会用到。
     * @return 创建的新的ViewHolder对象
     */
@Override
    public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new DataViewHolder(mInflater.inflate(R.layout.item, parent, false));
    }

    /**
     * @param holder   复用的ViewHolder对象
     * @param position 当前的位置(有时候为了动画效果,只调用notifyItemInserted(),notifyItemRemoved();
     *                 在增删数据没有调用notifyDataSetChanged();方法的时候,是不准的,不如ViewHolder.getLayoutPosition())
     *                 这里没有增删数据源,所以可以放心使用
     */
@Override
    public void onBindViewHolder(final DataViewHolder holder, int position) {
        holder.textView.setText(mDatas.get(position).getName());
        //加载网络图片 利用ImageRequest    begin  //有双缓存
        //第一个参数就是图片的URL地址,第二个参数是图片请求成功的回调,
        // 第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。
        // 第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。
        // 第六个参数是图片请求失败的回调,
        //摘自郭神博客
        /*ImageRequest imageRequest = new ImageRequest(mDatas.get(position).picSmall, new Response.Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap bitmap) {
                holder.imageView.setImageBitmap(bitmap);
            }
        }, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                //请求失败 设置默认图片
                holder.imageView.setImageResource(R.mipmap.ic_launcher);
            }
        });
        mQueue.add(imageRequest);*/
        //加载网络图片 利用ImageRequest    end  //有双缓存

        //加载网络图片2 利用ImageLoader   begin
        //内部也是使用ImageRequest实现的,它可以过滤重复的链接,更高效。
        //ImageListener :通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数
        // 第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示的图片,第三个参数指定加载图片失败的情况下显示的图片。
/*        ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(holder.imageView, R.mipmap.ic_loading, R.mipmap.ic_loaderror);
        //调用ImageLoader的get()方法来加载图片
        //两个参数,第一个参数就是图片的URL地址,第二个参数则是ImageListener对象,第三、四个参数是指定图片允许的最大宽度和高度。
        mImageLoader.get(mDatas.get(position).getPicSmall(), imageListener,0,0);*/
        //加载网络图片2 利用ImageLoader   end

        //加载网络图片使用NetworkImageView   begin
        //可以调用它的setDefaultImageResId()方法、setErrorImageResId()方法和setImageUrl()方法来分别设置加载中显示的图片,加载失败时显示的图片,以及目标图片的URL地址
        //setImageUrl()方法接收两个参数,第一个参数用于指定图片的URL地址,第二个参数则是前面创建好的ImageLoader对象。
        //ImageLoader imageLoader = new ImageLoader(mQueue,mImageCache);
/*        NetworkImageView networkImageView = holder.imageView;
        networkImageView.setDefaultImageResId(R.mipmap.ic_loading);
        networkImageView.setErrorImageResId(R.mipmap.ic_loaderror);
        networkImageView.setImageUrl(mDatas.get(position).getPicSmall(),mImageLoader);*/
        //加载网络图片使用NetworkImageView   end
    }

    /**
     * @return item的个数,最好加个为空判断。
     */
    @Override
    public int getItemCount() {
        return mDatas != null ? mDatas.size() : 0;
    }
}

/**
 * ViewHolder的创建很简单,
 * 一:打开item的布局layout,里面有啥控件,就在这里写啥。
 * (所以如果用到Volley的NetworkImageView,那这里就要用它替换ImageView,稍后会用到。)
 * 二:在构造方法里,完成Item View 和ViewHolder里属性的绑定,
 * 其中findViewById里的id值 就是item的布局layout里的值(好像太啰嗦了)
 */
class DataViewHolder extends RecyclerView.ViewHolder {
    TextView textView;
    ImageView imageView;
    //加载网络图片使用NetworkImageView   begin
    //NetworkImageView imageView;
    //加载网络图片使用NetworkImageView   end

    public DataViewHolder(View itemView) {
        super(itemView);
        //完成Item View 和ViewHolder里属性的绑定,begin
        textView = (TextView) itemView.findViewById(R.id.item_name);
        imageView = (ImageView) itemView.findViewById(R.id.item_image);
        //加载网络图片使用NetworkImageView   begin
        //imageView = (NetworkImageView) itemView.findViewById(R.id.item_image);
        //加载网络图片使用NetworkImageView   end
        //完成Item View 和ViewHolder里属性的绑定,end
    }
}

======================================================

【item的布局文件】:item.xml: 

如果使用Volley里的NetworkImageView,则替换NetworkImageView,注释ImageVIew。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:orientation="horizontal"
    android:background="#44ff0000"
    android:layout_margin="5dp">

    <TextView
        android:id="@+id/item_name"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:textSize="16sp"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <ImageView
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:id="@+id/item_image"
        android:layout_gravity="right"
        android:src="@mipmap/ic_launcher"/>
   <!-- //加载网络图片使用NetworkImageView   begin-->
<!--    <com.android.volley.toolbox.NetworkImageView
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:id="@+id/item_image"
        android:layout_gravity="right"
        android:src="@mipmap/ic_launcher"/>-->
    <!-- //加载网络图片使用NetworkImageView   end-->
</LinearLayout>

 

======================================================

【绑定Adapter和RecyclerVIew】:和ListVIew差不多。

mAdapter = new DataAdapter(MainActivity.this,mDatas,mQueue);
mRecyclerView.setAdapter(mAdapter);

======================================================

【WindImageCache】:

一个简单的LruCache,这里可以扩展,后续我会写成双缓存。

package com.mcxtzhang.demo.windrecyclerdemo;

import android.graphics.Bitmap;
import android.util.LruCache;

import com.android.volley.toolbox.ImageLoader;

/**
 * Created by zhangxutong on 2015/12/28.
 */
public class WindImageCache implements ImageLoader.ImageCache {
    private LruCache<String,Bitmap> mCache;

    public WindImageCache() {
        mCache = new LruCache<String,Bitmap>((int) (Runtime.getRuntime().maxMemory()/10)){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }

    @Override
    public Bitmap getBitmap(String s) {
        return mCache.get(s);
    }

    @Override
    public void putBitmap(String s, Bitmap bitmap) {
        if(null == getBitmap(s)){
            mCache.put(s,bitmap);
        }
    }
}

 

======================================================

【关于Volley加载网络图片】:

在Adapter的小节中,已经用代码写了三种使用Volley加载图片的方式:ImageRequest,ImageLoader,NetworkImageView,据说是后两种比较好,性能高还可以过滤重复url请求。但是第一种ImageRequest已经替我们实现了双缓存,后两种缓存机制要自己实现,我们这里使用的是上一节自定义的LruCache。


【ImageRequest】:

//加载网络图片 利用ImageRequest    begin  //有双缓存
//第一个参数就是图片的URL地址,第二个参数是图片请求成功的回调,
// 第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。
// 第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。
// 第六个参数是图片请求失败的回调,
//摘自郭神博客
ImageRequest imageRequest = new ImageRequest(mDatas.get(position).picSmall, new Response.Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap bitmap) {
        holder.imageView.setImageBitmap(bitmap);
    }
}, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
        //请求失败 设置默认图片
        holder.imageView.setImageResource(R.mipmap.ic_launcher);
    }
});
mQueue.add(imageRequest);
//加载网络图片 利用ImageRequest    end  //有双缓存

 

【ImageLoader】:

内部也是使用ImageRequest实现的,它可以过滤重复的链接,更高效。

1. 创建一个RequestQueue对象。
2. 创建一个ImageLoader对象。
3. 获取一个ImageListener对象。
4. 调用ImageLoader的get()方法加载网络上的图片。

//加载网络图片 利用ImageLoader    begin  //有软缓存 没有硬缓存
ImageLoader mImageLoader;
//加载网络图片 利用ImageLoader    end  //有软缓存 没有硬缓存

//加载网络图片2 利用ImageLoader    begin  //有软缓存 没有硬缓存
//构建ImageLoader,传入RequestQueue和ImageCache对象
mImageLoader = new ImageLoader(mQueue, mImageCache);
//加载网络图片2 利用ImageLoader    end  //有软缓存 没有硬缓存
//加载网络图片2 利用ImageLoader   begin
//内部也是使用ImageRequest实现的,它可以过滤重复的链接,更高效。
//ImageListener :通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数
// 第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示的图片,第三个参数指定加载图片失败的情况下显示的图片。
ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(holder.imageView, R.mipmap.ic_loading, R.mipmap.ic_loaderror);
//调用ImageLoader的get()方法来加载图片
//两个参数,第一个参数就是图片的URL地址,第二个参数则是ImageListener对象,第三、四个参数是指定图片允许的最大宽度和高度。
mImageLoader.get(mDatas.get(position).getPicSmall(), imageListener,0,0);
//加载网络图片2 利用ImageLoader   end

 

【NetworkImageView】:

1. 创建一个RequestQueue对象。(同上)
2. 创建一个ImageLoader对象。(同上)
3. 在布局文件中添加一个NetworkImageView控件。(见item.xml布局文件)

<com.android.volley.toolbox.NetworkImageView
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:id="@+id/item_image"
        android:layout_gravity="right"
        android:src="@mipmap/ic_launcher"/>


4. 在代码中获取该控件的实例。(ViewHolder里)

//加载网络图片使用NetworkImageView   begin
//NetworkImageView imageView;
//加载网络图片使用NetworkImageView   end
//加载网络图片使用NetworkImageView   begin
//imageView = (NetworkImageView) itemView.findViewById(R.id.item_image);
//加载网络图片使用NetworkImageView   end

 

5. 设置要加载的图片地址。(Adapter 的onBindViewHolder里)


//加载网络图片使用NetworkImageView   begin
//可以调用它的setDefaultImageResId()方法、setErrorImageResId()方法和setImageUrl()方法来分别设置加载中显示的图片,加载失败时显示的图片,以及目标图片的URL地址
//setImageUrl()方法接收两个参数,第一个参数用于指定图片的URL地址,第二个参数则是前面创建好的ImageLoader对象。
//ImageLoader imageLoader = new ImageLoader(mQueue,mImageCache);
NetworkImageView networkImageView = holder.imageView;
networkImageView.setDefaultImageResId(R.mipmap.ic_loading);
networkImageView.setErrorImageResId(R.mipmap.ic_loaderror);
networkImageView.setImageUrl(mDatas.get(position).getPicSmall(),mImageLoader);
//加载网络图片使用NetworkImageView   end

 

======================================================

【三种加载图片方式的区别】:

ImageRequest:双缓存,但是不会过滤重复连接。

ImageLoader:缓冲策略自定义,会过滤重复链接。

NetworkImageView:缓存策略自定义,由于是控件,所以会根据自身控件大小,自动对加载的图片进行压缩,不会浪费内存(加载到内存中的图片和显示的是一样的大小),不用手动控制这个压缩图片的过程。这是最好的一点。如果不想压缩图片,则将layout_width和layout_height都设置成wrap_content即可。

======================================================

【SwipeRefreshLayout】:

我掌握的也不多,很简单,包裹在RecyclerView外面后,向下拉动就会有进度条滚动的效果了。如图:


控件的初始化和其他一样,

private SwipeRefreshLayout mSwipeRefreshLayout;
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipelayout);

可以设置进度条的颜色,最多可以设置四个颜色:

//设置 进度条的颜色变化,最多可以设置4种颜色
mSwipeRefreshLayout.setColorSchemeColors(Color.parseColor("#ff00ff"),Color.parseColor("#ff0f0f"),Color.parseColor("#0000ff"),Color.parseColor("#000000"));

设置进度条距离顶部的间距:

//setProgressViewOffset(boolean scale, int start, int end) 调整进度条距离屏幕顶部的距离
mSwipeRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue
        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources()
                .getDisplayMetrics()));
//设置监听器,这里就简单的每当刷新(圆形进度条出现)时,延迟5秒将刷新状态改为false,即刷新结束。进度条也会消失
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        Log.i(TAG," onRefresh() now:"+mSwipeRefreshLayout.isRefreshing());
        mSwipeRefreshLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                mSwipeRefreshLayout.setRefreshing(false);
            }
        },5000);
    }
});

以上已经实现的 该控件自带的,下拉刷新功能。

【实现下滑到底端加载更多】:

这里也是通过监听OnScrollListener实现,在onScrooled()方法里判断,如果向下滑并且最后一个item可见,则执行加载更多操作。

//数据集的长度
private int mTotalCount;
//当前可见的最后一个item的id(最大值为数据集长度-1)
private int mLastVisiblePos;

 

/**
 *  下滑到底部 加载更多,也是通过重写OnScrollListener实现
 *  在onScrolled()方法里判断,如果已经可见的最后一个item,并且是向下滑(dy>0),且当前没有在刷新,则执行刷新操作。
 */
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }

    /**
     * @param recyclerView  
* @param dx  下滑为负 上滑为正
     * @param dy
*/
@Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        //数据集的长度
        mTotalCount = mAdapter.getItemCount();
        // (类似id,所以最大值也是数据集长度-1)
        mLastVisiblePos = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        Log.i(TAG,"getItemCount();"+ mTotalCount+ ".findLastVisibleItemPosition();"+ mLastVisiblePos +", dy :"+dy );

        //下拉到底端: 刷新   dy :纵坐标的位移 (上一个点 - 现在的点, 所以下滑为负,上滑为正)
        if(mLastVisiblePos + 1 ==mTotalCount  && !mSwipeRefreshLayout.isRefreshing() && dy>0){
            //手动设置当前刷新状态为true
            mSwipeRefreshLayout.setRefreshing(true);
            //模拟取消:
            //实际中这里要替换为加载更多数据,稍后完善
            mSwipeRefreshLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            },5000);

        }
    }
});

 

========================================================================================================================

处女座写到这里就算完结了,算是RecyclerView+SwipreRefreshLayout(一)吧。

后续会更新!计划加入

Toolbar

点击呈现大图,大图可缩放 移动。

把刷新和加载更多的数据功能完善(目前只有UI和模拟操作)。

实现ImageLoader和NetworkImageView 的二级缓存。

瀑布流

原生ListView。

【源代码】:

http://download.csdn.net/detail/zxt0601/9382618


posted @ 2016-11-05 20:17  天涯海角路  阅读(356)  评论(0编辑  收藏  举报