ListView优化、图片缓存、分页加载(二)

该项目主要是对一个食谱的json网络解析,运用ListView将解析的图片、文本展示给用户。实现了ListView的分页加载,利用convertView的复用,并且解决了图片的错位问题,也将图片进行了缓存,实现了良好的用户体验和内存优化

这里写图片描述
Android的优化

1.布局优化:在xml中布局页面减少嵌套

2.java代码中用面向对象的思想,把相同的代码封装,相同的功能都有封装类

3.栈内存的优化:尽量不使用递归,使用算法

4.堆内存的优化:少创建对象,能重复使用的对象就继续使用

一、ListView的优化

1.属性的优化

    宽高和高度都设置math—parent  getView()只会执行一次
                 wrap—content   getView()会执行多次

2.convertView的复用

    前提:convertView的类型和新建的View的类型是相同,所以可以重复使用

    优点:减少了创建的对象的数量,节省资源,降低内存的消耗,提高了性能


    系统会把第一屏的数据先进行加载(屏幕当前显示的数量+1),当滑系统会把移动屏幕的页面缓存起来,重复利用该页面(convertView)

3.ViewHolder的使用

    将控件封装,达到重用,减少findViewById的次数

    View中标签的tag属性:

        3.1和View中的Id相似,都能标识控件的唯一性

        3.2view.setTag(object)设置控件的标签

        3.3view.getTag()获取控件的标签

三、布局代码

1.主页面ListView控件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent "
        android:layout_height="match_parent " 
        android:divider="#00f"
        android:dividerHeight="1dp"/>
        <!--属性优化,将ListView控件的宽高设置为 match_parent -->

    <Button 
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="addMore"
        android:layout_alignParentBottom="true"
        android:visibility="gone"
        android:text="加载更多"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/bt_top"
        android:text="回到顶部"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:visibility="gone"
        android:layout_margin="10dp"/>
</RelativeLayout>
2.ListView的Item的布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/pic"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="45dp"
        android:layout_toRightOf="@id/pic"
        android:text="名称"
        android:textSize="20sp" />

</RelativeLayout>

四、适配器代码

package com.example.listviewdemo.adapter;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.graphics.Bitmap;
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.example.listviewdemo.R;
import com.example.listviewdemo.back.ImageCallBack;
import com.example.listviewdemo.task.DownLoadImageTask;
import com.example.listviewdemo.task.mode.CookBook;

public class MyAdapter extends BaseAdapter{

    private Context context;
    private List<CookBook> data;

    //保存当前下载的地址和图片的图片内容
    private Map<String,Bitmap> map=new HashMap<String,Bitmap>(); 


    public MyAdapter(Context context, List<CookBook> data) {
        super();
        this.context = context;
        this.data = data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        final ViewHolder holder;

        if (convertView==null)
        {
            LayoutInflater inflater=LayoutInflater.from(context);

            convertView = inflater.inflate(R.layout.item_lv,parent,false);

            holder=new ViewHolder();

            holder.iv_pic=(ImageView) convertView.findViewById(R.id.pic);
            holder.tv_title=(TextView) convertView.findViewById(R.id.tv_title);

            convertView.setTag(holder);
        }else
        {
            holder=(ViewHolder) convertView.getTag();
        }

        //为控件设置内容
        holder.iv_pic.setImageResource(R.drawable.ic_launcher);
        holder.tv_title.setText(data.get(position).getTitle());

        //开启异步任务加载网络图片,通过接口回调,将图片返回--------
        String imgUrl=data.get(position).getPicUrl();//当前下载的图片的地址

        //把控件和地址绑定
        holder.iv_pic.setTag(imgUrl);

        //判断内存中(map)中,是否有该图片,如果有则从内存中取出图片,并显示出来
        //如果没有开启异步任务下载图片,将下载后的图片,存入内存中(map),方便下次使用
        if (!map.containsKey(imgUrl)) {
            new DownLoadImageTask(new ImageCallBack() {

                /**
                 * String imgpath当前异步任务加载的图片地址
                 * Bitmap   bitmap当前异步任务  根据地址(imgpath)加载到的位图
                 */
                @Override
                public void sendBitmap(String imgpath,Bitmap bitmap) {

                    if (bitmap!=null) {
                        //判断当前加载完成的图片地址,是否和控件上绑定的图片地址一致,如果一致则显示,如果不一致,则不显示
                        if (imgpath.equals(holder.iv_pic.getTag())) {
                            holder.iv_pic.setImageBitmap(bitmap);

                        }
                        map.put(imgpath, bitmap);
                    }
                }
            }).execute(imgUrl);
        }else
        {
            holder.iv_pic.setImageBitmap(map.get(imgUrl));
        }

        return convertView;
    }

    class ViewHolder
    {
        private TextView tv_title;
        private ImageView iv_pic;
    }

}

五、实体类bean

package com.example.listviewdemo.task.mode;

/**
 * 食谱实体类
 * @author WangJ
 *
 */
public class CookBook {

    private String title;
    private String picUrl;
    public CookBook(String title, String picUrl) {
        super();
        this.title = title;
        this.picUrl = picUrl;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getPicUrl() {
        return picUrl;
    }
    public void setPicUrl(String picUrl) {
        this.picUrl = picUrl;
    }
}

六、接口回调

1.文本和图片地址下载的接口回调
package com.example.listviewdemo.back;

import java.util.List;

import com.example.day09_listview03.task.mode.CookBook;

public interface DataCallBack {

    public void sendResult(List<CookBook> data);

}
    2.图片下载的接口回调
package com.example.listviewdemo.back;

import android.graphics.Bitmap;

/**
 * 图片的接口回调
 * @author WangJ
 *
 */
public interface ImageCallBack {

