异步加载——MVP

来源:互联网 发布:南方数据粘贴 编辑:程序博客网 时间:2024/06/09 19:55

通过学习MVP设计模式,想写一个小例子巩固一下。写个什么例子好呢?之前学习过慕课网的有关异步加载的视频,就把这个例子改编一下吧。
使用到的json数据网址是:http://www.imooc.com/api/teacher?type=4&num=30

效果图

这里写图片描述

项目目录图

这里写图片描述

思路图

这里写图片描述

  • 根据当前滚动的状态变化异步加载图片
  • 当滚动停止时加载当前可见的item对应的图片
  • 当listview处于非停止状态时取消所有加载任务
  • 第一次进入页面时由于listview的状态没有改变所以不加载图片,需要增加一个boolean类型isFirst标识,在onScroll方法中触发加载。
  • 将加载的图片存到LruCache中,当加载的时候先从LruCache中获取,如果没有再加载。

V层

主布局文件activity_main

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.sjb.bupt.asynctaskdemo.view.activity.MainActivity"    android:orientation="vertical">    <ListView        android:id="@+id/lv"        android:layout_width="match_parent"        android:layout_height="match_parent">    </ListView></LinearLayout>

item布局文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal" android:layout_width="match_parent"    android:layout_height="wrap_content">    <ImageView        android:id="@+id/iv_icon"        android:layout_width="70dp"        android:layout_height="70dp"        android:src="@mipmap/ic_launcher"/>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:paddingLeft="5dp"        android:gravity="center"        >        <TextView            android:id="@+id/tv_title"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:textColor="#333"            android:textSize="16sp"            android:maxLines="1"            android:text="标题"/>        <TextView            android:id="@+id/tv_content"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:textSize="12sp"            android:maxLines="3"            android:text="内容"/>    </LinearLayout></LinearLayout>

接口IMainActivityView

P层与V层通信需要通过此接口

package com.sjb.bupt.asynctaskdemo.view;import android.graphics.Bitmap;import android.widget.ImageView;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;import java.util.List;/** * Created by sjb on 2017/7/5. */public interface IMainActivityView  {    /**     * 显示listview的数据     * @param datas     */    void showListView(List<NewsEntity> datas);    /**     * 获取listView数据失败是调用     */    void loadFailed();    /**     * 显示异步加载的图片     * @param imageView     * @param bitmap     */    void showAsyncTaskImage(ImageView imageView, Bitmap bitmap);}

MainActivity

MainActivity实现接口IMainActivityView和接口 AbsListView.OnScrollListener来监听listview的滚动事件

