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個包: adapter, app, model 和 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.xmllist_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;
    }
 
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章