Android代码优化----PullToRefresh+universal-image-loader实现从网络获取数据并刷新

 

 【声明】 

欢迎转载,但请保留文章原始出处→_→ 

生命壹号:http://www.cnblogs.com/smyhvae/

文章来源:http://www.cnblogs.com/smyhvae/p/4488049.html

联系方式:smyhvae@163.com 

 效果图:(gif图太大了,有点卡,建议将图片保存到本地查看或者直接本文末尾的源码查看gif图)

加载网络图片我们用universal-image-loader,然后实现ListView的上拉下拉刷新我们用PullToRefresh。下面开始写代码。

整个代码的工程文件结构如下:

  • libs文件夹下是需要用到的一些库和开源框架(这里有个picasso框架,是用来加载网络图片的,暂时不用哈,留着以后备用,现在这个项目用的是universal-image-loader)。
  • adapter文件夹下是listView的自定义适配器(本文的重点)
  • entities是从网络获取到json数据,解析之后,用来存放这些数据的实体
  • utils文件夹下是url的常量
  • MainActivity.java是主程序的入口
  • MyApplication才是真正的app的第一个入口,这个不多解释,都懂得。

一、开始写代码:

(1)activity_main.xml(MainActvity的布局)

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <com.handmark.pulltorefresh.library.PullToRefreshListView
 7         android:id="@+id/lv"
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent">
10 
11 
12     </com.handmark.pulltorefresh.library.PullToRefreshListView>
13 </RelativeLayout>

 

这里就放了个PullToRefreshListView,其实本质上还是个ListView

(2)item_listview.xml(ListView中单个item的布局)

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2               xmlns:tools="http://schemas.android.com/tools"
 3               android:layout_width="match_parent"
 4               android:layout_height="match_parent"
 5               android:orientation="vertical"
 6     >
 7 
 8     <LinearLayout
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginBottom="15dp"
12         android:gravity="center_vertical"
13         android:orientation="horizontal"
14         android:paddingTop="28dp">
15 
16         <!--作者头像-->
17         <ImageButton
18             android:id="@+id/mVideoAvatarBtn"
19             android:layout_width="56dp"
20             android:layout_height="56dp"
21             android:background="@mipmap/defaultimg"/>
22 
23         <!--昵称-->
24         <TextView
25             android:id="@+id/mVideoNicknameTv"
26             android:layout_width="match_parent"
27             android:layout_height="wrap_content"
28             android:layout_alignParentTop="true"
29             android:layout_marginLeft="13dp"
30             android:layout_toRightOf="@+id/mVideoAvatarIv"
31             android:singleLine="true"
32             android:text="张三"
33             android:textSize="16sp"/>
34 
35     </LinearLayout>
36 
37 
38     <!--视频缩略图-->
39     <ImageButton
40         android:id="@+id/mVideoImgBtn"
41         android:layout_width="wrap_content"
42         android:layout_height="wrap_content"
43 
44         />
45 
46 
47 </LinearLayout>

 

单个item中,一个是头像,一个是文本,一个是图片(包裹内容),其布局效果如下:

(3)MyApplication.java:

 1 package com.smyhvae.pulltorefreshdemo;
 2 
 3 import android.app.Application;
 4 
 5 import com.nostra13.universalimageloader.core.ImageLoader;
 6 import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
 7 
 8 /**
 9  * Created by smyhvae on 2015/5/8.
10  */
11 public class MyApplication extends Application {
12 
13     @Override
14     public void onCreate() {
15         super.onCreate();
16 
17 
18         //创建默认的ImageLoader配置参数
19         ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
20                 .writeDebugLogs() //打印log信息
21                 .build();
22 
23 
24         //Initialize ImageLoader with configuration.
25         ImageLoader.getInstance().init(configuration);
26     }
27 
28 }

 

主程序一进来,我们就在onCreate()中创建ImageLoader的配置参数,并初始化到ImageLoader中。

 

(4)JavaBean的实体:

这个其实就是下面的这些实体:

这个不需要赘述,代码略,在本文最后有源码下载链接。

