ListView的网络加载(使用LRU缓存,滚动监听器)

Posted EdwardRu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView的网络加载(使用LRU缓存,滚动监听器)相关的知识,希望对你有一定的参考价值。

public class MainActivity extends AppCompatActivity {
    private List<Newsbeans> mlist;
    private static  String url="http://www.imooc.com/api/teacher?type=4&num=30";
    private ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Myaysctask myaysctask=new Myaysctask();
        //将url传递进去实现对于url的异步访问
        new Myaysctask().execute(url);
        listView= (ListView) findViewById(R.id.listview);
    }
    //将url转化为我们所需要的json对象
    private List<Newsbeans> getjsondata(String url){
        //初始化list
        mlist=new ArrayList<>();
        JSONObject jsonObject;
        Newsbeans newsbeans;
        try {
            //获取字节流
            String jsonstring= readStream(new URL(url).openStream());
            //将传入的字节流转化为json对象
            jsonObject=new JSONObject(jsonstring);
            //得到json下的data下的数组
            JSONArray jsonArray=jsonObject.getJSONArray("data");

            for(int i=0;i<jsonArray.length();i++){
                //遍历json数组的到jsonobject的值
                jsonObject=jsonArray.getJSONObject(i);
                newsbeans=new Newsbeans();
                //将jsonobject中的值赋予到已经封装好的值之中,通过getString方法获得
                newsbeans.newscontent=jsonObject.getString("description");
                newsbeans.newspic=jsonObject.getString("picSmall");
                newsbeans.newstitle=jsonObject.getString("name");
                //将newsbeans添加到list之中
                mlist.add(newsbeans);
            }
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        }
        return mlist;
    }
    private String readStream(InputStream is){
        String result="";
        String line="";
        try {
            //将inputstream传入的为字节流,通过inputstreamreader将字节流转换为字符流
            InputStreamReader irs=new InputStreamReader(is,"utf-8");
            //通过bufferedreader将输入流读取出来
            BufferedReader br= new BufferedReader(irs);
            while ((line= br.readLine())!=null){
                result+=line;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
    class Myaysctask extends AsyncTask<String,Void,List<Newsbeans>>{

        @Override
        //进行所有的耗时操作但不包括更改UI
        protected List<Newsbeans> doInBackground(String... strings) {
            //将string数组中的0位返回也就是传入url
            return getjsondata(strings[0]);
        }

        @Override
        protected void onPostExecute(List<Newsbeans> newsbeans) {
            Myadapter myadapter=new Myadapter(MainActivity.this,newsbeans,listView);
            listView.setAdapter(myadapter);
            super.onPostExecute(newsbeans);
        }
    }
}

在mainactivity中从网络中获取JSON数组,并转化为JSON对象,通过AsyncTask使用异步线程下载数据,并封装在已经设置好的空间中以及在子线程中设置UI,通过Myadapter对象传递参数用于在BaseAdapter中设置监听事件和设置ListView

public class ImageviewHolder {
         //设置全局变量
         private String mURL;
         private LruCache<String,Bitmap> mCache;
         private Set<MyAsynctask> mTask;
         private ListView mListView;


    public ImageviewHolder(ListView listview){
        //在构造方法中初始化LruCache
        mListView=listview;
        mTask=new HashSet<>();
        //获取当前系统的最大的运行内存
        int MaxMem= (int) Runtime.getRuntime().maxMemory();
        //设置当前的缓存的内存
        int CacheMem=MaxMem/2;
        mCache=new LruCache<String,Bitmap>(CacheMem){
            //重写sizeof方法,获取缓存图片的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }
    private void addBitMapToCache(String url,Bitmap bitmap){
        //自定义方法,在判断所需的图片没有在缓存中存在的时候,将其放入到缓存之中
        if(getBitMapFromCache(url)==null){
            mCache.put(url,bitmap);
        }

    }
    private Bitmap getBitMapFromCache(String url){
        return mCache.get(url);

    }
    public void showImageBy(ImageView imageView,String mURL)  {
        Bitmap bitmap= getBitMapFromCache(mURL);
        /*
        * 判断在缓存中的图片是否存在,如果不存在则通过网络下载图片,其实这个方法在设定onScrollListener之后的作用
        * 已经失效了,只是作为一个在没有加载的时候初始化图标的功能。
        * */
        if (bitmap==null){
            imageView.setImageResource(R.mipmap.ic_launcher);
        }else {
            imageView.setImageBitmap(bitmap);
        }
    }
    public void imageLoder(int start,int end){
        //传递进来新的参数,在当前屏幕上的第一个和最后一个的可见项
        for (int i=start;i<end;i++){
            //获取到从第一个开始到最后一个结束的图片所对应的一个url,并对每一个url进行判断
            String url=Myadapter.mURLs[i];
            //加载我们所定义的一组imageview
            Bitmap bitmap= getBitMapFromCache(url);
            //判断在缓存中的图片是否存在,如果不存在则通过网络下载图片
            if (bitmap==null){
                MyAsynctask task =new MyAsynctask(url);
                task.execute(url);
                //将task对象放入一个set集合之中,统一的做管理,如果listview不在滚动的时候就将其全部取消加载
                mTask.add(task);
            }else {
                ImageView imageView= (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);

            }
        }

    }
    public void cancelTask(){
        if (mTask!=null){
            for (MyAsynctask task:mTask){
                task.cancel(false);
            }
        }
    }
    public Bitmap getbitmapstream(String bitmapurl) throws IOException {
        InputStream is=null;
            //指向互联网资源的指针
            URL url=new URL(bitmapurl);
            //打开连接,并强行转换格式
            HttpURLConnection connection= (HttpURLConnection) url.openConnection();
            //获取输入的数据流
            is=new BufferedInputStream(connection.getInputStream());
            //解析所传入的数据
            Bitmap bitmap= BitmapFactory.decodeStream(is);
            //断开连接
            connection.disconnect();
            return bitmap;
    }
           public class MyAsynctask extends AsyncTask<String,Void,Bitmap>  {
               //通过AsyncTask通过网络下载图片,如果下载的图片不在缓存中则放入缓存中
               private ImageView mImageView;
               private String mURL;
               public MyAsynctask(String url){
                   mURL=url;
               }
            @Override
            protected Bitmap doInBackground(String... strings) {
                String url=strings[0];
                //从网络获取图片
                Bitmap bitmap = null;
                try {
                    bitmap = getbitmapstream(url);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //将不在缓存的图片加入缓存
                if (bitmap!=null){
                    addBitMapToCache(url,bitmap);
                }
                return bitmap;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                ImageView imageView= (ImageView) mListView.findViewWithTag(mURL);
                if (bitmap!=null&&imageView!=null){
                    imageView.setImageBitmap(bitmap);
                }
                mTask.remove(this);
                }
            }
        }

在ImageHolder中设置BaseAdapter中所要获取的方法,设置Lru,若所要获取的图片已经存在则从缓存中获取,若没有在缓存中则需要从从网络中下载(也就是通过自定义方法通过url下载),通过传递进来的屏幕中的第一个和最后一个的item位置,通过for循环,为每一个item设置图片,如果已经存在则从缓存中取出,在找所要定义的imageview的时候,通过已经提前为imageview设置好的tag也就是对应的url找到

public class Myadapter extends BaseAdapter implements AbsListView.OnScrollListener {
    private Context mContext;
    private List<Newsbeans> mlist;
    private ListView mListView;
    private LayoutInflater mlayoutinflater;
    private boolean mFirstIntial;
    //如果使用new ImageViewHolder方法创建则每次调用都会创建一个新的LRUCache,通过定义全局变量确保只有一个LRUCACHE创建
    public ImageviewHolder mImageViewHolder;
    private int mStart,mEnd;
    public static String mURLs[];
    public Myadapter(Context context, List<Newsbeans> mlist1,ListView listView){
        mlist=mlist1;
        mListView=listView;
        mFirstIntial=true;
        //初始化字符数组的大小
        mURLs=new String[mlist.size()];
        //获取管理器
        mlayoutinflater=LayoutInflater.from(context);
        mImageViewHolder=new ImageviewHolder(mListView);
        mListView.setOnScrollListener(this);
        for (int i=0;i<mlist1.size();i++){
             mURLs[i]=mlist.get(i).newspic;
        }
    }


    @Override
    public int getCount() {

        return mlist.size();
    }

    @Override
    public Object getItem(int i) {
        return mlist.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View convertview, ViewGroup viewGroup) {
        Viewholder viewholder=null;
        if(convertview==null){
            viewholder=new Viewholder();
            //将item的layout文件转化为view,必须是item文件在之后的view中还要找到对应的控件
            convertview=mlayoutinflater.inflate(R.layout.item,null);
            viewholder.contenttext= (TextView)convertview.findViewById(R.id.tv_content);
            viewholder.imageView=(ImageView)convertview.findViewById(R.id.imageview);
            viewholder.titletext=(TextView)convertview.findViewById(R.id.tv_title);
            convertview.setTag(viewholder);
        }
            else {
            viewholder=(Viewholder)convertview.getTag();
        }
            Newsbeans newsbeans=mlist.get(i);
            viewholder.imageView.setImageResource(R.mipmap.ic_launcher);
            //调用方法传递所需信息
            mImageViewHolder.showImageBy(viewholder.imageView,mlist.get(i).newspic);
            viewholder.imageView.setTag(mlist.get(i).newspic);
            viewholder.titletext.setText(newsbeans.newstitle);
            viewholder.contenttext.setText(newsbeans.newscontent);
        return convertview;
    }

    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
        if(i==SCROLL_STATE_IDLE){
            //如果listview不在滑动状态
            mImageViewHolder.imageLoder(mStart,mEnd);
        }else{
            //如果listview在滑动状态
            mImageViewHolder.cancelTask();
        }
    }

    @Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) {
            mStart=i;
            mEnd=i+i1;
        /*
        * i为firstvisbieitem,i1为visbileItemCount,i2为totalitemcount
        * 在这里判断是否为第一次加载和listview是否开始加载,若没开始加载的时候可见项(visbleitem为0)
        *
        * */
        if (mFirstIntial&&i1>0){
              mImageViewHolder.imageLoder(mStart,mEnd);
              mFirstIntial=false;
        }

    }

    class Viewholder{
        public TextView titletext;
        public TextView contenttext;
        public ImageView imageView;
    }
}

通过设置BaseAdapter的onScrollListener接口,在OnScrollChanged方法中设定在只有在滑动后停止的时候才加载所需要的图片,若没有滑动的时候取消所有加载

以上是关于ListView的网络加载(使用LRU缓存,滚动监听器)的主要内容,如果未能解决你的问题,请参考以下文章

Android之ListView异步加载网络图片(优化缓存机制)

Imageloader在listview/gridview中scroll滚动时图片重载的问题及解决

universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法

Android之ListView&Json加载网络数据

ListView的优化

MySQL: 18 优化后的LRU链表中尾部的缓存页淘汰刷入磁盘的机制