依赖库:
先看服务端的代码(对象封装类和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)); } } } }--------------------------------------------分割线-------------------------------------------------------------------
先贴出布局来
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; } } }