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;
}


时间: 2024-10-29 19:10:21

Ace教你一步一步做Android新闻客户端(五) 优化Listview的相关文章

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

复制粘贴了那么多博文很不好意思没点自己原创的也说不出去,现在写一篇一步一步教你做安卓新闻客户端,借此机会也是让自己把相关的技术再复习一遍,大神莫笑,专门做给新手看. 手里存了两篇,一个包括软件视图 和新手引导 软件侧滑菜单 滑动主页的GUI篇 一个内容解析篇. 代码里有很详细的注释 所以直接放代码了 有不会的站内信或者评论我会及时回复. MainActivity XML :只有一个ListView布局 <?xml version="1.0" encoding="utf-

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

对于服务器端来说,返回给客户端的数据格式一般分为html.xml和json这三种格式,现在给大家讲解一下json这个知识点, 1 如何通过json-lib和gson这两个json解析库来对解析我们的json数据, 2 以及如何在我们的Android客户端解析来自服务器端的json数据,并更新到UI当中.(下篇来讲) 一.什么是json json(Javascript Object Notation)是一种轻量级的数据交换格式,相比于xml这种数据交换格式来说,因为解析xml比较的复杂,而且需要编

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

哈哈哈抱着没人看的心态随便写,直接上代码,各位看官看注释 一种Thread  一种AsyncTask 先不说用框架 public class ImageLoader { private ImageView mImageView; private String mUrl; //Thread明显很low 还需要handler来传递消息,好累T T android.os.Handler mHandler = new android.os.Handler(){ @Override public void

教你一步一步实现图标无缝变形切换

我的简书同步发布:教你一步一步实现图标无缝变形切换 ?欢迎打赏? 转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001] 本来这篇文章几天前就应该写好并发布出来的,由于要写小论文,被导师劈头盖脸的骂了几天,一直在搞论文,耽误了博文的编写.今天终于把小论文给投出去了,终于可以好好写博客了! 在上一篇文章<酷炫的Activity切换动画,打造更好的用户体验 >中,我们感受到了过渡切换动画带来的不一样的用户体验.如何你还意犹未尽,那么今天我

教你一步一步实现一个Promise

Promise我想现在大家都非常熟悉了,主要作用就是解决异步回调问题,这里简单介绍下. Promise规范是CommonJS规范之一,而Promise规范又分了好多种,比如 Promises/A.Promises/B.Promises/Kiss等等 有兴趣的可以到这多了解一些 http://wiki.commonjs.org/wiki/Promises 现在比较流行的是Promise/A规范,人们对它的完善和扩展,逐渐形成了Promise/A+规范,A+已脱颖而出. 说到这里规范是什么,可以去这

一步一步教你如何重装笔记本电脑系统?

本文标签:  电脑技巧 重装笔记本电脑系统 重装系统 重装dell联想宏碁电脑系统 原文地址:http://whosmall.com/?post=461 不知不觉中,已在程序猿这个职业中疯狂熬过去了3年时间,这3年虽然苹果技术天天更新,天天进步,但是如计算机常识方面却不忍心看它还是原地踏步! 从事编程时间久了,每次回家的时候,免不了会有朋友说起听说你从事计算机工作的吧,是啊,那帮我装个系统吧,最近电脑卡的要命.我家网线坏了,帮我连下网线吧!更有甚者,说我刚才误删某某重要文件,帮我恢复下吧! 你要

【Unity3D实战】零基础一步一步教你制作跑酷类游戏(填坑完整版)

在两个月前曾写了一篇<[Unity3D实战]零基础一步一步教你制作跑酷类游戏(1)>,里面一步一步演示了制作跑酷类游戏,然而由于时间原因,只写到了让角色往前移动为止.这个坑一直没有时间去填,(虽然也没多少人看啦),今天刚好有时间完成了一个跑酷类游戏的Demo.放上来给有兴趣的朋友看看. Demo源码及对应素材下载:链接:http://pan.baidu.com/s/1i4QkkuD 密码:p04w 游戏简要说明 游戏类型:跑酷类游戏(Demo,非完整游戏) 操作方式:左右方向键(可自己移植到手

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

一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app 转载 作者:jrainlau 链接:https://segmentfault.com/a/1190000005844155 项目地址:https://github.com/jrainlau/wechat-subscriptor 下载&运行 git clone git@github.com:jrainlau/wechat-subscriptor.git cd wechat-subscriptor && np

【好文翻译】一步一步教你使用Spire.Doc转换Word文档格式

背景: 本文试图证明和审查Spire.Doc的格式转换能力.很长的一段时间里,为了操作文档,开发人员不得不在服务器上安装Office软件.首先,这是一个很糟糕的设计和实践.第二,微软从没打算把Office作为一个服务器组件,它也用来在服务器端解释和操作文档的.于是乎,产生了类似Spire.Doc这样的类库.当我们讨论这个问题时,值得一提的是 Office Open Xml. Office Open XML (也有非正式地称呼为 OOXML 或OpenXML) 是一种压缩的, 基于XML的文件格式