今天写存货了 调试一些动画参数花了些时间 ,嘿嘿存货不多了就没法做教程了,今天来教大家优化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

工作原理:

 1.ListView针对List中每个item,要求adapter给我一个视图(getView)

 2.一个新的视图被返回并显示

 

如果我们有上亿个item要显示怎么办?为每个项目创建一个新视图?NO!这不可能~~~Android实际上为你缓存了视图

 

Android中有个叫做Recycler(反复循环器)的构件,下图是它的工作原理:


1.如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中

2.ListView先请求一个type1视图(getView),然后请求其他可见的项目。conVertView在getView中时null的

3.当item1滚出屏幕,并且一个新的项目从屏幕地段上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1.你只需要设定新的数据返回convertView,不必重新创建一个视图。这样直接使用convertView从而减少了很不不必要view的创建

 

 

!!!!!!更快的方式是定义一个ViewHolder,将convertView的tag设置为ViewHolder,不为空是重新使用

 

ViewHolder只是将需要缓存的那些view封装好,convertView的setTag才是将这些缓存起来供下次调用

当你的listview里布局多样化的时候 viewholder的作用就有比较明显的体现了。 当然了,单一模式的布局一样有性能优化的作用 只是不直观。  假如你2种模式的布局 当发生回收的时候 你会用setTag分别记录是哪两种   这两种模式会被封装到viewholder中进行保存方便你下次使用。 VH就是个静态类 与缓存无关的 ,下面看我们客户端代码的listview
 
 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的更多相关文章

  1. Ace教你一步一步做Android新闻客户端(一)

    复制粘贴了那么多博文很不好意思没点自己原创的也说不出去,现在写一篇一步一步教你做安卓新闻客户端,借此机会也是让自己把相关的技术再复习一遍,大神莫笑,专门做给新手看. 手里存了两篇,一个包括软件视图 和 ...

  2. Ace教你一步一步做Android新闻客户端(四) 优化Bitmap大法

    我计划着把需要用到的知识分解开来写,趁着我们要开发这款客户端的机会把安卓所有移动客户端开发中的技术贯穿其中,也是我自己成长的过程.By Ace in 20160121 我们开发一款新闻客户端程序,它的 ...

  3. Ace教你一步一步做Android新闻客户端(三) JSON数据解析

    对于服务器端来说,返回给客户端的数据格式一般分为html.xml和json这三种格式,现在给大家讲解一下json这个知识点, 1 如何通过json-lib和gson这两个json解析库来对解析我们的j ...

  4. 阿冰教你一步一步做Android新闻客户端(二)两种异步线程加载图片的方法

    哈哈哈抱着没人看的心态随便写,直接上代码,各位看官看注释 一种Thread  一种AsyncTask 先不说用框架 public class ImageLoader { private ImageVi ...

  5. 用TableView做的新闻客户端展示页面

    用TableView做的新闻客户端展示页面 //  MyTableViewImageCell.m //  SildToDo // //  Created by WildCat on 13-8-18. ...

  6. IOS学习之路十四(用TableView做的新闻客户端展示页面)

    最近做的也个项目,要做一个IOS的新闻展示view(有图有文字,不用UIwebview,因为数据是用webservice解析的到的json数据),自己一直没有头绪,可后来听一个学长说可以用listvi ...

  7. 一步一步教你如何在linux下配置apache+tomcat(转)

    一步一步教你如何在linux下配置apache+tomcat   一.安装前准备. 1.   所有组件都安装到/usr/local/e789目录下 2.   解压缩命令:tar —vxzf 文件名(. ...

  8. 一步一步教你使用Git

    一步一步教你使用Git 互联网给我们带来方便的同时,也时常让我们感到困惑.随便搜搜就出一大堆结果,然而总是有大量的重复和错误.小妖发出的内容,都是自己实测过的,有问题请留言. 现在,你已经安装了Git ...

  9. 一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app

    一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app 转载 作者:jrainlau 链接:https://segmentfault.com/a/1190000005844155 ...

随机推荐

  1. Jquery hover 事件

    hover(over,out)一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法.这是一个自定义的方法,它为频繁使用的任务提供了一种“保持在其中”的状态. 当鼠标移动到一个匹配的元素上面时 ...

  2. C# DataGridView控件动态添加新行

    C# DataGridView控件动态添加新行 DataGridView控件在实际应用中非常实用,特别需要表格显示数据时.可以静态绑定数据源,这样就自动为DataGridView控件添加相应的行.假如 ...

  3. 在C#中使用科大讯飞Web API进行语音合成

    最近工作中需要用到讯飞语音合成接口,网上看了下基本都是Java,PHP,Python版本的,正好补上C# 版本,代码比较简单.  首先在讯飞开放平台上创建一个WebApi项目,取到APPID与APIK ...

  4. C#常见编译报错

    mCaster.PlayAnim(ANIMID.ASTD); No overload for method 'PlayAnim' takes '1' arguments PlayAnim()内有两个参 ...

  5. asp.net core 外部认证多站点模式实现

    PS:之前因为需要扩展了微信和QQ的认证,使得网站是可以使用QQ和微信直接登录.github 传送门 .然后有小伙伴问,能否让这个配置信息(appid, appsecret)按需改变,而不是在 Con ...

  6. Jquery 页面元素事件绑定

    场景: 用一个Table来展示数据信息列表,通过鼠标点击Table中的Tr来获取到当前选中的数据行信息. <table class="Table" width="1 ...

  7. 321. Create Maximum Number (c++ ——> lexicographical_compare)

    Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum numb ...

  8. winform发布桌面程序后提示需开启“目录浏览”

    把发布文件里的publish.htm名字改为index.htm就好了

  9. Python 简单说明与数据结构

    Python 简单说明与数据结构 Python 作为 "国内" 较流行的高级语言,具有代码容易理解.专注解决问题.混合编译其他语言的优点. 变量 变量是一个最基本的储存单位,它暂时 ...

  10. linux防火墙(二)—— iptables语法之选项和控制类型

    一.语法: iptables [-t 表名] 选项 [链名] [匹配条件] [-j 控制类型] 未指定表名时,默认用filter表:链名,控制类型要大写:除非设置默认策略,否则必须指定匹配条件:不指定 ...