Android网络框架-Volley实践 使用Volley打造自定义ListView

这篇文章翻译自Ravi Tamada博客中的Android Custom ListView with Image and Text using Volley

最终效果

这个ListView呈现了一些影视信息,每一行是一个影片的信息,每一行中有一张电影的图片,电影的名字、评分、类型、年份等信息。

1.json数据

我们通过解析json然后拿到数据,这个json数据包括json数组,每个json数组中是一个json对象,json对象中包括了电影的图片url地址、标题、年份、评分、类型等信息

JSON Url:http://api.androidhive.info/json/movies.json

 

[
    {
        "title": "Dawn of the Planet of the Apes",
        "image": "http://api.androidhive.info/json/movies/1.jpg",
        "rating": 8.3,
        "releaseYear": 2014,
        "genre": ["Action", "Drama", "Sci-Fi"]
    },
    ....
    ....
]

2.下载Volley库(volley.jar)

如果你第一次使用Volley框架,我建议你去我之前的文章看一下Android网络框架-Volley(一) 工作原理分析 。然后到百度上下载一个volley.jar。添加到项目的lib文件夹里面

3.布局分析

我选择了RelativeLayout来实现这个布局,图片我们使用volley提供的NetworkImageView

现在我们来新建一个Android项目

4.创建一个新的项目

1.打开eclipse,点击File-->New-->Android Application Project。填好基本信息后,我们把包名命名为info.androidhive.customlistviewvolley

2.将volley.jar添加到项目的lib文件夹下

3.我们先把包建好,我们一共分为4个包: adapterappmodel 和 util  。现在我们项目结构如下:

info.androidhive.customlistviewvolley.adater
info.androidhive.customlistviewvolley.app
info.androidhive.customlistviewvolley.model
info.androidhive.customlistviewvolley.util

4.打开res/values/colors.xml。如果没有colors.xml,我们就自己创建一个。然后添加如下代码

 

<?xml version="1.0" encoding="utf-8"?>
#666666#888888#d9d9d9#ffffff#ffffff#ebeef0#ebeef0

5.打开res/values/dimens.xml。添加如下代码

 

17dp15dip13dip12dip

6.在写jsva代码之前,我们先完成UI部分,在res下新建一个drawable文件夹,在res/drawable中新建3个xml文件:list_row_bg.xml、list_row_bg_hover.xml 、list_row_selector.xml 。

 

list_row_bg.xml -没有被点击时listview的样式

<?xml version="1.0" encoding="utf-8"?>

list_row_bg_hover.xml -被点击后listview的样式

<?xml version="1.0" encoding="utf-8"?>

list_row_selector.xml -切换两种样式的slector文件

<?xml version="1.0" encoding="utf-8"?>

7.打开activity_main.xml 添加listview

 

 

 

8.创建每个item的布局文件list_row.xml

<?xml version="1.0" encoding="utf-8"?>

UI部分我们已经完成了,接下来是java代码部分

9.在util包下新建LruBitmapCache.java  这个类是用来缓存图片的,这个类我们在之前文章中已经分析过了。参见Android网络框架-Volley(二) RequestQueue源码分析以及建立一个RequestQueue

package info.androidhive.customlistviewvolley.util;
 
import com.android.volley.toolbox.ImageLoader.ImageCache;
 
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
 
public class LruBitmapCache extends LruCache implements
        ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 2;
 
        return cacheSize;
    }
 
    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }
 
    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }
 
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }
 
    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }
 
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
}

10.在app包下新建AppController.java  这个类是用来创建一个单例RequestQueue的,以及初始化一些volley核心对象

package info.androidhive.customlistviewvolley.app;
 
import info.androidhive.customlistviewvolley.util.LruBitmapCache;
import android.app.Application;
import android.text.TextUtils;
 
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
 
public class AppController extends Application {
 
    public static final String TAG = AppController.class.getSimpleName();
 
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
 
