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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM