昨天参加一个面试,面试官让当场写一个类似于新闻列表的页面,文本数据和图片都从网络上获取,想起我还没写过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>
<?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>
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) { } }); } }
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() { } }
private LruCache<String, BitmapDrawable> mImageCache;
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(); } };
mImageCache.get(key)
mImageCache.put(key, bitmapDrawable);
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