Android实现异步从网络加载图片列表和上拉加载更多、下拉刷新列表(使用xListView框架实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34819586/article/details/77801811

转载请注明出处:http://blog.csdn.net/qq_34819586

使用xlistview来进行 上拉加载更多、下拉刷新列表以及实现列表中图片的异步加载

参考文章:http://blog.csdn.net/afanbaby/article/details/55804850

http://blog.csdn.net/carterjin/article/details/7995935

附上:xListView的实现原理与解析http://blog.csdn.net/zhaokaiqiang1992/article/details/42392731

新手,首次写博客若有侵权请告知立刻删除文章。若有错误请多多指教。

效果图:网络差时会先用app内置图片

网络差时会先用app内置图片




MainActivity:

public class MainActivity extends Activity implements XListView.IXListViewListener {

    private XListView mListView;
    private Handler mHandler;
    private MyAdapter myAdapter;
    List<ImageEntry> mList;
	//要显示的参数及图片地址
    public String[] urls = new String[]{"http://img4.imgtn.bdimg.com/it/u=294550341,4259963231&fm=27&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=1037574783,2132354437&fm=27&gp=0.jpg"};
    public String[] time = new  String[]{"2017.08.01 11.11","2017.08.10 11.2"};
    public   Long[] id =new  Long[]{3L,4L};
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (XListView) findViewById(R.id.xlistview);
        //mainAdapter = new MainAdapter(this);//实例化一个适配器对象
        //实例化一个图片数组列表对象
        mList = new ArrayList<ImageEntry>();
        //datas = new ArrayList<>();
        mHandler = new Handler();
        //设置数据
        geneItems();
        //为xListView设置一些配置和适配器
        mListView.setPullLoadEnable(true);
        mListView.setAdapter(myAdapter);
        mListView.setXListViewListener(this);


        //设置item的点击事件
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                //这里的i-1,是因为头视图占了一个position
                //Toast.makeText(MainActivity.this, "i:" + (i - 1), Toast.LENGTH_SHORT).show();
				
				
                //这里我修改为显示items的id,在MyAdapter的getItemId处修改
                Toast.makeText(MainActivity.this, "你点击的item的id是:" +l, Toast.LENGTH_SHORT).show();
            }
        });
        //设置长按事件
        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(MainActivity.this, "长按事件 i:" + (i-1), Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        /*
        //添加头布局
        View view = getLayoutInflater().inflate(R.layout.head_view, null);
        mListView.addHeaderView(view);
        //添加尾视图
        View view2 = getLayoutInflater().inflate(R.layout.head_view, null);
        mListView.addFooterView(view2);
        */

        //设置禁止上拉
        //mListView.setPullLoadEnable(false);
        //设置禁止下拉
        //mListView.setPullRefreshEnable(false);

    }

//为图片数组列表设置值并为适配器传递数组列表即传递数据,且还实例化了一个myAdapt对象
private void geneItems() {
    for(int i=0; i<2; i++){
        ImageEntry entry = new ImageEntry();
        entry.seturl(urls[i]);//为ImageEntry中url赋值
        entry.settime(time[i]);
        entry.setid(id[i]);
        mList.add(entry);
    }
    myAdapter = new MyAdapter(this,mList);
    //为MyAdapter中xlist_v赋值
    myAdapter.SetXListView(mListView);
}



    //下拉刷新
    @Override
    public void onRefresh() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                
                mList.clear();//清空数组列表变量
                geneItems();
                
                mListView.setAdapter(myAdapter);
                onLoad();
            }
        }, 2000);
    }

    //上拉加载
    @Override
    public void onLoadMore() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                geneItems();
                //mainAdapter.notifyDataSetChanged();
                myAdapter.notifyDataSetChanged();
                onLoad();
            }
        }, 2000);
    }

//停止更新和加载并重置头尾部view。获取时间。
    private void onLoad() {
        mListView.stopRefresh();
        mListView.stopLoadMore();
        //获取当前时间
        Date curDate = new Date(System.currentTimeMillis());
        //格式化
        SimpleDateFormat formatter = new SimpleDateFormat();
        String time = formatter.format(curDate);
        mListView.setRefreshTime(time);
}
}

AsyncImageLoader :
/**
 * 异步加载图片类,内部有缓存,可以通过后台线程获取网络图片。首先生成一个实例,并调用loadDrawableByTag方法来获取一个Drawable对象
 */
public class AsyncImageLoader {

    /**
     * 使用软引用SoftReference,可以由系统在恰当的时候更容易的回收
     */
    private HashMap<String, SoftReference<Drawable>> imageCache;


    public AsyncImageLoader(){
        imageCache = new HashMap<String, SoftReference<Drawable>>();
    }




