Android 自定义 ListView 显示网络上 JSON 格式歌曲列表

it2022-05-08  7

本文内容

环境 项目结构 演示自定义 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

相关资源:数据结构—成绩单生成器

最新回复(0)