• 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

    图 1 项目结构

     

    演示自定义 ListView 显示网络上 JSON 歌曲列表


    演示运行结果如下所示,本文只给出核心代码。点击此处下载,自己调试一下。

    device-2014-07-07-160204

    图 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){}
        }
    }

     

    参考资料

    这三个链接的关系是,第二个链接的演示是根据第一个链接完成的,第三个链接翻译的第二个链接。但遗憾的是,英文原文是有bug的,程序运行不稳定。

     

    下载 Demo

  • 相关阅读:
    spring注解事务管理
    Spring切入点表达式常用写法
    JPA和事务管理
    maven仓库
    struts2拦截器
    js中 转义字符
    jquery中变量加$和不加$有什么区别!
    spring大乱炖
    第一章 java多线程
    CS:APP 05 笔记
  • 原文地址:https://www.cnblogs.com/liuning8023/p/3829914.html
Copyright © 2020-2023  润新知