(5)ViewHolder.java:(ListView的万能模板,嘿嘿)

 1 package com.smyhvae.pulltorefreshdemo.adapter;
 2 
 3 import android.content.Context;
 4 import android.util.SparseArray;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 
 9 /**
10  * Created by smyhvae on 2015/5/4.
11  * 通用的viewHolder类
12  */
13 public class ViewHolder {
14 
15     private SparseArray<View> mViews;
16     private int mPosition;
17     private View mConvertView;
18 
19     public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
20         this.mPosition = position;
21         this.mViews = new SparseArray<View>();
22 
23         mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
24 
25         mConvertView.setTag(this);
26 
27     }
28 
29     public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
30         if (convertView == null) {
31             return new ViewHolder(context, parent, layoutId, position);
32         } else {
33             ViewHolder holder = (ViewHolder) convertView.getTag();
34             holder.mPosition = position; //即时ViewHolder是复用的,但是position记得更新一下
35             return holder;
36         }
37     }
38 
39     /*
40     通过viewId获取控件
41      */
42     //使用的是泛型T,返回的是View的子类
43     public <T extends View> T getView(int viewId) {
44         View view = mViews.get(viewId);
45 
46         if (view == null) {
47             view = mConvertView.findViewById(viewId);
48             mViews.put(viewId, view);
49         }
50 
51         return (T) view;
52     }
53 
54     public View getConvertView() {
55         return mConvertView;
56     }
57 
58 }

 

(6)ListViewAdapter.java:(同样是ListView的万能模板)

 1 package com.smyhvae.pulltorefreshdemo.adapter;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 
 9 import java.util.List;
10 
11 /**
12  * Created by smyhvae on 2015/5/4.
13  * 通用的ListView的BaseAdapter,所有的ListView的自定义adapter都可以继承这个类哦
14  */
15 public abstract class ListViewAdapter<T> extends BaseAdapter {
16 
17     //为了让子类访问,于是将属性设置为protected
18     protected Context mContext;
19     protected List<T> mDatas;
20     protected LayoutInflater mInflater;
21     private int layoutId; //不同的ListView的item布局肯能不同,所以要把布局单独提取出来
22 
23     public ListViewAdapter(Context context, List<T> datas, int layoutId) {
24         this.mContext = context;
25         mInflater = LayoutInflater.from(context);
26         this.mDatas = datas;
27         this.layoutId = layoutId;
28     }
29 
30     @Override
31     public int getCount() {
32         return mDatas.size();
33     }
34 
35     @Override
36     public T getItem(int position) {
37         return mDatas.get(position);
38     }
39 
40     @Override
41     public long getItemId(int position) {
42         return position;
43     }
44 
45     @Override
46     public View getView(int position, View convertView, ViewGroup parent) {
47         //初始化ViewHolder,使用通用的ViewHolder,一样代码就搞定ViewHolder的初始化咯
48         ViewHolder holder = ViewHolder.get(mContext, convertView, parent, layoutId, position);//layoutId就是单个item的布局
49 
50         convert(holder, getItem(position));
51         return holder.getConvertView(); //这一行的代码要注意了
52     }
53 
54     //将convert方法公布出去
55     public abstract void convert(ViewHolder holder, T t);
56 
57 }

 

(7)【非常非常重要】VideoListViewAdapter.java:

