Android 自定义 ListView 显示网络上 JSON 格式歌曲列表
本文內容
- 環境
- 項目結構
- 演示自定義 ListView 顯示網絡上 JSON 歌曲列表
- 參考資料
本文最開始看的是一個國人翻譯的文章,沒有源代碼可下載,根據文中提供的代碼片段,自己新建的項目(比較可惡的是,沒有圖標圖片資源,只能自己亂搞),但程序不是很穩定,有時能顯示出列表中的縮略圖,有時顯示不出來,還在主線程訪問了網絡。但在文章評論中,作者給出英文原文鏈接,本來想這下沒事了吧,結果下載源代碼運行后,還是有問題~仔細看英文原文,原來他也是根據 Github 上一個項目搞的,只是添加了式樣,以及顯示完整的歌曲列表,包括歌曲名、歌手名、縮略圖、時長。
看來只能自己研究了,式樣用英文原文的,改變主要有兩個,一個是英文原文的網絡歌曲列表是 .xml 格式文件,本文采用 .json 格式;二是調整了網絡訪問。
環境
- Windows 2008 R2 64 位
- Eclipse ADT V22.6.2,Android 4.4.3
- SAMSUNG GT-I9008L,Android OS 2.2.2
?
項目結構
圖 1 項目結構
?
演示自定義 ListView 顯示網絡上 JSON 歌曲列表
演示運行結果如下所示,本文只給出核心代碼。點擊此處下載,自己調試一下。
圖 2 主程序
自定義式樣
gradient_bg.xml、gradient_bg_hover.xml、list_selector.xml 以及 image_bg.xml,定義列表背景,選中和沒選中的式樣,以及縮略圖的背景。
main.xml 主頁面及其后臺
main.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="fill_parent" android:orientation="vertical" > ? <ListView android:id="@+id/mylist" android:layout_width="fill_parent" android:layout_height="wrap_content" android:divider="#b5b5b5" android:dividerHeight="1dp" android:listSelector="@drawable/list_selector" /> ? <Button android:id="@+id/btn_clear" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="清除緩存, 重新加載" /> ? </LinearLayout>CustomizedListView.java
package com.example.androidhive; ? import org.json.JSONArray; ? import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ListView; ? public class CustomizedListView extends Activity { String url = "http://files.cnblogs.com/liuning8023/Android_Music_Demo_json_array.xml"; ? ListView list; LazyAdapter adapter; /* Button btn; */ ? private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0x123) { Request rq = new Request(); JSONArray jsonArr = rq.getJsonFromUrl(url); list = (ListView) findViewById(R.id.mylist); adapter = new LazyAdapter(CustomizedListView.this, this, jsonArr); list.setAdapter(adapter); ? /* * btn = (Button) findViewById(R.id.btn_clear); * btn.setOnClickListener(new OnClickListener() { * * @Override public void onClick(View arg0) { * adapter.imageLoader.clearCache(); * adapter.notifyDataSetChanged(); } }); */ } super.handleMessage(msg); } }; ? @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ? new Thread(new Runnable() { @Override public void run() { Message msg = new Message(); msg.what = 0x123; handler.sendMessage(msg); } }).start(); ? } ? }list_row.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/list_selector" android:orientation="horizontal" android:padding="5dip" > ? <!-- ListRow Left sied Thumbnail image --> <LinearLayout android:id="@+id/thumbnail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dip" android:layout_alignParentLeft="true" android:background="@drawable/image_bg" android:layout_marginRight="5dip"> <ImageView android:id="@+id/list_image" android:layout_width="50dip" android:layout_height="50dip" android:src="@drawable/rihanna"/> </LinearLayout> <!-- Title Of Song--> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/thumbnail" android:layout_toRightOf="@+id/thumbnail" android:text="Rihanna Love the way lie" android:textColor="#040404" android:typeface="sans" android:textSize="15dip" android:textStyle="bold"/> ? <!-- Artist Name --> <TextView android:id="@+id/artist" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/title" android:textColor="#343434" android:textSize="10dip" android:layout_marginTop="1dip" android:layout_toRightOf="@+id/thumbnail" android:text="Just gona stand there and ..." /> ? <!-- Rightend Duration --> <TextView android:id="@+id/duration" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignTop="@id/title" android:gravity="right" android:text="5:45" android:layout_marginRight="5dip" android:textSize="10dip" android:textColor="#10bcc9" android:textStyle="bold"/> <!-- Rightend Arrow --> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/arrow" android:layout_alignParentRight="true" android:layout_centerVertical="true"/> ? </RelativeLayout>LazyAdapter.java
package com.example.androidhive; ? import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; ? import android.app.Activity; import android.content.Context; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; ? public class LazyAdapter extends BaseAdapter { ? private Activity activity; private JSONArray data; private static LayoutInflater inflater = null; public ImageLoader imageLoader; ? public LazyAdapter(Activity a, Handler handler, JSONArray jsonArr) { activity = a; data = jsonArr; inflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); imageLoader = new ImageLoader(activity.getApplicationContext()); } ? public int getCount() { return data == null ? 0 : data.length(); } ? public Object getItem(int position) { return position; } ? public long getItemId(int position) { return position; } ? public View getView(int position, View convertView, ViewGroup parent) { View vi = convertView; if (convertView == null) vi = inflater.inflate(R.layout.list_row, null); ? JSONObject song = null; ? ImageView image = (ImageView) vi.findViewById(R.id.list_image); TextView tvartist = (TextView) vi.findViewById(R.id.artist); TextView tvtitle = (TextView) vi.findViewById(R.id.title); TextView tvduration = (TextView) vi.findViewById(R.id.duration); ? try { song = data.getJSONObject(position); imageLoader.DisplayImage(song.getString("thumb_url"), image); tvartist.setText(song.getString("artist")); tvtitle.setText(song.getString("title")); tvduration.setText(song.getString("duration")); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } ? return vi; } }其他工具類
FileCache.java 在外存緩存圖片
package com.example.androidhive; ? import java.io.File; import android.content.Context; ? public class FileCache { ? private File cacheDir; ? public FileCache(Context context) { // Find the dir to save cached images if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) cacheDir = new File( android.os.Environment.getExternalStorageDirectory(), "LazyList"); else cacheDir = context.getCacheDir(); if (!cacheDir.exists()) cacheDir.mkdirs(); } ? public File getFile(String url) { // I identify images by hashcode. Not a perfect solution, good for the // demo. String filename = String.valueOf(url.hashCode()); // Another possible solution (thanks to grantland) // String filename = URLEncoder.encode(url); File f = new File(cacheDir, filename); return f; ? } ? public void clear() { File[] files = cacheDir.listFiles(); if (files == null) return; for (File f : files) f.delete(); } ? }MemoryCache.java 在內存緩存圖片
package com.example.androidhive; ? import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import android.graphics.Bitmap; import android.util.Log; ? public class MemoryCache { ? private static final String TAG = "MemoryCache"; private Map<String, Bitmap> cache=Collections.synchronizedMap( new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering private long size=0;//current allocated size private long limit=1000000;//max memory in bytes ? public MemoryCache(){ //use 25% of available heap size setLimit(Runtime.getRuntime().maxMemory()/4); } public void setLimit(long new_limit){ limit=new_limit; Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB"); } ? public Bitmap get(String id){ try{ if(!cache.containsKey(id)) return null; //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 return cache.get(id); }catch(NullPointerException ex){ ex.printStackTrace(); return null; } } ? public void put(String id, Bitmap bitmap){ try{ if(cache.containsKey(id)) size-=getSizeInBytes(cache.get(id)); cache.put(id, bitmap); size+=getSizeInBytes(bitmap); checkSize(); }catch(Throwable th){ th.printStackTrace(); } } private void checkSize() { Log.i(TAG, "cache size="+size+" length="+cache.size()); if(size>limit){ Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated while(iter.hasNext()){ Entry<String, Bitmap> entry=iter.next(); size-=getSizeInBytes(entry.getValue()); iter.remove(); if(size<=limit) break; } Log.i(TAG, "Clean cache. New size "+cache.size()); } } ? public void clear() { try{ //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 cache.clear(); size=0; }catch(NullPointerException ex){ ex.printStackTrace(); } } ? long getSizeInBytes(Bitmap bitmap) { if(bitmap==null) return 0; return bitmap.getRowBytes() * bitmap.getHeight(); } }ImageLoader.java 加載圖片并緩存
package com.example.androidhive; ? import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; ? import android.os.Handler; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.widget.ImageView; ? public class ImageLoader { ? MemoryCache memoryCache = new MemoryCache(); FileCache fileCache; private Map<ImageView, String> imageViews = Collections .synchronizedMap(new WeakHashMap<ImageView, String>()); ExecutorService executorService; Handler handler = new Handler();// handler to display images in UI thread ? public ImageLoader(Context context) { fileCache = new FileCache(context); executorService = Executors.newFixedThreadPool(5); } ? final int stub_id = R.drawable.stub; ? /* * 顯示圖片 若緩存存在,則顯示,否則獲取 */ public void DisplayImage(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap = memoryCache.get(url); // 根據 url 從內存獲得圖片 if (bitmap != null) imageView.setImageBitmap(bitmap); else { queuePhoto(url, imageView); imageView.setImageResource(stub_id); } } ? private void queuePhoto(String url, ImageView imageView) { PhotoToLoad p = new PhotoToLoad(url, imageView); executorService.submit(new PhotosLoader(p)); } ? private Bitmap getBitmap(String url) { File f = fileCache.getFile(url); ? // from SD cache Bitmap b = decodeFile(f); if (b != null) return b; ? // from web try { Bitmap bitmap = null; URL imageUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) imageUrl .openConnection(); ? conn.setRequestMethod("GET"); // Sets the flag indicating whether this URLConnection allows input. // conn.setDoInput(true); conn.setConnectTimeout(3000); conn.setReadTimeout(3000); // Flag to define whether the protocol will automatically follow // redirects or not. conn.setInstanceFollowRedirects(true); int response_code = conn.getResponseCode(); if (response_code == 200) { InputStream is = conn.getInputStream(); OutputStream os = new FileOutputStream(f); StreamUtils.CopyStream(is, os); os.close(); conn.disconnect(); bitmap = decodeFile(f); return bitmap; } else { conn.disconnect(); return null; } ? } catch (Throwable ex) { ex.printStackTrace(); if (ex instanceof OutOfMemoryError) memoryCache.clear(); return null; } } ? // decodes image and scales it to reduce memory consumption private Bitmap decodeFile(File f) { try { // decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; FileInputStream stream1 = new FileInputStream(f); BitmapFactory.decodeStream(stream1, null, o); stream1.close(); ? // Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE = 70; int width_tmp = o.outWidth, height_tmp = o.outHeight; int scale = 1; while (true) { if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) break; width_tmp /= 2; height_tmp /= 2; scale *= 2; } ? // decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; FileInputStream stream2 = new FileInputStream(f); Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2); stream2.close(); return bitmap; } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } return null; } ? // Task for the queue private class PhotoToLoad { public String url; public ImageView imageView; ? public PhotoToLoad(String u, ImageView i) { url = u; imageView = i; } } ? class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; ? PhotosLoader(PhotoToLoad photoToLoad) { this.photoToLoad = photoToLoad; } ? @Override public void run() { try { if (imageViewReused(photoToLoad)) return; Bitmap bmp = getBitmap(photoToLoad.url); memoryCache.put(photoToLoad.url, bmp); if (imageViewReused(photoToLoad)) return; BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad); handler.post(bd); } catch (Throwable th) { th.printStackTrace(); } } } ? boolean imageViewReused(PhotoToLoad photoToLoad) { String tag = imageViews.get(photoToLoad.imageView); if (tag == null || !tag.equals(photoToLoad.url)) return true; return false; } ? // Used to display bitmap in the UI thread class BitmapDisplayer implements Runnable { Bitmap bitmap; PhotoToLoad photoToLoad; ? public BitmapDisplayer(Bitmap b, PhotoToLoad p) { bitmap = b; photoToLoad = p; } ? public void run() { if (imageViewReused(photoToLoad)) return; if (bitmap != null) photoToLoad.imageView.setImageBitmap(bitmap); else photoToLoad.imageView.setImageResource(stub_id); } } ? public void clearCache() { memoryCache.clear(); fileCache.clear(); } ? }Request.java 訪問網絡
package com.example.androidhive; ? import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; ? import org.json.JSONArray; import org.json.JSONException; ? public class Request { ? public Request() { } ? public JSONArray getJsonFromUrl(String urlStr) { ? try { URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); // Sets the flag indicating whether this URLConnection allows input. // conn.setDoInput(true); conn.setConnectTimeout(3000); conn.setReadTimeout(3000); // Flag to define whether the protocol will automatically follow // redirects or not. conn.setInstanceFollowRedirects(true); ? int response_code = conn.getResponseCode(); if (response_code == 200) { InputStream in = conn.getInputStream(); InputStreamReader inputReader = new InputStreamReader(in); BufferedReader buffReader = new BufferedReader(inputReader); ? String line = "", JsonStr = ""; while ((line = buffReader.readLine()) != null) { JsonStr += line; } JSONArray jsonArray = new JSONArray(JsonStr); return jsonArray; } else { conn.disconnect(); return null; } ? } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }StreamUtils.java 緩存時的I/O操作
package com.example.androidhive; ? import java.io.InputStream; import java.io.OutputStream; ? public class StreamUtils { public static void CopyStream(InputStream is, OutputStream os) { final int buffer_size=1024; try { byte[] bytes=new byte[buffer_size]; for(;;) { int count=is.read(bytes, 0, buffer_size); if(count==-1) break; os.write(bytes, 0, count); } } catch(Exception ex){} } }?
參考資料
- Github LazyList
- androidhive android-custom-listview-with-imgae-and-text
- androidhive android android-custom-listview-with-image-and-text 譯文
這三個鏈接的關系是,第二個鏈接的演示是根據第一個鏈接完成的,第三個鏈接翻譯的第二個鏈接。但遺憾的是,英文原文是有bug的,程序運行不穩定。
- Android ListView 和 ***Adapter 從本地/網絡獲取歌曲列表
- Android LazyList 從網絡獲取圖片并緩存
?
下載 Demo
轉載于:https://www.cnblogs.com/liuning8023/p/3829914.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Android 自定义 ListView 显示网络上 JSON 格式歌曲列表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery简单实现树形结构收缩展开效果
- 下一篇: angularjs 服务