package com.sjb.bupt.asynctaskdemo.view.activity;import android.graphics.Bitmap;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.AbsListView;import android.widget.ImageView;import android.widget.ListView;import android.widget.Toast;import com.sjb.bupt.asynctaskdemo.R;import com.sjb.bupt.asynctaskdemo.adapter.MyAdapter;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;import com.sjb.bupt.asynctaskdemo.presenter.ImageLoadPresenter;import com.sjb.bupt.asynctaskdemo.presenter.LoadDataPresenter;import com.sjb.bupt.asynctaskdemo.view.IMainActivityView;import java.util.List;public class MainActivity extends AppCompatActivity implements IMainActivityView, AbsListView.OnScrollListener {    private ListView lv;    private MyAdapter myAdapter;    private String url="http://www.imooc.com/api/teacher?type=4&num=30";    private LoadDataPresenter presenter;//加载数据的presenter    private ImageLoadPresenter mImageLoadPresenter;//加载图片的presenter    private int mVisibleStartItem,mVisibleEndItem;//需要加载图片listview中item的开始和结束    public static String[] URLS;//将数据中的图片URL存起来    private boolean isFirst=true;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();//初始化控件        initObjects();//初始化对象        initEvents();//初始化事件        loadDatas();//加载listView的数据    }    private void initEvents() {        lv.setOnScrollListener(this);    }    private void loadDatas() {        presenter.getDatas(url);    }    private void initObjects() {        presenter=new LoadDataPresenter(this);        mImageLoadPresenter = new ImageLoadPresenter(this,lv);    }    private void initViews() {        lv = (ListView) findViewById(R.id.lv);    }    @Override    public void showListView(List<NewsEntity> datas) {        URLS=new String[datas.size()];        for (int i=0;i<datas.size();i++) {            URLS[i]=datas.get(i).getPicSmall();        }        myAdapter=new MyAdapter(this,datas);        lv.setAdapter(myAdapter);    }    @Override    public void loadFailed() {        Toast.makeText(this,"数据加载失败",Toast.LENGTH_SHORT).show();    }    @Override    public void showAsyncTaskImage(ImageView imageView, Bitmap bitmap) {        imageView.setImageBitmap(bitmap);    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (scrollState == SCROLL_STATE_IDLE) {            //加载图片            mImageLoadPresenter.showImages(mVisibleStartItem, mVisibleEndItem);        }else{            //停止加载            mImageLoadPresenter.cancelAllTask();        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        mVisibleStartItem=firstVisibleItem;        mVisibleEndItem=firstVisibleItem+visibleItemCount;        if (isFirst&&visibleItemCount>0) {            mImageLoadPresenter.showImages(mVisibleStartItem,mVisibleEndItem);            isFirst=false;        }    }}

adapter

listview的MyAdapter代码如下

package com.sjb.bupt.asynctaskdemo.adapter;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import com.sjb.bupt.asynctaskdemo.R;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;import java.util.List;/** * Created by sjb on 2017/7/5. */public class MyAdapter extends BaseAdapter {    private List<NewsEntity> mDatas;    private Context mContex;    public MyAdapter(Context context, List<NewsEntity> datas){        this.mContex=context;        this.mDatas=datas;    }    @Override    public int getCount() {        return mDatas.size();    }    @Override    public Object getItem(int position) {        return mDatas.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {       if (convertView==null){           convertView = LayoutInflater.from(mContex).inflate(R.layout.listview_item, parent, false);       }      MyViewHolder holder= MyViewHolder.getViewHolder(convertView);        NewsEntity news = mDatas.get(position);        holder.tv_title.setText(news.getName());        holder.tv_content.setText(news.getDescription());        holder.iv_icon.setImageResource(R.mipmap.ic_launcher);        holder.iv_icon.setTag(news.getPicSmall());        return convertView;    }    static class MyViewHolder{        private ImageView iv_icon;        private TextView tv_title;        private TextView tv_content;        private   MyViewHolder(View convertView){            iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);            tv_title = (TextView) convertView.findViewById(R.id.tv_title);            tv_content = (TextView) convertView.findViewById(R.id.tv_content);        }        public static  MyViewHolder getViewHolder(View convertView){            MyViewHolder holder = (MyViewHolder) convertView.getTag();            if (holder == null) {                holder= new MyViewHolder(convertView);                convertView.setTag(holder);            }            return holder;        }    }}

P层

加载数据的LoadDataPresenter

package com.sjb.bupt.asynctaskdemo.presenter;import com.sjb.bupt.asynctaskdemo.model.GetNewsModelImpl;import com.sjb.bupt.asynctaskdemo.model.IGetNewsModel;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;import com.sjb.bupt.asynctaskdemo.view.IMainActivityView;import java.util.List;/** * Created by sjb on 2017/7/5. */public class LoadDataPresenter {    private IGetNewsModel mIGetNewsModel;    private IMainActivityView mIMainActivityView;    public LoadDataPresenter(IMainActivityView iMainActivityView){        this.mIMainActivityView=iMainActivityView;        mIGetNewsModel=new GetNewsModelImpl();    }   public void getDatas(String url){       mIGetNewsModel.getNews(url, new IGetNewsModel.OnGetNewsListener() {           @Override           public void onSuccess(List<NewsEntity> newsEntities) {               mIMainActivityView.showListView(newsEntities);           }           @Override           public void onFailed() {               mIMainActivityView.loadFailed();           }       });    }}

异步加载图片的ImageLoadPresenter

package com.sjb.bupt.asynctaskdemo.presenter;import android.graphics.Bitmap;import android.widget.ImageView;import android.widget.ListView;import com.sjb.bupt.asynctaskdemo.view.activity.MainActivity;import com.sjb.bupt.asynctaskdemo.model.IImageLoadModel;import com.sjb.bupt.asynctaskdemo.model.ImageLoadModelImpl;import com.sjb.bupt.asynctaskdemo.view.IMainActivityView;/** * Created by sjb on 2017/7/6. */public class ImageLoadPresenter {    private IMainActivityView mIMainActivityView;    private IImageLoadModel mIImageLoadModel;    private ListView mListView;    public ImageLoadPresenter(IMainActivityView iMainActivityView, ListView listView){       this.mIMainActivityView=iMainActivityView;        this.mListView=listView;        mIImageLoadModel=new ImageLoadModelImpl(mListView);    }    public void showImages(int start,int end){        for (int i=start;i<end;i++) {            mIImageLoadModel.loadImage(MainActivity.URLS[i], new IImageLoadModel.OnImageLoadListener() {                @Override                public void onSuccess(ImageView imageView, Bitmap bitmap) {                    mIMainActivityView.showAsyncTaskImage(imageView,bitmap);                }                @Override                public void onFailed() {                }            });        }    }    public void cancelAllTask(){        mIImageLoadModel.cancelAllTask();    }}

M层

根据json数据格式写了两个实体类

DatasEntity

package com.sjb.bupt.asynctaskdemo.model.bean;import java.util.List;/** * Created by sjb on 2017/7/5. */public class DatasEntity {    private String status;    private List<NewsEntity> data;    public String getStatus() {        return status;    }    public void setStatus(String status) {        this.status = status;    }    public List<NewsEntity> getData() {        return data;    }    public void setData(List<NewsEntity> data) {        this.data = data;    }}

NewsEntity

package com.sjb.bupt.asynctaskdemo.model.bean;/** * Created by sjb on 2017/7/5. */public class NewsEntity {    private int id;    private String name;    private String picSmall;    private String picBig;    private String description;    private int learner;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPicSmall() {        return picSmall;    }    public void setPicSmall(String picSmall) {        this.picSmall = picSmall;    }    public String getPicBig() {        return picBig;    }    public void setPicBig(String picBig) {        this.picBig = picBig;    }    public String getDescription() {        return description;    }    public void setDescription(String description) {        this.description = description;    }    public int getLearner() {        return learner;    }    public void setLearner(int learner) {        this.learner = learner;    }}

获取数据的接口model

package com.sjb.bupt.asynctaskdemo.model;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;import java.util.List;/** * Created by sjb on 2017/7/5. */public interface IGetNewsModel {    /**     * 获取listview的数据     * @param url     * @param listener     */    void getNews(String url, GetNewsModelImpl.OnGetNewsListener listener);    /**     * 监听获取数据的回调接口     */    interface OnGetNewsListener {        void onSuccess(List<NewsEntity> newsEntities);        void onFailed();    }}

获取数据接口的实现类GetNewsModelImpl

package com.sjb.bupt.asynctaskdemo.model;import android.os.AsyncTask;import android.os.Handler;import com.google.gson.Gson;import com.sjb.bupt.asynctaskdemo.model.bean.DatasEntity;import com.sjb.bupt.asynctaskdemo.model.bean.NewsEntity;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;/** * Created by sjb on 2017/7/5. */public class GetNewsModelImpl implements IGetNewsModel {    private OnGetNewsListener mListener;    private Handler myHandler = new Handler();    @Override    public void getNews(String url,OnGetNewsListener listener) {        this.mListener=listener;        new NewsAsyncTask().execute(url);    }    private List<NewsEntity> getJsonDatas(String url) {        List<NewsEntity> datas = new ArrayList<>();        try {            String jsonResult = readStream(new URL(url).openStream());            Gson gson=new Gson();            DatasEntity datasEntity = gson.fromJson(jsonResult, DatasEntity.class);             datas = datasEntity.getData();        } catch (Exception e) {            myHandler.post(new Runnable() {                @Override                public void run() {                    mListener.onFailed();                }            });            e.printStackTrace();        }        return datas;    }    private String readStream(InputStream is) {        InputStreamReader isr;        String result = "";        try {            String line = "";            isr = new InputStreamReader(is, "utf-8");            BufferedReader br = new BufferedReader(isr);            while ((line = br.readLine()) != null) {                result += line;            }        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return result;    }    class NewsAsyncTask extends AsyncTask<String, Void, List<NewsEntity>> {        @Override        protected List<NewsEntity> doInBackground(String... params) {            return getJsonDatas(params[0]);        }        @Override        protected void onPostExecute(List<NewsEntity> newsEntities) {            super.onPostExecute(newsEntities);            if (newsEntities != null) {                mListener.onSuccess(newsEntities);            }else{                mListener.onFailed();            }        }    }}

加载图片的接口Model

package com.sjb.bupt.asynctaskdemo.model;import android.graphics.Bitmap;import android.widget.ImageView;/** * Created by sjb on 2017/7/6. */public interface IImageLoadModel {    /**     * 通过url获取bitmap     * @param url     * @return     */    Bitmap getBitmapFromUrl(String url);    /**     * 加载图片     * @param url     * @param listener     */    void loadImage(String url,OnImageLoadListener listener);    /**     * 取消所有加载任务     */    void cancelAllTask();    /**     * 图片加载回调接口     */    interface OnImageLoadListener{        void onSuccess(ImageView imageView, Bitmap bitmap);        void onFailed();    }}

加载图片接口model的实现类ImageLoadModelImpl

package com.sjb.bupt.asynctaskdemo.model;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.util.Log;import android.util.LruCache;import android.widget.ImageView;import android.widget.ListView;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import java.util.ArrayList;import java.util.List;import static android.content.ContentValues.TAG;/** * Created by sjb on 2017/7/5. */public class ImageLoadModelImpl implements IImageLoadModel {    LruCache<String, Bitmap> mCache;    private OnImageLoadListener mListener;    private ListView mListView;    private List<ImageAsyncTask> tasks;    public ImageLoadModelImpl(ListView listView) {       this.mListView=listView;        tasks = new ArrayList<>();        final int maxMemory = (int) Runtime.getRuntime().maxMemory();        Log.d(TAG, "ImageLoadModelImpl: " + maxMemory);        mCache = new LruCache<String, Bitmap>(maxMemory / 4) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getByteCount();            }        };    }    @Override    public void loadImage(String url, OnImageLoadListener listener) {        this.mListener = listener;        ImageView imageView=(ImageView) mListView.findViewWithTag(url);        Bitmap bitmap = getBitmapFromCache(url);        if (bitmap != null) {            mListener.onSuccess(imageView, bitmap);        } else {            ImageAsyncTask task = new ImageAsyncTask(imageView, url);            tasks.add(task);           task.execute(url);        }    }    @Override    public void cancelAllTask() {        if (tasks.size() != 0) {            for (ImageAsyncTask task : tasks) {                task.cancel(false);            }        }    }    private void addBitmapToCache(String key, Bitmap bitmap) {        if (getBitmapFromUrl(key) == null) {            mCache.put(key, bitmap);        }    }    private Bitmap getBitmapFromCache(String key) {        return mCache.get(key);    }    @Override    public Bitmap getBitmapFromUrl(String url) {        Bitmap bitmap = null;        HttpURLConnection connection = null;        InputStream is = null;        try {            URL mUrl = new URL(url);            connection = (HttpURLConnection) mUrl.openConnection();            is = connection.getInputStream();            bitmap = BitmapFactory.decodeStream(is);        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (is != null) {                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }            if (connection != null) {                connection.disconnect();            }        }        return bitmap;    }    private class ImageAsyncTask extends AsyncTask<String, Void, Bitmap> {        private String url;        private ImageView imageView;        public ImageAsyncTask(ImageView imageView,String url) {            this.url = url;            this.imageView = imageView;        }        @Override        protected Bitmap doInBackground(String... params) {            return getBitmapFromUrl(params[0]);        }        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            if (imageView.getTag().equals(url)) {                //将图片存到缓存中                if (bitmap != null) {                    addBitmapToCache(url, bitmap);                }                mListener.onSuccess(imageView, bitmap);            }            tasks.remove(this);        }    }}

结束

如有错误望指出,相互交流,谢谢!