Ace教你一步一步做Android新闻客户端(五) 优化Listview
今天写存货了 调试一些动画参数花了些时间 ,嘿嘿存货不多了就没法做教程了,今天来教大家优化listview,等下我把代码编辑下 这次代码有些多 所以我把条理给大家理清楚。思路就是把加载图片的权利交给OnScrollListener 。
1 首先来到 NewsAdapter这个类 ,我们给他实现了一个 AbsListView.OnScrollListener 这个接口,这个接口有两个方法:
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {//Listview状态改变完才执行这个方法(比如说滑动-----》停止滑动)
if (scrollState == SCROLL_STATE_IDLE){ //IDLE是定制flying是滑动
//滚动状态=停止 加载可见项
mImageLoader.loadImages(startX,endX);
}else{
//其他状态我们就需要停止任务 我们的异步线程集合mTask就起到作用了
mImageLoader.cancelAllTask(); //给我们的ImageLoader创建一个方法来停止所有的异步加载任务 } } @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {//listview滑动过程中一直执行,传进来的第一个参数是listview,第二个是起始位置,第三个是可见项的数量,第四个是可见元素的总数
startX = firstVisibleItem;
endX = firstVisibleItem + visibleItemCount; }
这一步我们把加载图片的控制权从adapter的getview方法 挪到了我们的滑动状态监听器 AbsListView.OnScrollListener 上 只有在滚动完毕后我们才加载 大大节省了内存 和不必要消耗的流量,提升了listview的
流畅度 哈哈哈哈哈哈 这样它就可以流畅的滚了
2 视角转入ImageLoader , 我们创建一个方法 loadImages 用来加载start -------end 的图片
public void loadImages (int start ,int end ){ //通过这个循环我们拿到对应的循环和URL
for (int i =start ; i < end ; i++){
String url = NewsAdapter.URLS[i];
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null){
MyIconSyncTask task = new MyIconSyncTask(url);//创建 myIconSyncTask对象
task.execute(url);
mTask.add(task);//加入到我们创建的mTask集合
}else{
ImageView imageView = (ImageView)mListView.findViewWithTag(url);//找到url对应的ListView
imageView.setImageBitmap(bitmap);
}
}
}
3 再创建一个取消所有线程的方法
public void cancelAllTask(){
if (mTask != null) {
for (MyIconSyncTask task : mTask) { //遍历mTask中的任务, 并执行cancer方法取消掉
task.cancel(false);
}
}
}
4 修改 ImageLoader方法的参数 ,因为我现在加载的是 start ------- end 的整体 所以只传入Imageview单个条目的控件就不太合适了,我们需要加载一整块ListView 所以我们先要通过
ImageView imageView = (ImageView)mListView.findViewWithTag(mUrl);//找到url对应的ListView
然后
imageView.setImageBitmap(bitmap);
5 最后不要忘记给listview设置监听哟~~~~~~~Ace友情提醒 不行了 太困了 要睡了 把整体代码提交给大家! github做好整体代码会放给大家~
MainActivity
package asynctask.zb.com.asynctask_02; import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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 {
//初始化
String TAG = "zbace";//日志TAG
private ListView listView;
private String URL =" http://www.imooc.com/api/teacher?type=4&num=30";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listview);
new NewsAsyncTask().execute(URL);
}
/** Ace in 2016/1/20
* 创建getJsonData(传入URL地址), 把从流中读取的JSON数据封装进NewsBean中放入List集合
* 1 调用readString方法获取到jason格式的字符串, openStream与url.openConnection().geTinpuStream() 一样;
* 获取到jsonString Log.d(TAG, jsonString);打印下是否可以获取到JSON数据
* 2 然后创建JSONObject对象,传入jsonString。
* 3 getJSONArray("data")方法 从中取出JSONArray,
* 在创建个for循环遍历JSONArray并取出newsicon,title,content,等信息
* 最后把信息放入NewsBean,再添加进数组
*
*/ private List<NewsBean> getJsonData(String url){
List<NewsBean> nesBeanList = new ArrayList<>(); try {
String jsonString = readStream(new URL(url).openStream());
Log.d(TAG, jsonString);
JSONObject jsonObject;
NewsBean newsBean;
try { jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");
for (int i = 0 ; i <jsonArray.length(); i++ ){
//每个JSONArray 的元素都是一个JSONObject
jsonObject = jsonArray.getJSONObject(i);
//把得到的jsonObject, 放入NewsBean
newsBean = new NewsBean();
newsBean.newsIconUrl = jsonObject.getString("picSmall");
newsBean.newsTitle = jsonObject.getString("name");
newsBean.newsContent = jsonObject.getString("description");
nesBeanList.add(newsBean);
} } catch (JSONException e) {
e.printStackTrace();
} } catch (IOException e) {
e.printStackTrace();
} return nesBeanList; //记得返回list
} /** Ace in 2016/1/20
* readStream方法是为了读取流中的数据从而获得流里的JSONString
*
* */ private String readStream(InputStream is){
InputStreamReader isr;
String result = "";
try {
String line = "";
//用把字节流转换为字符流(不转字符流无法显示中文),并设置编码为UTF-8;
isr = new InputStreamReader(is,"utf-8");
//套上缓冲流
BufferedReader br = new BufferedReader(isr);
//创建一个while循环
while ((line=br.readLine()) != null ){
result += line;//这就得到了我们需要的JSON字符串,从JSON字符串中就可以得到我们想要数据
} } catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} return result;
}
/** Ace in 2016/1/20
* 异步获取JSON数据
*
* */ class NewsAsyncTask extends AsyncTask<String,Void,List<NewsBean>>{
@Override
protected List<NewsBean> doInBackground(String... params) {
return getJsonData(params[0]);//params就是我们传进来的String URL 网址 只传进来了一个 就输入[0]
} @Override
protected void onPostExecute(List<NewsBean>newsBeanList) {
super.onPostExecute(newsBeanList);
NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this ,newsBeanList,listView);
listView.setAdapter(newsAdapter); }
} }
NewsAdapter:
package asynctask.zb.com.asynctask_02; import android.content.Context;
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.net.URL;
import java.util.List; /**
* Created by Ace on 2016/1/20.
*/
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
private List<NewsBean> mlist;
private LayoutInflater mInflater;
private ImageLoader mImageLoader; private int startX;
private int endX;
public static String URLS[];//创建一个变量 并把权限设置成public public NewsAdapter(Context context,List<NewsBean>data,ListView listView){
//映射下 把data传给mlist mlist = data;
//从一个上下文中(这里的上下文是MainActivity),获得一个布局填充器,这样你就可以使用这个填充器的inflater.inflate()来把xml布局文件转为View对象了,然后利用view对象,findViewById就可以找到布局中的组件
mInflater = LayoutInflater.from(context);
mImageLoader = new ImageLoader(listView); //在适配器初始化ImageLoader
URLS = new String[data.size()];//初始化URLS数组 把data里面的icon的url信息放到里面来,方便取用
for (int i = 0 ; i <data.size(); i++){
URLS[i] = data.get(i).newsIconUrl;
}
listView.setOnScrollListener(this);
}
@Override
public Object getItem(int position) {
return mlist.get(position);
} @Override
public int getCount() { return mlist.size();
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder= null;
if (convertView == null){
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.adapter_item,null);
viewHolder.iconimage = (ImageView)convertView.findViewById(R.id.tvimage);
viewHolder.title = (TextView)convertView.findViewById(R.id.tvtitle);
viewHolder.content = (TextView)convertView.findViewById(R.id.tvcontent);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder)convertView.getTag();
viewHolder.iconimage.setImageResource(R.mipmap.ic_launcher);
String url = mlist.get(position).newsIconUrl;
viewHolder.iconimage.setTag(url);//给imageview设置标签 是为了增加一个判断的标准(再imageloader类里),只有URL地址和当前位置的Item的图片相匹配才显示
// new ImageLoader().showImageByThread(viewHolder.iconimage, mlist.get(position).newsIconUrl);
mImageLoader.showImageByAsyncTask(viewHolder.iconimage, mlist.get(position).newsIconUrl);//不能使用 new ImageLoader(). 因为每new一次都创建一个ImageLoader()这样就会有很多的lru
viewHolder.title.setText(mlist.get(position).newsTitle);
viewHolder.content.setText(mlist.get(position).newsContent);
}
return convertView;
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {//Listview状态改变完才执行这个方法(比如说滑动-----》停止滑动)
if (scrollState == SCROLL_STATE_IDLE){ //IDLE是定制flying是滑动
//滚动状态=停止 加载可见项
mImageLoader.loadImages(startX,endX);
}else{
//其他状态我们就需要停止任务 我们的异步线程集合mTask就起到作用了
mImageLoader.cancelAllTask(); //给我们的ImageLoader创建一个方法来停止所有的异步加载任务 } } @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {//listview滑动过程中一直执行,传进来的第一个参数是listview,第二个是起始位置,第三个是可见项的数量,第四个是可见元素的总数
startX = firstVisibleItem;
endX = firstVisibleItem + visibleItemCount; } class ViewHolder{
public TextView title;
public ImageView iconimage;
public TextView content; }
}
ImageLoader
package asynctask.zb.com.asynctask_02; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Message;
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.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set; /**
* Created by Administrator on 2016/1/20.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
public LruCache<String,Bitmap> mCache;
private ListView mListView;//因为我们要加载start - end 的所有图片 是个整体而不是单个条目,所以创建一个listview 通过listview的findViewWithTag方法来找到对应的ImageView
private Set<MyIconSyncTask> mTask;//创建一个Set 集合 用于装我们所有的AsyncTask public ImageLoader (ListView listView){//给构造方法传入listview 然后初始化
mListView = listView;//初始化listview
mTask = new HashSet<>();//初始化mTask int maxMemory = (int) Runtime.getRuntime().maxMemory(); //获取虚拟机可用内存(内存占用超过该值的时候,将报OOM异常导致程序崩溃)
int cacheSzie = maxMemory/4; //使用可用内存的1/4来作为Memory Cache
mCache = new LruCache<String,Bitmap>(cacheSzie) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount(); //返回Bitmap占用的空间,告诉系统我这张图片要用多少内存
}
};
}
//把bitmap加入到缓存
public void addBitmapToCache(String mUrl, Bitmap bitmap) {
if (getBitmapFromCache(mUrl) == null) {
mCache.put(mUrl, bitmap);
}
}
//从缓存中获取数据 public Bitmap getBitmapFromCache(String mUrl) {
return mCache.get(mUrl);
} android.os.Handler mHandler = new android.os.Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mImageView.getTag().equals(mUrl))
mImageView.setImageBitmap((Bitmap)msg.obj);
}
}; public void showImageByThread(ImageView imageView, final String url) {
mImageView =imageView;
mUrl = url;//对传过来的imageView 和url进行缓存(为了避免程序逻辑顺序错误,和viewholder的机制差不多)
new Thread() {
@Override
public void run() {
super.run();
Bitmap bitmap = getBitmapFromURL(url);
Message message = Message.obtain();
message.obj = bitmap;
mHandler.sendMessage(message);
}
}.start(); }
public void cancelAllTask(){
if (mTask != null) {
for (MyIconSyncTask task : mTask) { //遍历mTask中的任务, 并执行cancer方法取消掉
task.cancel(false);
}
}
}
// 用来加载start -------end 的图片
public void loadImages (int start ,int end ){ //通过这个循环我们拿到对应的循环和URL
for (int i =start ; i < end ; i++){
String url = NewsAdapter.URLS[i];
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null){
MyIconSyncTask task = new MyIconSyncTask(url);//创建 myIconSyncTask对象
task.execute(url);
mTask.add(task);//加入到我们创建的mTask集合
}else{
ImageView imageView = (ImageView)mListView.findViewWithTag(url);//找到url对应的ListView
imageView.setImageBitmap(bitmap);
}
}
} //创建从URL获取Bitmap的放方法
public Bitmap getBitmapFromURL(String stringUrl) { Bitmap bitmap;
BufferedInputStream bis = null; URL url1 = null;
try {
url1 = new URL(stringUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
} HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url1.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
try {
bis = new BufferedInputStream(connection.getInputStream()); bitmap = BitmapFactory.decodeStream(bis);
connection.disconnect();
return bitmap;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
} }return null;
} public void showImageByAsyncTask (ImageView imageView, String url){
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null){
imageView.setImageResource(R.mipmap.ic_launcher);//在bitmap=空的时候我们就让它显示默认图片,这样修改后把加载图片的控制权全部放入新建的loadImages方法中了
}else{
imageView.setImageBitmap(bitmap);
}
} class MyIconSyncTask extends AsyncTask<String,Void,Bitmap> {
// private ImageView mImageView;
private String mUrl;
public MyIconSyncTask(String url){
mUrl = url;
// mImageView = imageView;
}
@Override
protected Bitmap doInBackground(String... params) {
String mUrl = params[0];
//从网络获取图片,并存入缓存中
Bitmap bitmap = getBitmapFromURL(mUrl);
if (bitmap != null){
addBitmapToCache(mUrl,bitmap);
}
return bitmap;
} @Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
// if (mImageView.getTag().equals(mUrl)) {
// mImageView.setImageBitmap(bitmap);
ImageView imageView = (ImageView)mListView.findViewWithTag(mUrl);//找到url对应的ListView
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
mTask.remove(this);//加载位图完毕移除这个异步线程
}
}
}
NewsBean
package asynctask.zb.com.asynctask_02; import java.net.URL; /**
* Created by Administrator on 2016/1/20.
*/
public class NewsBean {
public String newsIconUrl;
public String newsTitle;
public String newsContent;
}
还有一个优化就是ViewHolder 和 concertView ,这张图我觉得非常棒 LsitView和Adapter

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder= null;
if (convertView == null){
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.adapter_item,null);
viewHolder.iconimage = (ImageView)convertView.findViewById(R.id.tvimage);
viewHolder.title = (TextView)convertView.findViewById(R.id.tvtitle);
viewHolder.content = (TextView)convertView.findViewById(R.id.tvcontent);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder)convertView.getTag();
viewHolder.iconimage.setImageResource(R.mipmap.ic_launcher);
String url = mlist.get(position).newsIconUrl;
viewHolder.iconimage.setTag(url);//给imageview设置标签 是为了增加一个判断的标准(再imageloader类里),只有URL地址和当前位置的Item的图片相匹配才显示
// new ImageLoader().showImageByThread(viewHolder.iconimage, mlist.get(position).newsIconUrl);
mImageLoader.showImageByAsyncTask(viewHolder.iconimage, mlist.get(position).newsIconUrl);//不能使用 new ImageLoader(). 因为每new一次都创建一个ImageLoader()这样就会有很多的lru
viewHolder.title.setText(mlist.get(position).newsTitle);
viewHolder.content.setText(mlist.get(position).newsContent);
}
return convertView;
}
class ViewHolder{
public TextView title;
public ImageView iconimage;
public TextView content; }
}
Ace教你一步一步做Android新闻客户端(五) 优化Listview的更多相关文章
- Ace教你一步一步做Android新闻客户端(一)
复制粘贴了那么多博文很不好意思没点自己原创的也说不出去,现在写一篇一步一步教你做安卓新闻客户端,借此机会也是让自己把相关的技术再复习一遍,大神莫笑,专门做给新手看. 手里存了两篇,一个包括软件视图 和 ...
- Ace教你一步一步做Android新闻客户端(四) 优化Bitmap大法
我计划着把需要用到的知识分解开来写,趁着我们要开发这款客户端的机会把安卓所有移动客户端开发中的技术贯穿其中,也是我自己成长的过程.By Ace in 20160121 我们开发一款新闻客户端程序,它的 ...
- Ace教你一步一步做Android新闻客户端(三) JSON数据解析
对于服务器端来说,返回给客户端的数据格式一般分为html.xml和json这三种格式,现在给大家讲解一下json这个知识点, 1 如何通过json-lib和gson这两个json解析库来对解析我们的j ...
- 阿冰教你一步一步做Android新闻客户端(二)两种异步线程加载图片的方法
哈哈哈抱着没人看的心态随便写,直接上代码,各位看官看注释 一种Thread 一种AsyncTask 先不说用框架 public class ImageLoader { private ImageVi ...
- 用TableView做的新闻客户端展示页面
用TableView做的新闻客户端展示页面 // MyTableViewImageCell.m // SildToDo // // Created by WildCat on 13-8-18. ...
- IOS学习之路十四(用TableView做的新闻客户端展示页面)
最近做的也个项目,要做一个IOS的新闻展示view(有图有文字,不用UIwebview,因为数据是用webservice解析的到的json数据),自己一直没有头绪,可后来听一个学长说可以用listvi ...
- 一步一步教你如何在linux下配置apache+tomcat(转)
一步一步教你如何在linux下配置apache+tomcat 一.安装前准备. 1. 所有组件都安装到/usr/local/e789目录下 2. 解压缩命令:tar —vxzf 文件名(. ...
- 一步一步教你使用Git
一步一步教你使用Git 互联网给我们带来方便的同时,也时常让我们感到困惑.随便搜搜就出一大堆结果,然而总是有大量的重复和错误.小妖发出的内容,都是自己实测过的,有问题请留言. 现在,你已经安装了Git ...
- 一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app
一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app 转载 作者:jrainlau 链接:https://segmentfault.com/a/1190000005844155 ...
随机推荐
- ORCHARD学习教程-安装
安装说明:测试对象为正式版1.8 安装方法: 使用Microsoft Web Platform Installer 利用Microsoft WebMatrix 来安装 Working with Orc ...
- delphi创建服务程序
创建一个自己的服务程序: File -> New -> Other -> New(选项) -> Service Application 这样delphi会自动生成服务程序的框架 ...
- centos 重新安装python3.6之后 yum 无法使用报错
问题: $ yum File "/usr/bin/yum", line 30 except KeyboardInterrupt, e: ^ SyntaxError: invalid ...
- 指针和动态分配内存 (不定长度数组)------新标准c++程序设计
背景: 数组的长度是定义好的,在整个程序中固定不变.c++不允许定义元素个数不确定的数组.例如: int n; int a[n]; //这种定义是不允许的 但是在实际编程中,往往会出现要处理的数据数量 ...
- git Problem with the SSL CA cert (path? access rights?)
问题: [root@localhost opt]# git clone https://github.com/docker/docker.git 正克隆到 'docker'...fatal: unab ...
- 上课总结-数据结构c++
class 1 数据结构就是指 按一定的逻辑结构组成的一批数据,使用某种存储结构将这批数据存储于计算机中,并在这些数据上定义了一个运算集合. 例1.煤气管道的铺设问题.如图需为城市的各小区之间铺设煤气 ...
- 842. Split Array into Fibonacci Sequence
Given a string S of digits, such as S = "123456579", we can split it into a Fibonacci-like ...
- Spring Boot多数据源
我们在开发过程中可能需要用到多个数据源,我们有一个项目(MySQL)就是和别的项目(SQL Server)混合使用了.其中SQL Server是别的公司开发的,有些基本数据需要从他们平台进行调取,那么 ...
- shell传参给matlab问题解决办法
之前需要通过shell脚本传参给matlab程序,但是遇到一些问题,现将我遇到的问题分享出来,给遇到同样问题的人一些借鉴. shell部分脚本Execl.sh: /usr/bin/python /ho ...
- kali linux之wireshark/tcpdump
抓包嗅探协议分析,必备技能,抓包引擎(linux---libpcap9 windows-----winpcap10) 解码能力优秀 常见协议包 数据包的分层i协议 arp icmp tcp--三次 ...