    private static AppController mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized AppController getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }
 
    public  void addToRequestQueue(Request req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public  void addToRequestQueue(Request req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

11.现在我们要在 AndroidManifest.xml  中注册这个AppController,并且添加上网络权限

<?xml version="1.0" encoding="utf-8"?>

12.现在在model包下创建一个Movie实体类,解析完的json数据会保存到这个实体类中

package info.androidhive.customlistviewvolley.model;
 
import java.util.ArrayList;
 
public class Movie {
    //title=标题, thumbnailUrl=图片地址
    private String title, thumbnailUrl;
    //年份
    private int year;
    //评分
    private double rating;
    //类别
    private ArrayList genre;
 
    public Movie() {
    }
 
    public Movie(String name, String thumbnailUrl, int year, double rating,
            ArrayList genre) {
        this.title = name;
        this.thumbnailUrl = thumbnailUrl;
        this.year = year;
        this.rating = rating;
        this.genre = genre;
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setTitle(String name) {
        this.title = name;
    }
 
    public String getThumbnailUrl() {
        return thumbnailUrl;
    }
 
    public void setThumbnailUrl(String thumbnailUrl) {
        this.thumbnailUrl = thumbnailUrl;
    }
 
    public int getYear() {
        return year;
    }
 
    public void setYear(int year) {
        this.year = year;
    }
 
    public double getRating() {
        return rating;
    }
 
    public void setRating(double rating) {
        this.rating = rating;
    }
 
    public ArrayList getGenre() {
        return genre;
    }
 
    public void setGenre(ArrayList genre) {
        this.genre = genre;
    }
 
}

13.在adapter包下新建一个 CustomListAdapter.java  adapter会将item布局加载出来,并且将数据显示到listview上面

package info.androidhive.customlistviewvolley.adater;
 
import info.androidhive.customlistviewvolley.R;
import info.androidhive.customlistviewvolley.app.AppController;
import info.androidhive.customlistviewvolley.model.Movie;
 
import java.util.List;
 
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
 
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
 
public class CustomListAdapter extends BaseAdapter {
    private Activity activity;
    private LayoutInflater inflater;
    private List movieItems;
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();
 
    public CustomListAdapter(Activity activity, List movieItems) {
        this.activity = activity;
        this.movieItems = movieItems;
    }
 
    @Override
    public int getCount() {
        return movieItems.size();
    }
 
    @Override
    public Object getItem(int location) {
        return movieItems.get(location);
    }
 
    @Override
    public long getItemId(int position) {
        return position;
    }
 
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
 
        if (inflater == null)
            inflater = (LayoutInflater) activity
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (convertView == null)
            convertView = inflater.inflate(R.layout.list_row, null);
 
        if (imageLoader == null)
            imageLoader = AppController.getInstance().getImageLoader();
        NetworkImageView thumbNail = (NetworkImageView) convertView
                .findViewById(R.id.thumbnail);
        TextView title = (TextView) convertView.findViewById(R.id.title);
        TextView rating = (TextView) convertView.findViewById(R.id.rating);
        TextView genre = (TextView) convertView.findViewById(R.id.genre);
        TextView year = (TextView) convertView.findViewById(R.id.releaseYear);
 
        // getting movie data for the row
        Movie m = movieItems.get(position);
 
        // thumbnail image
        thumbNail.setImageUrl(m.getThumbnailUrl(), imageLoader);
         
        // title
        title.setText(m.getTitle());
         
        // rating
        rating.setText("Rating: " + String.valueOf(m.getRating()));
         
        // genre
        String genreStr = "";
        for (String str : m.getGenre()) {
            genreStr += str + ", ";
        }
        genreStr = genreStr.length() > 0 ? genreStr.substring(0,
                genreStr.length() - 2) : genreStr;
        genre.setText(genreStr);
         
        // release year
        year.setText(String.valueOf(m.getYear()));
 
        return convertView;
    }
 
}

14.打开我们的MainActivity.java。添加如下代码,我们使用JsonArrayRequest来发送请求,发送json请求我们在Android网络框架-Volley(四) 使用get和post方法发送json请求 已经讲过了。我们将解析来的Movie对象存储在一个ArrayList中,调用notifyDataSetChanged()方法通知listview去更新我们的数据。

package info.androidhive.customlistviewvolley;
 
import info.androidhive.customlistviewvolley.adater.CustomListAdapter;
import info.androidhive.customlistviewvolley.app.AppController;
import info.androidhive.customlistviewvolley.model.Movie;
 
import java.util.ArrayList;
import java.util.List;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ListView;
 
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;
 
public class MainActivity extends Activity {
    // 用来打Log日志的TAG
    private static final String TAG = MainActivity.class.getSimpleName();
 
    // JSON地址
    private static final String url = "http://api.androidhive.info/json/movies.json";
    private ProgressDialog pDialog;
    //用来存储Movie对象的list
    private List movieList = new ArrayList();
    private ListView listView;
    private CustomListAdapter adapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        listView = (ListView) findViewById(R.id.list);
        adapter = new CustomListAdapter(this, movieList);
        listView.setAdapter(adapter);
 
        pDialog = new ProgressDialog(this);
        // 加载框
        pDialog.setMessage("Loading...");
        pDialog.show();
 
        // 发送一个Json请求
        JsonArrayRequest movieReq = new JsonArrayRequest(url,
                new Response.Listener() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.d(TAG, response.toString());
                        hidePDialog();
 
                        // 解析json数据
                        for (int i = 0; i < response.length(); i++) {
                            try {
 
                                JSONObject obj = response.getJSONObject(i);
                                Movie movie = new Movie();
                                movie.setTitle(obj.getString("title"));
                                movie.setThumbnailUrl(obj.getString("image"));
                                movie.setRating(((Number) obj.get("rating"))
                                        .doubleValue());
                                movie.setYear(obj.getInt("releaseYear"));
 
                                // Genre是一个json数组
                                JSONArray genreArry = obj.getJSONArray("genre");
                                ArrayList genre = new ArrayList();
                                for (int j = 0; j < genreArry.length(); j++) {
                                    genre.add((String) genreArry.get(j));
                                }
                                movie.setGenre(genre);
 
                                // 将解析好的一个movie对象添加到list中
                                movieList.add(movie);
 
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
 
                        }
 
                        // 通知listview我们的数据已经改变,现在更新
                        adapter.notifyDataSetChanged();
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d(TAG, "Error: " + error.getMessage());
                        hidePDialog();
 
                    }
                });
 
        // 将request添加到requestQueue中
        AppController.getInstance().addToRequestQueue(movieReq);
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        hidePDialog();
    }
 
    private void hidePDialog() {
        if (pDialog != null) {
            pDialog.dismiss();
            pDialog = null;
        }
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值