1. 程式人生 > >PullToRefreshListView進階(五)----->上下重新整理、上拉載入

PullToRefreshListView進階(五)----->上下重新整理、上拉載入

依賴庫:

先看服務端的程式碼(物件封裝類和servlet類)

ShopInfo.java(get、set、構造器、toString方法省略)

private String name;
private String img;
ShopListServlet.java
package com.atguigu.dianpin_server.servlet;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;

/**
 * 獲取分頁ShopList的json字串
 */
public class ShopListServlet extends HttpServlet {
	private List<ShopInfo> infos;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		init();

		// 得到start和count的請求引數
		int start = Integer.parseInt(request.getParameter("start"));
		int count = Integer.parseInt(request.getParameter("count"));
		// 如果start太大, 就返回一個空串
		// 15 start=15
		if (start >= infos.size()) {
			response.getWriter().write("");
			return;
		}
		// 從集合中取當前請求頁的資料集合
		List<ShopInfo> data = new ArrayList<ShopInfo>();

		// 11 start=10&count=5
		if (start + count > infos.size()) {
			count = infos.size() - start;
		}
		for (int i = 0; i < count; i++) {
			data.add(infos.get(start + i));
		}

		// 轉換為json字串
		String json = new Gson().toJson(data);

		// 寫到客戶端
		response.setContentType("text/json;charset=utf-8");
		response.getWriter().write(json);
		// [{"name":"商鋪名稱1", "img":"f1.jpg"},{"name":"商鋪名稱2", "img":"f12.jpg"}]
	}

	public void init() {

		if (infos == null) {
			infos = new ArrayList<ShopInfo>();
			// 得到/image的真實路徑
			String imagesPath = getServletContext().getRealPath("/image");
			// 得到路徑物件
			File dirFile = new File(imagesPath);
			// 得到所有圖片file物件
			File[] files = dirFile.listFiles();
			// 遍歷
			for (int i = 0; i < files.length; i++) {
				// 將圖片資訊封裝為一個shopinfo物件, 並儲存到集合中
				String imageName = files[i].getName();
				String name = "商鋪名稱 " + (i + 1);
				infos.add(new ShopInfo(name, imageName));
			}
		}
	}
}
--------------------------------------------分割線-------------------------------------------------------------------
看android程式碼

先貼出佈局來

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    
    <com.handmark.pulltorefresh.library.PullToRefreshListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
    <ProgressBar
        android:id="@+id/pb_main"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_gravity="center"/>

</FrameLayout>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="100dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/iv_img"
        android:layout_width="90dp"
        android:layout_height="90dp"/>

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="描述文字" 
        android:layout_marginLeft="20dp"
        android:textSize="20sp"/>
</LinearLayout>
listview_foot.xml
<?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="wrap_content"
    android:orientation="horizontal" 
    android:gravity="center"
    android:clickable="false"
    android:focusable="false">
    <ProgressBar
        android:id="@+id/pb_foot"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/tv_foot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="正在載入中..." />
</LinearLayout>
<!-- 
	1. 如果還有更多資料, 它就會顯示
	2. 如果沒有更多資料: 隱藏ProgressBar, 更新TextView的文字
 -->

ShopInfo.java(get、set、構造器、toString方法省略)

private String name;
private String img;

VolleyTool.java(框架的工具類)

package com.atguigu.day03_test;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.android.volley.toolbox.Volley;

public class VolleyTool {
	//初始化請求佇列、圖片載入器
	private RequestQueue queue;
	private ImageLoader imageLoader;

	//私有靜態例項
	private static VolleyTool instance;
	//私有構造方法
	private VolleyTool(Context context) {
		//建立請求佇列
		queue = Volley.newRequestQueue(context);
		//建立圖片載入器
		imageLoader = new ImageLoader(queue, new LruImageCache());
	}
	//公共、靜態的方法
	public static VolleyTool getInstance(Context context) {
		if (instance == null) {
			instance = new VolleyTool(context);
		}
		return instance;
	}