    public void sendBitmap(String imgpath,Bitmap bitmap);
}

七、解析数据和请求网络

1.解析数据
package com.example.listviewdemo.task;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import com.example.listviewdemo.task.mode.CookBook;

public class ParseJson {

    public static List<CookBook> parseJson(String jsonStr) {

        List<CookBook> data = null;

        try {

            if (jsonStr != null) {

                data = new ArrayList<CookBook>();

                JSONObject jsonObject = new JSONObject(jsonStr);

                JSONArray jsonArray = jsonObject.getJSONArray("data");

                for (int i = 0; i < jsonArray.length(); i++) {

                    JSONObject jsonObject2 = jsonArray.getJSONObject(i);
                    String title = jsonObject2.getString("title");
                    String picUrl = jsonObject2.getString("pic");

                    data.add(new CookBook(title, picUrl));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }

}

2.请求网络工具类

package com.example.listview03demo.tools;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * Http 加载网络数据
 * @author Administrator
 *
 */
public class HttpUtils {

    /**
     * 加载网络数据  
     * @param path  路径
     * @return  String
     */
    public static String getStringResult(String path) {

        HttpURLConnection conn=null;
        InputStream is = null;
        StringBuilder sBuilder = null;

        try {

            //1, 得到URL 对象
            URL url = new URL(path);

            //2, 打开连接
             conn = (HttpURLConnection) url.openConnection();

            //3, 设置请求方式
            conn.setRequestMethod("GET");

            //4, 连接
            conn.connect();

            //5, 判断返回的结果码 (200),得到响应数据
            if(conn.getResponseCode() == 200)
            {
                 is = conn.getInputStream();

                sBuilder = new StringBuilder();

                byte[] buffer  = new  byte[1024];

                int len = 0;

                while ((len = is.read(buffer))!=-1) {

                    sBuilder.append(new String(buffer,0,len));
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally
        {
            try {
                if (conn!=null) {
                    conn.disconnect();
                }
                if (is!=null) {
                    is.close();
                }
            } catch (Exception e2) {
            }
        }


        return sBuilder.toString();
    }


    /**
     * 加载网络图片
     * @param path
     * @return byte[]
     */
    public static byte[] getByteResult(String path)
    {
        HttpURLConnection conn=null;
        InputStream is=null;
        ByteArrayOutputStream baos=null;

        try {

            //1, 得到URL 对象
            URL url = new URL(path);

            //2, 打开连接
             conn = (HttpURLConnection) url.openConnection();

            //3, 设置请求方式
            conn.setRequestMethod("GET");

            //4, 连接
            conn.connect();

            //5, 判断返回的结果码 (200),得到响应数据
            if(conn.getResponseCode() == 200)
            {
                 is = conn.getInputStream();

                 baos = new ByteArrayOutputStream();

                byte[] buffer = new byte[1024];

                int len = 0;

                while ((len=is.read(buffer))!=-1) {

                    baos.write(buffer, 0, len);
                    baos.flush();
                }


            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally{

            try {
                if (conn!=null) {
                    conn.disconnect();
                }
                if (is!=null) {
                    is.close();
                }
                if (baos!=null) {
                    baos.close();
                }
            } catch (Exception e2) {
            }

        }


        return baos.toByteArray();
    }
}

八、开启异步

package com.example.listviewdemo;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import com.example.listviewdemo.adapter.MyAdapter;
import com.example.listviewdemo.back.DataCallBack;
import com.example.listviewdemo.task.DownLoadDataTask;
import com.example.listviewdemo.task.mode.CookBook;

public class MainActivity extends Activity {

    private ListView listview;
    //地址
    private String path="http://www.qubaobei.com/ios/cf/dish_list.php?stage_id=1&limit=20&page=";
    private int num=1;//当前页码

    private MyAdapter adapter;
    private boolean isLast=false;
    private Button button;//加载更多按钮
    private List<CookBook> totalList=new ArrayList<CookBook>();//要显示的总数据
    private Button bt_top;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listview = (ListView) findViewById(R.id.listview);
        button = (Button) findViewById(R.id.button);
        bt_top = (Button) findViewById(R.id.bt_top);

        //开启异步任务,加载数据,加载第一页
        new DownLoadDataTask(this,new DataCallBack() {

            @Override
            public void sendResult(List<CookBook> data) {
                //返回当前加载的数据

                totalList.addAll(data);

                adapter=new MyAdapter(MainActivity.this, totalList);

                listview.setAdapter(adapter);
            }
        }).execute(path+num);

        listview.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

                if (isLast&&scrollState==SCROLL_STATE_IDLE) {
                    //显示加载更多
                    button.setVisibility(View.VISIBLE);
                }else
                {
                    //隐藏
                    button.setVisibility(View.GONE);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {

                //判断是否到底部
                isLast=(firstVisibleItem+visibleItemCount==totalItemCount);

                //判断是否显示回到底部
                if (firstVisibleItem>5) {
                    bt_top.setVisibility(View.VISIBLE);
                }else {
                    bt_top.setVisibility(View.GONE);
                }

            }
        });


      //回到列表的顶部
        bt_top.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                listview.setSelection(0);
            }
        });
    }

    public void addMore(View v)
    {
        //当前页面+1
        num++;
        //2.开启异步任务加载数据

        new DownLoadDataTask(MainActivity.this, new DataCallBack() {

            @Override
            public void sendResult(List<CookBook> data) {

                if (data==null||data.size()<=0) {
                    Toast.makeText(MainActivity.this,
                            "已结加载到最后一页", 1).show();
                }else
                {
                    //加载的数据放入总数据中
                    totalList.addAll(data);
                    //刷新适配器
                    adapter.notifyDataSetChanged();
                }



            }
        }).execute(path+num);

        //3.隐藏“加载数据”
        button.setVisibility(View.GONE);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值