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



最新文章

  1. MySQL 5.7 create VIEW or FUNCTION or PROCEDURE
  2. 公众平台关注用户达到5万即可开通流量主功能 可以推广APP应用
  3. 发布ASP.NET Core程序到Linux生产环境
  4. 开源Jabber(XMPP) IM服务器介绍
  5. coroutine协程
  6. Spark运行各个时间段的解释
  7. hdu 1754 线段树模板题
  8. 如何判断手机收有几张SIM卡
  9. Linux对于录音
  10. 安卓---Tabhost实现页面局部刷新--父页子页之间的传值
  11. jq siblings()的强大之处
  12. file_zilla 通过key连接远程服务器
  13. A----奇怪的玩意
  14. SQL两列数据,行转列
  15. js数组方法大全
  16. 前端入门16-JavaScript进阶之EC和VO
  17. Resolving Issues of &quot;Library Cache Pin&quot; or &quot;Cursor Pin S wait on X&quot; (Doc ID 1476663.1)
  18. First Wainberg-2018-Deep learning in biomedicine Experience
  19. Eclipse 处理 Console 打印信息自动删除
  20. BZOJ 1965 洗牌(扩展欧几里得)

热门文章

  1. Android 单位dp和px之间相互转换
  2. [.net 多线程]ThreadPool
  3. Vue 动态传值,Get传值
  4. 如何修改git显示的用户名
  5. 1. Shell编程第一讲
  6. 关于logging的那些坑
  7. 理解Javascript_02_执行上下文01
  8. 数据库,asp总结思维导图图片
  9. 蠕虫Worm virus
  10. JAVA数据结构--LinkedList双向链表