    /**
     * 通过传入的TagInfo来获取一个网络上的图片
     * @param tag TagInfo对象,保存了position、url和一个待获取的Drawable对象
     * @param callback ImageCallBack对象,用于在获取到图片后供调用侧进行下一步的处理
     * @return drawable 从网络或缓存中得到的Drawable对象,可为null,调用侧需判断
     */
    public Drawable loadDrawableByTag(final TagInfo tag, final ImageCallBack callback){
        Drawable drawable;

        /**
         * 先在缓存中找,如果通过URL地址可以找到,则直接返回该对象
         */
        if(imageCache.containsKey(tag.getUrl())){
            drawable = imageCache.get(tag.getUrl()).get();
            if(null!=drawable){
                return drawable;
            }
        }


        /**
         * 用于在获取到网络图片后,保存图片到缓存,并触发调用侧的处理
         */
        final Handler handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {

                TagInfo info = (TagInfo)msg.obj;
                imageCache.put(info.url, new SoftReference<Drawable>(info.drawable));
                callback.obtainImage(info);

                super.handleMessage(msg);
            }

        };


        /**
         * 如果在缓存中没有找到,则开启一个线程来进行网络请求
         */
        new Thread(new Runnable() {

            @Override
            public void run() {

                TagInfo info = getDrawableIntoTag(tag);
                Message msg = new Message();
                msg.what = 0;
                msg.obj = info;
                handler.sendMessage(msg);
            }
        }).start();

        return null;
    }



    /**
     * 通过传入的TagInfo对象,利用其URL属性,到网络请求图片,获取到图片后保存在TagInfo的Drawable属性中,并返回该TagInfo
     * @param info TagInfo对象,需要利用里面的url属性
     * @return TagInfo 传入的TagInfo对象,增加了Drawable属性后返回
     */
    public TagInfo getDrawableIntoTag(TagInfo info){
        URL request;
        InputStream input;
        Drawable drawable = null;

        try{
            request = new URL(info.getUrl());
            input = (InputStream)request.getContent();
            drawable = Drawable.createFromStream(input, "src"); // 第二个属性可为空,为DEBUG下使用,网上的说明
        }
        catch(Exception e){
            e.printStackTrace();
        }

        info.drawable = drawable;
        return info;
    }



    /**
     * 获取图片的回调接口,里面的obtainImage方法在获取到图片后进行调用
     */
    //interface类是让别的类继承的,如果没有类继承就没意义了,所以你不能用private(私有的)、protected(受保护的)来修饰它。如果修饰了别的类都没法继承它啦,就没意义啦interface类是让别的类继承的,如果没有类继承就没意义了,所以你不能用private(私有的)、protected(受保护的)来修饰它。如果修饰了别的类都没法继承它啦,就没意义啦
    interface ImageCallBack{
        /**
         * 获取到图片后在调用侧执行具体的细节
         * @param info TagInfo对象,传入的info经过处理,增加Drawable属性,并返回给传入者
         */
        public void obtainImage(TagInfo info);
    }



}


ImageEntry:
/**
 * 一个ImageEntry代表了一个带有图片地址url等其他属性的实例,为提高可扩展性,封装了一个对象。
 */
public  class ImageEntry{
    String url;
    String name;
    String time;
    String title;
    Long id;
    public String getUrl(){
        return this.url;
    }
    public String getName(){
        return this.name;
    }
    public String getTime(){
        return this.time;
    }
    public String getTitle(){
        return this.title;
    }
    public Long getId(){
        return this.id;
    }

    public void setid(Long id){
        this.id = id;
    }
    public void seturl(String url){
        this.url = url;
    }
    public void settime(String time){
        this.time = time;
    }
    public void settitle(String title){
        this.title = title;
    }

}

MyAdapter:
/**
 * 重写的Adapter
 */
public  class MyAdapter extends BaseAdapter {

    Context context;
    List<ImageEntry> mList;
    XListView xlist_lv;

    HashMap<String, Drawable> imgCache;     // 图片缓存
    HashMap<Integer, TagInfo> tag_map;      // TagInfo缓存
    AsyncImageLoader loader;                // 异步加载图片类



    /**
     * 构造函数
     * @param context 上下文
     * @param list 包含了所有要显示的图片的ImageEntry对象的列表
     */
    public MyAdapter(Context context, List<ImageEntry> list){
        this.context = context;
        this.mList = list;
        imgCache = new HashMap<String, Drawable>();
        loader = new AsyncImageLoader();
        tag_map = new HashMap<Integer, TagInfo>();

    }