	//得到請求佇列
	public RequestQueue getQueue() {
		return queue;
	}
	//得到圖片載入器
	public ImageLoader getImageLoader() {
		return imageLoader;
	}

	/**
	 * 使用LRU回收演算法的快取類
	 */
	class LruImageCache implements ImageCache {

		// 快取容器
		private LruCache<String, Bitmap> cache;

		public LruImageCache() {
			// 計算快取的最值
			int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
			//建立快取物件例項
			cache = new LruCache<String, Bitmap>(maxSize) {
				@Override
				protected int sizeOf(String key, Bitmap value) {
					// 返回bitmap佔用的記憶體大小
					return value.getRowBytes() * value.getHeight();
				}
			};
		}

		// 從快取中取圖片物件
		@Override
		public Bitmap getBitmap(String url) {
			return cache.get(url);
		}

		// 將圖片物件儲存到快取容器中
		@Override
		public void putBitmap(String url, Bitmap bitmap) {
			cache.put(url, bitmap);
		}

	}
}
MainActivity.java
package com.atguigu.day03_test;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.StringRequest;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshListView;

public class MainActivity extends Activity {
	// 進度條
	private ProgressBar pb_main;
	// 請求佇列
	RequestQueue requestQueue;

	// 顯示存放服務端資料的listView
	private PullToRefreshListView lv_main;
	// 資料物件集合
	private List<ShopInfo> data = new ArrayList<ShopInfo>();
	// 介面卡
	private MainAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 定義的PullToRefreshListView需要轉型
		lv_main = (PullToRefreshListView) findViewById(R.id.lv_main);
		// 下拉重新整理的監聽
		lv_main.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener<ListView>() {
			@Override
			public void onRefresh(PullToRefreshBase<ListView> refreshView) {
				//定義一個標示,如果為true,代表下拉
				loadData(true);
			}
		});
		// 上拉載入的重新整理
		lv_main.setOnLastItemVisibleListener(new PullToRefreshBase.OnLastItemVisibleListener() {
			@Override
			public void onLastItemVisible() {
				// pb_foot.isShown()說明資料沒有載入完畢
				if (pb_foot.isShown()) {
					//定義一個標示,如果為false,代表上拉
					loadData(false);
				}
			}
		});
		// 建立進度條物件
		pb_main = (ProgressBar) findViewById(R.id.pb_main);
		// 得到請求佇列
		requestQueue = VolleyTool.getInstance(getApplicationContext())
				.getQueue();

		// 新增一個footView(上拉鬆開載入)
		addFootView();

		// 初始化載入資料顯示(false或者true均可)
		loadData(false);
	}

	/**
	 * 新增一個footView 1. 如果還有更多資料, 它就會顯示 2. 如果沒有更多資料: 隱藏ProgressBar, 更新TextView的文字
	 */
	private ProgressBar pb_foot;
	private TextView tv_foot;

	private void addFootView() {
		View footView = View.inflate(this, R.layout.listview_foot, null);
		pb_foot = (ProgressBar) footView.findViewById(R.id.pb_foot);
		tv_foot = (TextView) footView.findViewById(R.id.tv_foot);
		lv_main.getRefreshableView().addFooterView(footView);
	}

	/*
	 * 頁面上下滑動,如果還沒有載入完畢,就快速滑動過去
	 * 這樣消耗記憶體 定義一個標記,標記的意思是--某次請求是否正在載入圖片(預設沒有載入,表明已經載入過)
	 */
	private boolean loading = false;

	private void loadData(final boolean reset) {

		/*
		 * 如果正在載入,直接結束,從新滑到的圖片無需繼續載入了
		 */
		if (loading)
			return;
		// 一旦方法執行,就將標記改成true,說明正在載入
		loading = true;

		// 計算start-如果載入第一頁就是0,如果不是第一頁就是data.size
		int start = reset ? 0 : data.size();
		// data.size()==10 -->start=10
		String url = "http://192.168.30.41:8090/dianpin_03/ShopListServlet?start="
				+ start + "&count=5";

		// 建立一個請求
		Request request = new StringRequest(url, new Listener<String>() {

			@Override
			public void onResponse(String response) {
				/*
				 * 從伺服器得到資料,一旦該方法觸發,說明某次請求已經載入完畢圖片了 要將標記改為false,說明已經載入完畢,無需載入了
				 */
				loading = false;

				/*
				 * 即使載入完畢,如果繼續往下拉的話,還會發送請求 這裡需要判斷伺服器端返回null值的情況(檢視服務端程式碼)
				 */
				if ("".equals(response)) {
					// 隱藏ProgressBar, 更新TextView的文字
					pb_foot.setVisibility(View.GONE);
					tv_foot.setText("已載入完部資料");
					// 將該方法直接返回,無需繼續往下執行了
					return;
				}

				// 將伺服器端的json陣列解析為ShopInfo物件集合
				List<ShopInfo> newData = new Gson().fromJson(response,
						new TypeToken<List<ShopInfo>>() {
						}.getType());

				// 因為每次請求5個json物件,如果返回小於5說明已經載入完所有的資料了
				if (newData.size() < 5) {
					// 隱藏ProgressBar, 更新TextView的文字
					pb_foot.setVisibility(View.GONE);
					tv_foot.setText("已載入完部資料");
				}

				/*
				 * 第一次載入 lv_main.setAdapter(adapter);
				 * 說明只顯示第一頁的資料
				 */
				
				if (adapter == null) {
					data = newData;
					adapter = new MainAdapter();
					lv_main.setAdapter(adapter);
					pb_main.setVisibility(View.GONE);
				} else {
					if (reset) {
						// 如果介面卡不為null,並且下拉重新整理,需要清空資料,只加載第一頁
						data.clear();
						// 顯示載入更多
						pb_foot.setVisibility(View.VISIBLE);
						tv_foot.setText("下拉載入更多");
						lv_main.onRefreshComplete();
					}
					// 不是第一次載入,就需要將每次獲取的資料放到data集合中
					data.addAll(newData);
					adapter.notifyDataSetChanged();
				}
			}
		}, new Response.ErrorListener() {
			@Override
			public void onErrorResponse(VolleyError error) {
				Toast.makeText(getApplicationContext(), "請求伺服器異常", 0).show();
			}
		});
		// 將請求新增到佇列中, 自動處理
		requestQueue.add(request);

	}

	/**
	 * 介面卡程式碼
	 */
	class MainAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return data.size();
		}

		@Override
		public Object getItem(int position) {
			return data.get(position);
		}

		@Override
		public long getItemId(int position) {
			return 0;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder holder = null;
			if (convertView == null) {
				holder = new ViewHolder();
				convertView = View.inflate(getApplicationContext(),
						R.layout.list_item, null);
				holder.imageView = (NetworkImageView) convertView
						.findViewById(R.id.iv_img);
				holder.textView = (TextView) convertView
						.findViewById(R.id.tv_name);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}

			ShopInfo shopInfo = data.get(position);
			holder.textView.setText(shopInfo.getName());

			// 設定未載入預設圖片
			holder.imageView.setDefaultImageResId(R.drawable.default_icon);
			// 設定載入異常的圖片
			holder.imageView.setErrorImageResId(R.drawable.error);
			// 動態載入圖片
			String url = "http://192.168.30.41:8090/dianpin_03/image/"
					+ shopInfo.getImg();
			holder.imageView.setImageUrl(url,
					VolleyTool.getInstance(getApplicationContext())
							.getImageLoader());

			return convertView;
		}

		class ViewHolder {
			NetworkImageView imageView;
			TextView textView;
		}
	}
}