ListView 异步加载并使用LruCache进行缓存
来源:互联网 发布:mac照片和文稿里的图片 编辑:程序博客网 时间:2024/06/09 20:12
这篇文章是根据慕课网上的ListView的视频讲解做的练习:
1.视频链接:http://www.imooc.com/video/7871
2.json数据获取地址:http://www.imooc.com/api/teacher?type=4&num=30
数据格式截图:
app效果展示:
一共有四个类,两个布局文件:
1.activity的布局文件:activity_main.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.admin.mylistviewdemo.MainActivity"> <ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="wrap_content" /></RelativeLayout>
2.具体列表项的布局:item_layout.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon" android:layout_width="64dp" android:layout_height="64dp" android:src="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical" android:paddingLeft="4dp"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="1" android:text="title" android:textSize="18sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="3" android:text="content" android:textSize="13sp" /> </LinearLayout></LinearLayout>
3.MainActivity.java文件
package com.example.admin.mylistviewdemo;import android.os.AsyncTask;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.widget.ListView;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.URL;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity { public ListView mListView; public List<NewsBean> mNewsBeanList; public static String JSONPATH = "http://www.imooc.com/api/teacher?type=4&num=30"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.lv_main); mNewsBeanList = new ArrayList<>(); new NewsAsyncTask().execute(JSONPATH); } //用于根据指定的Url异步地获取json数据 class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> { @Override protected void onPostExecute(List<NewsBean> newsBeen) { super.onPostExecute(newsBeen); NewsAdapter mNewsAdapter = new NewsAdapter(MainActivity.this,newsBeen,mListView); mListView.setAdapter(mNewsAdapter); } @Override protected List<NewsBean> doInBackground(String... params) { //这里传进来的参数就是我们需要解析的json数据的地址,因为就只有一个参数, // 因此取参数数组的第一个元素params[0] return getJsonData(params[0]); } } //从指定的流中读取json数据,将得到的json数据转化为String类型的字符串 public String readStream(InputStream is) { String result = "", line; BufferedReader bf = null; try { //将输入流读入字符缓冲数组中 bf = new BufferedReader(new InputStreamReader(is, "utf-8")); while ((line = bf.readLine()) != null) { result += line; } return result; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } //通过指定的Url来解析json数据,封装进NewsBean,并且装入mList public List<NewsBean> getJsonData(String url) { //用于将数据封装到该NewsBean中 NewsBean mNewsBean; try { String mJsonString = readStream(new URL(JSONPATH).openStream()); JSONObject mJSONObject = new JSONObject(mJsonString); JSONArray mJSONArray = mJSONObject.getJSONArray("data"); for (int i = 0; i < mJSONArray.length(); i++) { //获得json数组中的子json对象 mJSONObject = mJSONArray.getJSONObject(i); //将数据封装到该NewsBean中 mNewsBean = new NewsBean(); mNewsBean.newsTitle = mJSONObject.getString("name"); mNewsBean.newsContent = mJSONObject.getString("description"); mNewsBean.newsIconUrl = mJSONObject.getString("picSmall"); //添加该NewsBean到List中 mNewsBeanList.add(mNewsBean); } return mNewsBeanList; } catch (IOException e) { Log.d("qc", "网络连接故障"); System.out.println("网络连接故障"); e.printStackTrace(); } catch (JSONException e) { Log.d("qc", "json数据异常"); System.out.println("json数据异常"); e.printStackTrace(); } return null; }}
在该类中,我们主要通过在AsyncTackzhong 中异步地进行json数据的获取,主要在getJsonData(String url)方法中进行json数据的解析。
4.NewsBean.java为我们定义的用来存放数据的javabean
package com.example.admin.mylistviewdemo;/** * Created by admin on 2016/9/5. */public class NewsBean { public String newsIconUrl; public String newsTitle; public String newsContent;}
5.我们自定义了一个类:ImageLoader,来进行图片的加载
package com.example.admin.mylistviewdemo;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.util.Log;import android.util.LruCache;import android.widget.ImageView;import android.widget.ListView;import java.io.BufferedInputStream;import java.io.IOException;import java.net.HttpURLConnection;import java.net.URL;import java.util.HashSet;import java.util.Set;/** * Created by admin on 2016/9/5. */public class ImageLoader { //创建cache private LruCache<String, Bitmap> mLruCache; //将传入的ListView赋给mListView,从而可以直接对列表项进行设置,比如只加载可见的列表项,或者设置bitmap等 private ListView mListView; //创建set,用来存储我们的那些加载图片的线程,便于在需要的时候开启或者关闭相应的后台人任务 private Set<MyAsyncTask> mTasks; //构造方法,传入我们需要加载进图片的listview,并在该构造函数中初始化缓存大小 //我们通过传如ListView,从而可以在需要的时候对ListView直接进行设置 public ImageLoader(ListView listView) { mListView = listView; mTasks = new HashSet<>(); //获得系统当前最大可用内存,这里取最大缓存的1/4来作为此应用的缓存大小 int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 4; mLruCache = new LruCache<String, Bitmap>(cacheSize) { //覆写其sizeOf(String key, Bitmap value)方法, // 返回值就是我们每次存入数据所需缓存空间的大小 @Override protected int sizeOf(String key, Bitmap value) { //return super.sizeOf(key, value); //返回值为我们需要存入的bitmap的大小 return value.getByteCount(); } }; } //增加数据到缓存中 public void addBitmapToCache(String url, Bitmap bitmap) { //如果缓存中已经没有的话,就存入缓存,否则略过 if (getBitmapFromCache(url) == null) { mLruCache.put(url, bitmap); } } //从缓存中读取数据 public Bitmap getBitmapFromCache(String url) { return mLruCache.get(url); } public void cancelAllTasks() { for (MyAsyncTask task : mTasks) { //传入false表示只取消那些没有开始执行的线程,如果线程正在执行中则不取消,等待其执行完毕 task.cancel(false); } } //根据传入的图片的Url去获取对应的图片bitmap class MyAsyncTask extends AsyncTask<String, Void, Bitmap> { private String mUrl; // public MyAsyncTask(String url){// mUrl = url;// } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } mTasks.remove(this); } @Override protected Bitmap doInBackground(String... params) { mUrl = params[0]; Bitmap bitmap = getBitmapFromUrl(mUrl); if (bitmap != null) { addBitmapToCache(mUrl, bitmap); } return bitmap; } } public Bitmap getBitmapFromUrl(String url) { BufferedInputStream bufferedInputStream = null; try { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); bufferedInputStream = new BufferedInputStream(connection.getInputStream()); Bitmap bitmap = BitmapFactory.decodeStream(bufferedInputStream); //及时关闭数据连接 connection.disconnect(); return bitmap; } catch (IOException e) { e.printStackTrace(); } finally { try { bufferedInputStream.close(); } catch (IOException e) { Log.d("qc", "未能正常关闭流"); e.printStackTrace(); } } return null; } public void loadImages(int start, int end) { String url; Bitmap bitmap; MyAsyncTask task; ImageView imageView; for (int i = start; i < end; i++) { url = NewsAdapter.URLS[i]; bitmap = getBitmapFromCache(url); if (bitmap == null) { task = new MyAsyncTask(); task.execute(url); mTasks.add(task); } else { imageView = (ImageView) mListView.findViewWithTag(url); imageView.setImageBitmap(bitmap); } } }}
在该类中我们使用了缓存机制:LruCache,以下是LruCache的源码
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.camera.gallery;import java.lang.ref.ReferenceQueue;import java.lang.ref.WeakReference;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.Map;public class LruCache<K, V> { private final HashMap<K, V> mLruMap; private final HashMap<K, Entry<K, V>> mWeakMap = new HashMap<K, Entry<K, V>>(); private ReferenceQueue<V> mQueue = new ReferenceQueue<V>(); @SuppressWarnings("serial") public LruCache(final int capacity) { mLruMap = new LinkedHashMap<K, V>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; } }; } private static class Entry<K, V> extends WeakReference<V> { K mKey; public Entry(K key, V value, ReferenceQueue<V> queue) { super(value, queue); mKey = key; } } @SuppressWarnings("unchecked") private void cleanUpWeakMap() { Entry<K, V> entry = (Entry<K, V>) mQueue.poll(); while (entry != null) { mWeakMap.remove(entry.mKey); entry = (Entry<K, V>) mQueue.poll(); } } public synchronized V put(K key, V value) { cleanUpWeakMap(); mLruMap.put(key, value); Entry<K, V> entry = mWeakMap.put( key, new Entry<K, V>(key, value, mQueue)); return entry == null ? null : entry.get(); } public synchronized V get(K key) { cleanUpWeakMap(); V value = mLruMap.get(key); if (value != null) return value; Entry<K, V> entry = mWeakMap.get(key); return entry == null ? null : entry.get(); } public synchronized void clear() { mLruMap.clear(); mWeakMap.clear(); mQueue = new ReferenceQueue<V>(); }}
从源码中我们可以清楚的看到,LruCache的本质就是一个hashmap.在这里我们根据指定的tag来设置对应的bitmmap,这里bitmap和其tag(具体为图片的Url)关联了起来。
6.自定义一个adapter:NewsAdapter,来为listview提供数据
package com.example.admin.mylistviewdemo;import android.content.Context;import android.graphics.Bitmap;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import java.util.List;/** * Created by admin on 2016/9/5. */public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener { private List<NewsBean> mNewsBeanList; private Context mContext; private ListView mListView; private LayoutInflater mInflater; private ImageLoader mImageLoader; private boolean isJsutEnter; private int mStart, mEnd; public static String[] URLS; /** * 传入context对象,从而可以生成LayoutInflater来解析布局文件 * 根据传进来的mNewsBeanList来为listview提供数据 * 根据传进来的ListView,可以在需要的时候直接对列表项的数据进行修改,比如设置bitmap等 */ public NewsAdapter(Context context, List<NewsBean> mNewsBeanList, ListView listView) { mContext = context; mInflater = LayoutInflater.from(mContext); this.mNewsBeanList = mNewsBeanList; mListView = listView; mImageLoader = new ImageLoader(listView); isJsutEnter = true; URLS = new String[mNewsBeanList.size()]; for (int i = 0;i< URLS.length;i++){ URLS[i]= mNewsBeanList.get(i).newsIconUrl; } //为listView添加滚动监听器 mListView.setOnScrollListener(this); } //只有在滑动状态发生变化的时候才会回调该onScrollStateChanged方法,比如由滑动状态变为停止状态 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //当滑动停止的时候就加载可见项,否则不加载,停止所有执行加载任务的线程 if (scrollState == SCROLL_STATE_IDLE) { mImageLoader.loadImages(mStart, mEnd); } else{ mImageLoader.cancelAllTasks(); } } //每一次滚动都会回调该onScroll方法 @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mStart = firstVisibleItem; mEnd = firstVisibleItem + visibleItemCount; //预加载第一页,进行visibleItemCount > 0判断是因为当列表项还没有加载出来的时候, // 也就是当visibleItemCount为0的时候,onScroll方法就已经被回调了 if (isJsutEnter && visibleItemCount > 0) { mImageLoader.loadImages(mStart, mEnd); isJsutEnter = false; } } @Override public int getCount() { return mNewsBeanList.size(); } @Override public Object getItem(int position) { return mNewsBeanList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder ; if (convertView == null) { convertView = mInflater.inflate(R.layout.item_layout, null); viewHolder = new ViewHolder(); //从布局文件中取出对应控件的引用,并赋值给ViewHolder中的相应的控件 viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title); viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); viewHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon); //为convertView设置viewHolder标签 convertView.setTag(viewHolder); } else { //通过getTag()取出与convertView关联的viewHolder viewHolder = (ViewHolder) convertView.getTag(); } //设置viewHolder的tvTitle和tvContent,但是不能直接设置ivIcon, // 因为在我们需要开启线程去获取图片,这样在滑动的时候会出现图片错位的问题 //因此我们需要为ivIcon设置标签(这里我们将bitmap的url设置为它的标签,从而将ivIcon与其对应的图片地址关联起来) viewHolder.tvTitle.setText(mNewsBeanList.get(position).newsTitle); viewHolder.tvContent.setText(mNewsBeanList.get(position).newsContent); //为viewHolder的ivIcon设置tag //该设置实际上是对每个ivIcon设置唯一标签,就是该ivIcon只能显示由newsIconUrl唯一标识的bitmap String url = mNewsBeanList.get(position).newsIconUrl; viewHolder.ivIcon.setTag(url); //如果缓存中没有我们需要的bitmap,就显示默认图片,否则就将该bitmap设置为ivIcon显示的图片 //之所以可以这样做,是因为我们是从缓存中取bitmap,而不是从网络加载,不会差生错位。 //当到缓存中没有的时候,我们虽然加载了默认图片,看似不符合要求, // 但是我们可以在其他地方改成我们需要的正确的图片就好了(在滑动监听器中获取我们需要的bitmap并修改) Bitmap bitmap = mImageLoader.getBitmapFromCache(url); if (bitmap == null) { viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); } else { viewHolder.ivIcon.setImageBitmap(bitmap); } return convertView; } class ViewHolder { private ImageView ivIcon; private TextView tvTitle; private TextView tvContent; }}这里我们通过自定义一个ViewHolder,并将其设置为convertView的Tag,从而实现重用,并且之后通过为ImageView设置Tag来避免了出现图片的错位,通过实现 AbsListView.OnScrollListener接口,我们在需要覆写的方法中实现了ListView的预加载,并且子滚动过程中不进行图片的获取,只有当滚动停止时才进行可见列表项的设置。
PS:表示CSDN的编辑功能真的太难用了,用一会就什么也不想写了,图片插入大小还要手动设置,不可以直接拉伸。。。还是觉得博客园的好用点
0 0
- ListView 异步加载并使用LruCache进行缓存
- Android批量图片加载经典系列——使用LruCache、AsyncTask缓存并异步加载图片
- ListView异步加载 LruCache缓存 滑动状态监听
- 使用LruCache进行图片缓存
- 异步加载(AsyncTask异步任务、Handler、Json解析、Lrucache缓存、ListView滑动优化等来实现ListView图文混排)
- 异步加载图片,使用lruCache,,和sdcrad手机缓存 或手机缓存,,效果很很流程
- Android ListView 异步加载图片并缓存到本地
- android listview 异步加载图片并防止错位+双缓存
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 自己实现顺序表和单链表以及他们的对比
- 我所遇到的activiti问题
- HHUOJ 1012 欧洲杯(水题)
- UIImagePickerController详解
- 2016广联达面试题5
- ListView 异步加载并使用LruCache进行缓存
- 【LightOJ】1064 - Throwing Dice(dp打表)
- [leetcode][SQL]ALL
- Web初识之Servlet基础上
- spring-boot 实现通过访问路径后加参数返回json数据
- Service, AIDL
- android中的service总结和小例子
- 字符串排列组合
- iOS文章两篇