这个才是我们的这个ListView的自定义适配器哦:

  1 package com.smyhvae.pulltorefreshdemo.adapter;
  2 
  3 
  4 import android.content.Context;
  5 import android.view.ViewGroup;
  6 import android.widget.ImageButton;
  7 import android.widget.LinearLayout;
  8 import android.widget.TextView;
  9 
 10 import com.nostra13.universalimageloader.core.DisplayImageOptions;
 11 import com.nostra13.universalimageloader.core.ImageLoader;
 12 import com.nostra13.universalimageloader.core.assist.ImageScaleType;
 13 import com.smyhvae.pulltorefreshdemo.R;
 14 import com.smyhvae.pulltorefreshdemo.entities.Video;
 15 import com.smyhvae.pulltorefreshdemo.utils.Constants;
 16 
 17 import java.util.List;
 18 
 19 /**
 20  * Created by smyhvae on 2015/5/5.
 21  */
 22 
 23 
 24 public class VideoListViewAdapter extends ListViewAdapter<Video> {
 25 
 26 
 27     DisplayImageOptions options;        // DisplayImageOptions是用于设置图片显示的类
 28 
 29 
 30     //MyAdapter需要一个Context,通过Context获得Layout.inflater,然后通过inflater加载item的布局
 31     public VideoListViewAdapter(Context context, List<Video> datas) {
 32         super(context, datas, R.layout.item_listview);
 33     }
 34 
 35 
 36 
 37   /*  @Override
 38     public void convert(ViewHolder holder, Bean bean) {
 39 
 40         ((TextView) holder.getView(R.id.titleTv)).setText(bean.getTitle());
 41         ((TextView) holder.getView(R.id.descTv)).setText(bean.getDesc());
 42         ((TextView) holder.getView(R.id.timeTv)).setText(bean.getTime());
 43         ((TextView) holder.getView(R.id.phoneTv)).setText(bean.getPhone());
 44 
 45 *//*
 46         TextView tv = holder.getView(R.id.titleTv);
 47         tv.setText(...);
 48 
 49        ImageView view = getView(viewId);
 50        Imageloader.getInstance().loadImag(view.url);
 51 /*//*
 52     }*/
 53 
 54     @Override
 55     public void convert(ViewHolder holder, final Video video) {
 56 
 57         //1、作者的头像
 58         ImageButton mVideoAvatarBtn = holder.getView(R.id.mVideoAvatarBtn);
 59         //如果用的是开源框架Picasso获取网络图片,那就按照下面注释掉这一行代码来做。
 60         // load方法里的参数是请求的图片的url。placeholder方法中的参数是说,加载图片成功之前默认显示的图片
 61         //Picasso.with(mContext).load(Constants.CONTENT_HOST + video.getUserAvatarUrl()).placeholder(R.mipmap.ic_launcher).into(mVideoAvatarBtn);
 62         String imageUrl = Constants.CONTENT_HOST + video.getUserAvatarUrl();
 63 
 64         /*
 65         下面的加载网络图片中,用到了Android-Universal-Image-Loader框架
 66          */
 67         //显示图片的配置
 68         // 使用DisplayImageOptions.Builder()创建DisplayImageOptions
 69         options = new DisplayImageOptions.Builder()
 70                 .showStubImage(R.mipmap.phone)            // 设置图片下载期间显示的图片
 71                 .showImageForEmptyUri(R.mipmap.ic_launcher)    // 设置图片Uri为空或是错误的时候显示的图片
 72                 .showImageOnFail(R.drawable.default_ptr_flip)        // 设置图片加载或解码过程中发生错误显示的图片
 73                 .cacheInMemory(true)                        // 设置下载的图片是否缓存在内存中
 74                 .cacheOnDisc(true)                            // 设置下载的图片是否缓存在SD卡中
 75                 .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)   //图片会缩放到目标大小完全。非常重要,也就是说,这个view有多大,图片就会缩放到多大
 76                 .build();
 77 
 78         ImageLoader.getInstance().displayImage(imageUrl, mVideoAvatarBtn, options);
 79 
 80         //2、作者的昵称
 81         TextView mVideoNicknameTv = holder.getView(R.id.mVideoNicknameTv);
 82         mVideoNicknameTv.setText(video.getUserNickName());
 83 
 84 
 85         //3、视频的缩略图
 86         ImageButton mVideoImgBtn = holder.getView(R.id.mVideoImgBtn);
 87 
 88        //让缩略图的宽度为手机屏幕的宽度,高度为手机屏幕宽度的一半。说白了,就是让 图片的尺寸为2:1
 89         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
 90                 ViewGroup.LayoutParams.MATCH_PARENT,
 91                 (int) (Constants.displayWidth * 0.5f + 0.5f));
 92         mVideoImgBtn.setLayoutParams(params);
 93 
 94         String PicUrl = Constants.CONTENT_HOST + video.getPicUrl();
 95 
 96 
 97         //Picasso.with(mContext).load(Constants.CONTENT_HOST + video.getPicUrl()).placeholder(R.mipmap.defaultimg).into(mVideoImgBtn);
 98         ImageLoader.getInstance().displayImage(PicUrl, mVideoImgBtn, options);
 99 
