ListView异步加载图片,完美实现图文混排
昨天参加一个面试,面试官让当场写一个类似于新闻列表的页面,文本数据和图片都从网络上获取,想起我还没写过ListView异步加载图片并实现图文混排效果的文章,so,今天就来写一下,介绍一下经验。
ListView加载文本数据都是很简单的,即使是异步获取文本数据。但是异步加载图片就稍微有一点麻烦,既要获得一个比较好的用户体验,还要防止出现图片错位等各种不良BUG,其实要考虑的东西还是挺多的。好了,我们先来看一下我们今天要实现的一个效果图:
看起来似乎并不难,确实,我们今天的核心问题只有一个,就是怎么异步加载图片,并且没有违和感。
好了,废话不多说,先来看主布局文件:
<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"
tools:context="com.example.listview.MainActivity" > <ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView> </RelativeLayout>
主布局中就一个listview,看看listview的item布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp" > <ImageView
android:id="@+id/iv"
android:layout_width="80dp"
android:layout_height="90dp"
android:layout_centerVertical="true"
android:padding="5dp"
android:src="@drawable/ic_launcher" /> <TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_toRightOf="@id/iv"
android:gravity="center_vertical"
android:text="人社部:养老转移已有初稿"
android:textSize="14sp"
android:textStyle="bold" /> <TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/iv"
android:text="人社部:养老转移已有初稿"
android:textSize="12sp" /> </RelativeLayout>
这个布局和我们上图描述的一样,左边一个ImageView,右边是两个TextView,这些都不难,我们看看MainActivity:
public class MainActivity extends Activity { private ListView lv;
private List<News> list;
private String HTTPURL = "http://litchiapi.jstv.com/api/GetFeeds?column=3&PageSize=20&pageIndex=1&val=100511D3BE5301280E0992C73A9DEC41";
private Handler mHandler = new Handler(){ @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
MyAdaper adapter = new MyAdaper(list);
lv.setAdapter(adapter);
break; default:
break;
}
} }; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) this.findViewById(R.id.lv);
initData();
} private void initData() {
list = new ArrayList<News>(); OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(HTTPURL).build();
Call call = client.newCall(request);
call.enqueue(new Callback() { @Override
public void onResponse(Response response) throws IOException {
try {
JSONObject jo1 = new JSONObject(response.body().string());
JSONObject jo2 = jo1.getJSONObject("paramz");
JSONArray ja = jo2.getJSONArray("feeds");
News news = null;
for (int i = 0; i < ja.length(); i++) {
JSONObject data = ja.getJSONObject(i).getJSONObject(
"data");
String imageUrl = "http://litchiapi.jstv.com"
+ data.getString("cover");
String title = data.getString("subject");
String summary = data.getString("summary");
news = new News(imageUrl, title, summary);
list.add(news);
}
} catch (JSONException e) {
e.printStackTrace();
}
mHandler.obtainMessage(0).sendToTarget();
} @Override
public void onFailure(Request arg0, IOException arg1) { }
});
}
}
在onCreate方法中,我们先拿到一个ListView的实例,然后就是初始化数据,这里初始化数据我们使用了OKHttp,关于OKHttp的使用可以查看我之前的文章OKHttp的简单使用,我们拿到一串json数据,至于json里边的结构是怎么样的,我就不多说了,大家可以直接在浏览器中打开上面的地址,这样就能看到json数据了,我们把我们需要的数据封装成一个JavaBean,其中ImageView我们先存储一个url地址,然后在Adapter中通过这个url地址异步加载图片。json解析就不多说了,我们瞅一眼这个Bean:
public class News { private String imageUrl;
private String title;
private String summary; public String getImageUrl() {
return imageUrl;
} public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getSummary() {
return summary;
} public void setSummary(String summary) {
this.summary = summary;
} public News(String imageUrl, String title, String summary) {
this.imageUrl = imageUrl;
this.title = title;
this.summary = summary;
} public News() {
}
}
好了,到这里,所有的东西都还是基本用法,下面我们先不急着看Adapter,先来看看Google给我们提供的一个缓存机制,在android-support-v4.jar包中,Google提供了这样一个类LruCache,这个LruCache的使用和Java中的Map用法差不多,甚至你就可以把它当作Map来使用,不同的是LruCache中的Value可以是一张图片。如果我们缓存的图片太多,超出了我们设置的缓存大小,那么系统会自动移除我们在最近使用比较少的图片。好了,我们来看看LruCache的定义:
private LruCache<String, BitmapDrawable> mImageCache;
每个图片的缓存的key我们就使用该图片的url(这个是唯一的),value就是一张我们要缓存的图片,在实例化LruCache的时候,我们需要传入一个参数,表明我们可以使用的最大缓存,这个缓存参数我们传入可用缓存的1/8,同时我们需要重写sizeOf方法,查看源码我们可以知道,如果不重写sizeOf方法,它默认返回的是图片的数量,但是我们实际上是需要计算图片大小来判断当前已经使用的缓存是否已经超出界限,所以我们这里重写sizeOf方法,返回每张图片的大小。代码如下:
int maxCache = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxCache / 8;
mImageCache = new LruCache<String, BitmapDrawable>(cacheSize) {
@Override
protected int sizeOf(String key, BitmapDrawable value) {
return value.getBitmap().getByteCount();
}
};
从LruCache中读取一张图片的方式和从Map中取值是一样的:
mImageCache.get(key)
向LruCache中存储一张图片:
mImageCache.put(key, bitmapDrawable);
关于LruCache的基本用法就说这些,这已经够我们后面使用了,现在我就大概说说我们的一个思路,当我们要给ImageView设置图片的时候,就先在本地缓存中查看是否有该图片,有的话,直接从本地读取,没有的话就从网络请求,同时,在从网络请求图片的时候,为了防止发生图片错位的情况,我们要给每一个item的每一个ImageView设置一个tag,这个tag就使用该ImageView要加载的图片的url(这样就可以确保每一个ImageView唯一),在给ImageView设置图片的时候我们就可以通过这个tag找到我们需要的ImageView,这样可以有效避免图片错位的问题。好了,看代码:
public class MyAdaper extends BaseAdapter { private List<News> list;
private ListView listview;
private LruCache<String, BitmapDrawable> mImageCache; public MyAdaper(List<News> list) {
super();
this.list = list; int maxCache = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxCache / 8;
mImageCache = new LruCache<String, BitmapDrawable>(cacheSize) {
@Override
protected int sizeOf(String key, BitmapDrawable value) {
return value.getBitmap().getByteCount();
}
}; } @Override
public int getCount() {
return list.size();
} @Override
public Object getItem(int position) {
return list.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (listview == null) {
listview = (ListView) parent;
}
ViewHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.listview_item, null);
holder = new ViewHolder();
holder.iv = (ImageView) convertView.findViewById(R.id.iv);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.summary = (TextView) convertView.findViewById(R.id.summary);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
News news = list.get(position);
holder.title.setText(news.getTitle());
holder.summary.setText(news.getSummary());
holder.iv.setTag(news.getImageUrl());
// 如果本地已有缓存,就从本地读取,否则从网络请求数据
if (mImageCache.get(news.getImageUrl()) != null) {
holder.iv.setImageDrawable(mImageCache.get(news.getImageUrl()));
} else {
ImageTask it = new ImageTask();
it.execute(news.getImageUrl());
}
return convertView;
} class ViewHolder {
ImageView iv;
TextView title, summary;
} class ImageTask extends AsyncTask<String, Void, BitmapDrawable> { private String imageUrl; @Override
protected BitmapDrawable doInBackground(String... params) {
imageUrl = params[0];
Bitmap bitmap = downloadImage();
BitmapDrawable db = new BitmapDrawable(listview.getResources(),
bitmap);
// 如果本地还没缓存该图片,就缓存
if (mImageCache.get(imageUrl) == null) {
mImageCache.put(imageUrl, db);
}
return db;
} @Override
protected void onPostExecute(BitmapDrawable result) {
// 通过Tag找到我们需要的ImageView,如果该ImageView所在的item已被移出页面,就会直接返回null
ImageView iv = (ImageView) listview.findViewWithTag(imageUrl);
if (iv != null && result != null) {
iv.setImageDrawable(result);
}
} /**
* 根据url从网络上下载图片
*
* @return
*/
private Bitmap downloadImage() {
HttpURLConnection con = null;
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(10 * 1000);
bitmap = BitmapFactory.decodeStream(con.getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
} return bitmap;
} } }
好了,listview图文混排就说到这里,有问题欢迎留言讨论。
Demo下载https://github.com/lenve/listview_PicText
ListView异步加载图片,完美实现图文混排的更多相关文章
- android listview 异步加载图片并防止错位
网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertVie ...
- Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案
我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如L ...
- Android 实现ListView异步加载图片
ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; ...
- 又优化了一下 Android ListView 异步加载图片
写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看 ...
- Android的ListView异步加载图片时,错位、重复、闪烁问题的分析及解决方法
Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图 ...
- listview异步加载图片并防止错位
android listview 异步加载图片并防止错位 网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 conver ...
- ListView异步加载图片
ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; ...
- Listview 异步加载图片之优化篇(有图有码有解释)
在APP应用中,listview的异步加载图片方式能够带来很好的用户体验,同时也是考量程序性能的一个重要指标.关于listview的异步加载,网上其实很多示例了,中心思想都差不多,不过很多版本或是有b ...
- Android之ListView异步加载图片且仅显示可见子项中的图片
折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整 ...
随机推荐
- 如何判断retina,如何判断设备的比例
http://www.189works.com/article-95647-1.html 说起iPhone 4带来的革新,retina display绝对是最吸引眼球的一项,以至于我现在看电脑的显示屏 ...
- lua练手基础
lua的库文件地址: http://luaforge.net/projects/lua官网 http://lua.org --[[ print string. multiple line commen ...
- python 内置函数 : compile()
这个函数用来编译一段字符串的源码,结果可以生成字节码或者AST(抽像语法树),字节码可以使用函数exec()来执行,而AST可以使用eval()来继续编译. 参数source是一串字符串的源码,或者是 ...
- /etc/bashrc和/etc/profile傻傻分不清楚?
导读 在一般的 linux 或者 unix 系统中, 都可以通过编辑 bashrc 和 profile来设置用户的工作环境, 很多文章对于 profile 和 bashrc 也都有使用, 但究竟每个文 ...
- python __file__ 与相对路径
用__file__ 来获得脚本所在的路径是比较方便的,但这可能得到的是一个相对路径,比如在脚本test.py中写入: #!/usr/bin/env pythonprint __file__ 按相对路径 ...
- 我要阻止做java开发的男朋友去创业型公司工作吗?
命这样的标题,我没有瞧不起创业型公司,我只是有点急了,因为男朋友今天晚上就要回复招聘公司了.我先来说说来由吧. 前段时间男朋友离职了,从毕业到现在在公司呆了2年多,因为资金不足导致他做的项目被迫停止了 ...
- BZOJ 4665: 小w的喜糖
Sol DP+容斥. 这就是一个错排的扩展...可是想到容斥却仅限于种数的容斥,如果种数在一定范围内我就会做了QAQ. 但是容斥的是一定在原来位置的个数. 发现他与原来的位置无关,可以先把每个同种的糖 ...
- Linux 查看磁盘空间大小
(1)查看文件大小 1. 查看当前文件夹下所有文件大小(包括子文件夹) du -sh 2.查看var目录下文件大小 du -sh var 3.查看指定文件夹下所有文件大小(包括子文件 ...
- PHP--获取响应头(Response Header)方法
方法一: $baiduUrl = "http://www.baidu.com/link"; file_get_contents($baiduUrl); $responseInf ...
- python gui之tkinter事件处理
事件一览表 事件 代码 备注 鼠标左键单击按下 1/Button-1/ButtonPress-1 鼠标左键单击松开 ButtonRelease-1 鼠标右键单击 3 鼠标左键双击 Doub ...