从网站解析JSON异步加载到ListView事例

从http://www.imooc.com/learn/406学习到从网站解析JSON异步加载到ListView这样一个例子。整理如下。

主要知识点包括:异步(子线程)下载图片、图片的缓存、JSON解析

最终列表效果如图



主布局就是一个ListView,没什么好说的。

然后是每个子View的布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:paddingLeft="5dp">

        <TextView
            android:id="@+id/title_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:text="Title"
            android:textSize="15sp" />

        <TextView
            android:id="@+id/content_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxLines="3"
            android:text="content"
            android:textSize="10sp" />

    </LinearLayout>
</LinearLayout>

默认左边一张android机器人小图,右边标题和内容


public class MainActivity extends AppCompatActivity {

    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list_view);
        String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
        /*开启异步进程*/
        new newsAsyncTask().execute(URL);
    }

    class newsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {

        @Override
        protected List<NewsBean> doInBackground(String... params) {
            String url = params[0];
            List<NewsBean> newsBeanList = new ArrayList<>();
            NewsBean newsBean;
            try {
                /*打开url对应的输入流,用readStream方法得到String格式的数据*/
                String jsonString = readStream(new URL(url).openStream());
                JSONObject jsonObject = new JSONObject(jsonString);
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                for (int i = 0; i < jsonArray.length(); i++) {
                    /*每个JSONArray里的一项都是一个包含图片、标题、内容的JSONObject对象,依次把他们存入
                    * 一个封装的newsBean数组中*/
                    jsonObject = jsonArray.getJSONObject(i);
                    newsBean = new NewsBean();
                    newsBean.newsImageUrl = jsonObject.getString("picSmall");
                    newsBean.newsTitle = jsonObject.getString("name");
                    newsBean.newsContent = jsonObject.getString("description");
                    newsBeanList.add(newsBean);
                }
            } catch (IOException | JSONException e) {
                e.printStackTrace();
            }
            return newsBeanList;
        }

        @Override
        protected void onPostExecute(List<NewsBean> newsBeans) {
            super.onPostExecute(newsBeans);
            NewsArrayAdapter newsArrayAdapter = new NewsArrayAdapter(MainActivity.this,
                    R.layout.item_layout, newsBeans);
            listView.setAdapter(newsArrayAdapter);
        }
    }

    private String readStream(InputStream inputStream) {
        String result = "";
        try {
            String line;
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            while ((line = bufferedReader.readLine()) != null) {
                result += line;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

MainActivity中的url是一个慕课网的API


可以看到整个的首先是一个Object,点击展开


我们需要的是这个名为“data”的JSONArray


遍历JSONArray中的每个JSONObject,我们需要的就是里面的数据。把他们存进一个NewsBean封装类里

public class NewsBean {
    public String newsImageUrl;
    public String newsTitle;
    public String newsContent;
}

完成后把List<NewsBean> newsBeans传入我们自定义的Adapter中,加载到ListView里显示出来。

那么我们具体的去看看这个自定义的Adapter


public class NewsArrayAdapter extends ArrayAdapter<NewsBean> {

    private int resource;
    private List<NewsBean> objects;
    private ImageLoader imageLoader;

    public NewsArrayAdapter(Context context, int resource, List<NewsBean> objects) {
        super(context, resource, objects);
        this.resource = resource;
        this.objects = objects;
        imageLoader = new ImageLoader();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        ViewHolder viewHolder;

        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resource, null);
            /*viewHolder用来对控件实例进行缓存,避免每次通过findViewById()获取控件*/
            viewHolder = new ViewHolder();
            viewHolder.titleView = (TextView) view.findViewById(R.id.title_text);
            viewHolder.contentView = (TextView) view.findViewById(R.id.content_text);
            viewHolder.imageView = (ImageView) view.findViewById(R.id.image_view);
            view.setTag(viewHolder);
        } else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.titleView.setText(objects.get(position).newsTitle);
        viewHolder.contentView.setText(objects.get(position).newsContent);
        String imageUrl = objects.get(position).newsImageUrl;
        /*每个imageView设置独立对应的标签(对应的url),避免上下滑动listView时,
        缓存中的图片加载显示到不对应的imageView中*/
        viewHolder.imageView.setTag(imageUrl);
        /*通过自定义的imageLoader类设置图片*/
        imageLoader.setImageByThread(viewHolder.imageView, imageUrl);
        return view;
    }

    class ViewHolder {
        TextView titleView;
        TextView contentView;
        ImageView imageView;
    }
}

ListView中经典的利用convertView和ViewHolder避免多次加载的技巧我们当然用到了,我们还用到了一个自定义的类,用这个类中的方法去加载图片。那我们去看看这个类。

public class ImageLoader {

    private ImageView imageView;
    private String imageUrl;
    private LruCache<String, Bitmap> mCache;

    public ImageLoader() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 4;
        /*用来缓存图片的内存,设置大小为最大可用内存的1/4*/
        mCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                /*默认返回的是数量,我们重写让它返回每个bitmap的大小*/
                return value.getByteCount();
            }
        };
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            /*当imageView与设置的Tag配对时才加载图片*/
            if (imageView.getTag().equals(imageUrl)) {
                imageView.setImageBitmap((Bitmap) msg.obj);
            }
        }
    };

    public void setImageByThread(ImageView imageView, final String newsImageUrl) {
        this.imageView = imageView;
        imageUrl = newsImageUrl;
        new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = getBitmapFromURL(newsImageUrl);
                Message message = Message.obtain();
                message.obj = bitmap;
                handler.sendMessage(message);
            }
        }.start();
    }

    public Bitmap getBitmapFromURL(String newsImageUrl) {
        Bitmap bitmap = getBitmapFromCache(newsImageUrl);
        /*如果内存中已有缓存的此图,直接返回此图*/
        if (bitmap != null) {
            return bitmap;
        }
        InputStream inputStream = null;
        try {
            URL url = new URL(newsImageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            inputStream = new BufferedInputStream(connection.getInputStream());
            bitmap = BitmapFactory.decodeStream(inputStream);
            connection.disconnect();
            if (bitmap != null) {
                addBitmapToCache(newsImageUrl, bitmap);
                return bitmap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public void addBitmapToCache(String imageUrl, Bitmap bitmap) {
        if (getBitmapFromCache(imageUrl) == null) {
            mCache.put(imageUrl, bitmap);
        }
    }

    public Bitmap getBitmapFromCache(String imageUrl) {
        return mCache.get(imageUrl);
    }
}

值得注意的是,我们之前在Adapter中已经对每个imageView设置了特定的Tag,在这里就用到了这个判断,保证不会因为imageView的复用出现图片多次加载的情况。至此整个demo就基本完成了。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值