    //获取当前items项的大小,也可以看成是数据源的大小
    //getCount决定了listview一共有多少个item
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return mList.size();
    }
    //根据item的下标获取到View对象
    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return mList.get(position);
    }
    //获取到items的id,对应onItemClick中long的na参数
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return mList.get(position).getId();
       // return 0;
    }

    //根据传入item的下标,获取到view对象
        /*
         * int position,      表示item所在listView中的下标,也是在数据源中下标所对应的数据
         * View convertView,  缓存机制,当一些item项滑出屏幕的时候,会创建新的View对象,这样会使得内存资源占据,
         *                                  所以使用convertView判断是否为空,如果为空的说明item没有滑出,需要创建新的view对象
         *                          如果不为空,说明已经滑出类屏幕所以使用convertView ,view = convertView,
         *                                  可以把convert 理解为滑出的view对象
         * ViewGroup parent   视图组对象,即 表示当前绘制的items项所属的ListView对象。
         * */
    //getView返回了每个item项所显示的view
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder = null;

        if(null==convertView){
            convertView = LayoutInflater.from(context).inflate(R.layout.item, null, false);
            holder = new ViewHolder();
            holder.img = (ImageView) convertView.findViewById(R.id.logo);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }


        String imgurl = mList.get(position).getUrl();   // 得到该项所代表的url地址
        Drawable drawable = imgCache.get(imgurl);       // 先去缓存中找

        TagInfo tag = new TagInfo();
        tag.setPosition(position);  // 保存了当前在adapter中的位置
        tag.setUrl(imgurl);         // 保存当前项所要加载的url
        holder.img.setTag(tag);     // 为ImageView设置Tag,为以后再获取图片后好能找到它
        tag_map.put(position, tag); // 把该TagInfo对应position放入Tag缓存中

        //为列表的文本赋值
        //TextView ItemName = (TextView) convertView.findViewById(R.id.name);
        //ItemName.setText(mList.get(position).getName());
        TextView ItemTime = (TextView) convertView.findViewById(R.id.time);
        ItemTime.setText(mList.get(position).getTime());
        //TextView ItemTitle = (TextView) convertView.findViewById(R.id.title);
        //ItemTitle.setText(mList.get(position).getTitle());


        if(null!=drawable){                         // 找到了直接设置为图像
            holder.img.setImageDrawable(drawable);
        }else{                                      // 没找到则开启异步线程
            drawable = loader.loadDrawableByTag(tag, new ImageCallBack() {

                @Override
                public void obtainImage(TagInfo ret_info) {

                    imgCache.put(ret_info.getUrl(), ret_info.getDrawable());    // 首先把获取的图片放入到缓存中
                    // 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageView
                    ImageView tag_view = (ImageView) xlist_lv.findViewWithTag(tag_map.get(ret_info.getPosition()));
                    Log.i("carter", "tag_view: " + tag_view + " position: " + ret_info.getPosition());
                    if(null!=tag_view)
                    {
                        tag_view.setImageDrawable(ret_info.getDrawable());
                        notifyDataSetChanged();//这样可以在fragement中实现 异步更新图片
                    }
                }
            });


            if(null==drawable){ // 如果获取的图片为空,则默认显示一个图片
                holder.img.setImageResource(R.drawable.nv);

            }
        }


        return convertView;
    }



    class ViewHolder{
        ImageView img;
    }

    public void SetXListView(XListView XListView){
        this.xlist_lv=XListView;
    }

}

TagInfo
public class TagInfo {
    String url;
    int position;
    Drawable drawable;

    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public int getPosition() {
        return position;
    }
    public void setPosition(int position) {
        this.position = position;
    }
    public Drawable getDrawable() {
        return drawable;
    }
    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }

}

view中的3个java可以去github下载我项目中没有修改过:
https://github.com/Maxwin-z/XListView-Android
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.what.xlistview.MainActivity">

    <com.example.what.xlistview.view.XListView
        android:id="@+id/xlistview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="#00000000"
        android:scrollbars="none" />

</RelativeLayout>


item.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/box1"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:id="@+id/box2"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        >
        <LinearLayout
            android:id="@+id/textbox"
            android:layout_width="280dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dp"
            android:orientation="horizontal"
            >
            <ImageView
                android:id="@+id/photo"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginLeft="10dp"
                />
            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="5dp"
                android:textSize="16dp"
                />
            <TextView
                android:id="@+id/time"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_marginLeft="10dp"
                android:textSize="12dp"
                android:layout_marginTop="5dp"
                android:textColor="#999"
                />
        </LinearLayout>
        <LinearLayout
            android:layout_width="280dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textbox"
            android:layout_marginTop="15dp"
            >
            <TextView
                android:id="@+id/title"
                android:layout_width="230dp"
                android:layout_height="60dp"
                android:layout_marginLeft="10dp"
                />
        </LinearLayout>
        <ImageView
            android:id="@+id/logo"
            android:layout_width="100dp"
            android:layout_height="80dp"
            android:layout_marginTop="20dp"
            android:layout_marginRight="10dp"
            android:layout_alignParentRight="true"
            />
    </RelativeLayout>
</RelativeLayout>

head_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:background="@drawable/headiv" />

</LinearLayout>

xlistview_footer.xml和xlistview_header.xml,strings.xml也是github中的

权限方面只要有  <uses-permissionandroid:name="android.permission.INTERNET"/>

项目下载地址:http://download.csdn.net/download/qq_34819586/9961558

猜你喜欢

转载自blog.csdn.net/qq_34819586/article/details/77801811