100 
101         System.out.println("---->" + video.getPicUrl());
102 
103 
104 
105         //跳转到具体是哪一个视频(即item的详情页)
106 /*        mVideoImgBtn.setOnClickListener(new View.OnClickListener() {
107             @Override
108             public void onClick(View v) {
109                 int id = video.getVideoUrl();
110                 mContext.startActivity(new Intent());
111             }
112         });*/
113     }
114 }

 

尤其要注意第75行的属性哦,这样可以让图片缩放到当前控件的大小。(如果没有这一行,图片大小就是包裹内容;如果加了这一行,图片大小就是匹配当前控件的大小,因为我在第88行设置了这个ImageButton的宽度是手机屏幕的宽度,高度是手机屏幕宽度的一半,这样的话,不管网络上的 图片是多大,都能够保证显示出来的图片比例是2:1)

(8)MainActivity.java:

  1 package com.smyhvae.pulltorefreshdemo;
  2 
  3 import android.app.Activity;
  4 import android.content.Context;
  5 import android.os.Bundle;
  6 import android.os.Handler;
  7 import android.os.Message;
  8 import android.util.DisplayMetrics;
  9 import android.util.Log;
 10 import android.widget.ListView;
 11 import android.widget.Toast;
 12 
 13 import com.google.gson.Gson;
 14 import com.google.gson.GsonBuilder;
 15 import com.google.gson.reflect.TypeToken;
 16 import com.handmark.pulltorefresh.library.PullToRefreshBase;
 17 import com.handmark.pulltorefresh.library.PullToRefreshListView;
 18 import com.lidroid.xutils.HttpUtils;
 19 import com.lidroid.xutils.exception.HttpException;
 20 import com.lidroid.xutils.http.RequestParams;
 21 import com.lidroid.xutils.http.ResponseInfo;
 22 import com.lidroid.xutils.http.callback.RequestCallBack;
 23 import com.lidroid.xutils.http.client.HttpRequest;
 24 import com.smyhvae.pulltorefreshdemo.adapter.VideoListViewAdapter;
 25 import com.smyhvae.pulltorefreshdemo.entities.ResponseObject;
 26 import com.smyhvae.pulltorefreshdemo.entities.Video;
 27 import com.smyhvae.pulltorefreshdemo.entities.VideoResponse;
 28 import com.smyhvae.pulltorefreshdemo.utils.Constants;
 29 
 30 import java.util.List;
 31 
 32 
 33 public class MainActivity extends Activity {
 34 
 35     private PullToRefreshListView lv;
 36     private Context mContext;
 37 
 38     private List<Video> videoList; //用来存放视频列表的集合
 39 
 40     private int page = 0;  //当前页码
 41     private int size = 10; //每页显示10个
 42     private int count = 0; //当前页面有多少个视频
 43 
 44     private VideoListViewAdapter videoListViewAdapter;
 45 
 46 
 47 
 48 
 49     @Override
 50     protected void onCreate(Bundle savedInstanceState) {
 51         super.onCreate(savedInstanceState);
 52         setContentView(R.layout.activity_main);
 53         //第一个Activity加载进来时,我们就获取屏幕的宽度和高度
 54         DisplayMetrics displayMetrics = new DisplayMetrics();
 55         getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
 56         Constants.displayWidth = displayMetrics.widthPixels;
 57         Constants.displayHeight = displayMetrics.heightPixels;
 58 
 59 
 60 
 61         initView();
 62 
 63     }
 64 
 65     private void initView() {
 66         lv = (PullToRefreshListView) findViewById(R.id.lv);
 67 
 68         /*
 69         设置刷新的模式:
 70         可选值为:disabled(禁用下拉刷新),
 71         pullFromStart(仅支持下拉刷新),
 72         pullFromEnd(仅支持上拉刷新),
 73         both(二者都支持),
 74         manualOnly(只允许手动触发)
 75          */
 76         lv.setMode(PullToRefreshBase.Mode.BOTH);  //让这个Listview支持上拉加载更多,下拉刷新
 77         lv.setScrollingWhileRefreshingEnabled(true);//滚动的时候不允许刷新,要不然么会很乱
 78         //很重要,刷新时做回调
 79         lv.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener<ListView>() {
 80             @Override
 81             public void onRefresh(PullToRefreshBase<ListView> refreshView) {
 82                 //在这里做数据加载的操作
 83                 loadData(refreshView.getScrollY() < 0);
 84             }
 85         });
 86 
 87         //首次打开页面时,延时200ms后自动加载数据
 88        new Handler (new Handler.Callback(){
 89            @Override
 90             public boolean handleMessage(Message msg) {
 91                lv.setRefreshing();
 92                return true;
 93            }
 94        }).sendEmptyMessageDelayed(0,200);
 95     }
 96 
 97 
 98     //如果是true表示下拉刷新,false表示上拉加载更多(如果y值小于0,说明是下拉操作)
 99     private void loadData(final boolean direction) {
100         //http://172.24.1.49:8081/video/getVideos?apikey=  &typeid=1&page=1
101         RequestParams params = new RequestParams();
102 
103         if (direction) {  //如果是上拉,那应该将page变为第一页
104             page = 1;
105 
106         } else {
107             page++; //如果是下拉,就让page加1
108 
109         }
110 
111         params.addQueryStringParameter("page", String.valueOf(page)); //默认显示第一页
112         // params.addQueryStringParameter("size", "10"); //每页显示10个
113 
114         new HttpUtils().send(HttpRequest.HttpMethod.GET, Constants.VIDEO_LIST + "typeid=1&", params, new RequestCallBack<String>() {
115             @Override
116             public void onSuccess(ResponseInfo<String> responseInfo) {
117                 lv.onRefreshComplete();
118                 Log.d("json", "---video的json数据>" + responseInfo.result);
119 
120                 //解析服务器端的json数据
121                 Gson gson = new GsonBuilder().create();
122                 ResponseObject<VideoResponse> object = gson.fromJson(responseInfo.result, new TypeToken<ResponseObject<VideoResponse>>() {
123                 }.getType());
124 /*                ResponseObject<VideoResponse> object = new GsonBuilder().create().fromJson(responseInfo.result, new TypeToken<VideoResponse>() {
125                 }.getType());*/
126                 page = Integer.parseInt(object.getResult().getPage()); //获取服务器端返回来的当前页码
127                 count = object.getResult().getCnt(); //获取当前页面有多少个视频
128                 Log.d("json","---当前页面的item的个数>"+count);
129                 if (direction) { //下拉刷新
130                     videoList = object.getResult().getVideos();  //获取视频信息的集合,并存放
131 
132                     videoListViewAdapter = new VideoListViewAdapter(MainActivity.this,videoList);
133                     lv.setAdapter(videoListViewAdapter); //为这个listView绑定适配器
134 
135                 } else {//尾部加载更多
136                     videoList.addAll(object.getResult().getVideos());
137 
138                 }
139 
140                 if (count == 0) { //如果当前页面已经没有视频了,那就告诉客户端,不要再拉了,因为后面没有数据了。
141                     lv.setMode(PullToRefreshBase.Mode.PULL_FROM_START);
142                 }
143 
144             }
145 
146             @Override
147             public void onFailure(HttpException e, String s) {
148                 lv.onRefreshComplete();//不管是请求成功还是请求失败,我们都停止加载数据
149                 Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
150 
151             }
152         });
153 
154     }
155 
156 } 

这个MainActivity中讲到了xUtils怎样获取到网络上的json数据,并用Gson解析,然后用pull to refresh处理上拉下拉刷新的逻辑,好吧,确实是快速开发,用到的框架还挺多的好伐~

 

【工程文件】

 2015-05-08-PullToRefreshDemo.rar

 

posted @ 2015-05-08 16:27  千古壹号  阅读(2345)  评论(3编辑  收藏  举报