使用ListView时的几个BUG与相应的优化

package com.example.ex01_1;

成都创新互联专业为企业提供剑河网站建设、剑河做网站、剑河网站设计、剑河网站制作等企业网站建设、网页设计与制作、剑河企业网站模板建站服务,10年剑河做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLConnection;

import java.util.ArrayList;

import java.util.HashMap;

import android.os.AsyncTask;

import android.os.Bundle;

import android.os.Environment;

import android.app.Activity;

import android.app.AlertDialog;

import android.app.Notification;

import android.app.NotificationManager;

import android.content.Context;

import android.content.DialogInterface;

import android.database.CursorJoiner.Result;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.util.Log;

import android.view.LayoutInflater;

import android.view.Menu;

import android.view.MenuItem;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.ViewGroup;

import android.widget.ArrayAdapter;

import android.widget.BaseAdapter;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.ListView;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

public class MainActivity extends Activity 

{

    private ArrayList dataList = new ArrayList();

    private ArrayList fileurl = new ArrayList();

    private MyAdapater myadapater;

    // String PATH = "http://192.168.56.1:8080/server/";

    String PATH = "http://192.168.56.1:8080/server/";

    private ListView listView;

    

/**

问题:图片下载完成,上下滑动ListView,图片重新下载

    原因:上下滑动,触发getView()方法,新建异步任务,重新下载

    解决:创建一个容器,保存已经下载的Bitmap对象

    为了防止重复下载,把以及下载的图片放在imgmap这个容器中

*/

HashMap imgmap = new HashMap();

/**

* 问题:下载过程中,上下滑动ListView,等待下载完成,图片多次下载

        原因:上下滑动ListView,触发getView()方法,判断图片还未下载完成,创

        建新的异步任务.

        解决:创建一个容器保存已经开始执行的AsyncTask对象

        创建一个容器用于保存已经开始的异步任务

*/

    HashMap asymap = new HashMap();

/**问题是:某一行下载完成后,上下滑动ListView, 其他未点击下载的行会出现前面个行的状态。

      引用容器保存文件下载状态,

      我们通过下载状态的区别来更新控件的状态,得以解决这个问题。创建一个容器用来保存当前行的文件的下载状态,用来解决文件下载时,复用View带来的Bug

*/

//

    HashMap stateMap = new HashMap();

    @Override

    protected void onCreate(Bundle savedInstanceState) 

    {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        // 首先找到activity_mian.xml里面的listView1

        listView = (ListView) findViewById(R.id.listView1);

        myadapater = new MyAdapater();

        listView.setAdapter(myadapater);

        // 添加图片

        for (int i = 1; i < 16; i++) 

        {

             dataList.add(PATH + "ic" + i + ".png");

        }

        // 添加文件

             for (int i = 1; i < 16; i++) 

        {

             fileurl.add(PATH + "ic" + i + ".rar");

        }

    }

        

        // 得到一段保存地址

        private String getSavePath(String fileurl)

         {

            String sdcard = Environment.getExternalStorageDirectory() + "";

            // 截取一段地址

            int lastIndexOf = fileurl.lastIndexOf("/");

            String savename = fileurl.substring(lastIndexOf);

            String savePath = sdcard + savename;

            return savePath;

            

        }

        // 创建一个适配器的类

        class MyAdapater extends BaseAdapter {

        private ImgAsy asy;

        

        // 获得行数

        @Override

        public int getCount()

         {

            return 15;

           }

        

        @Override

        public Object getItem(int position) {

        return null;

        }

        

        @Override

        public long getItemId(int position)

         {

            return 0;

           }

    @Override

    // 得到每一行控件的样式和内容,则对每一行的控件的内容和样式的修改都在这里进行

    public View getView(final int position, View convertView, ViewGroup parent) 

     {

    View view = null;

    ViewHolder holder = null;

    // 继续优化,优化1复用布局,防止重复下载,浪费不必要的流量

    if (convertView == null)     // convertView:消失的行布局

    {

                    // 找到XML文件转换器

     LayoutInflater inflater = getLayoutInflater();

                    // 把每一行的样式转换成VIEW对象

      view = inflater.inflate(R.layout.listview, null);

                    // 优化2,减少控件的查找.封装一个类包含所有的控件

       holder = new ViewHolder();

       holder.iv = (ImageView) view.findViewById(R.id.p_w_picpathView1);

       holder.btn = (Button) view.findViewById(R.id.button1);

       holder.tv = (TextView) view.findViewById(R.id.textView1);                       holder.pb = (ProgressBar)view.findViewById(R.id.progressBar1);

       view.setTag(holder);

       } else

     {

       view = convertView;

       holder = (ViewHolder) view.getTag();// 优化2

       }

/** 问题:ListView某一行下载完成之后,上下滑动ListView,还未下载的行数显示图片

       原因:复用造成

       解决:还未下载完成的行数,设置默认图片显示*/

// 把没有参与下载的行的图片设置为默认图片,解决未下载的图片提前显示出来的问题

   holder.iv.setImageResource(R.drawable.ic_launcher);

// 这两行代码是为了判断当前行图片是否下载完成和异步任务是否开启而写的

     Bitmap bitmap = imgmap.get(dataList.get(position));

     ImgAsy asy = asymap.get(dataList.get(position));

     if (bitmap == null) 

               {

// 图片尚未下载,判断当前行的异步任务是否开始

    if (asy == null) 

               {          // 这是一个构造方法

    asy = new ImgAsy(holder.iv, position);

                         

    asy.execute(dataList.get(position));

    asymap.put(dataList.get(position), asy);

    }

    // 解决上下滑动过程中,ImageView对象创建了,却没有创建新的异步任务的问题。

    asy.changView(holder.iv);

    } else {

    holder.iv.setImageBitmap(bitmap);

    }

    //根据下载状态设置各控件的显示状态

    Boolean state=stateMap.get(position);

    if (state==null)

    {

             holder.btn.setEnabled(true);

             holder.pb.setProgress(0);

             holder.tv.setText("未下载");

             holder.btn.setText("下载");

    }else

    {  if (state==true) 

      {

     holder.btn.setEnabled(false);

                 holder.pb.setProgress(100);

                 holder.tv.setText("下载完成");

                 holder.btn.setText("下载完成");

      }else 

          {

               holder.btn.setEnabled(false);

                 holder.tv.setText("下载中");

                 holder.btn.setText("下载中");

          }

    

    }

    final ProgressBar pb = holder.pb;

    final TextView tv = holder.tv;

    final Button btn = holder.btn;

    //用匿名内部类的方法写按钮的监听事件

    holder.btn.setOnClickListener(new OnClickListener()

 {

    public void onClick(View v) 

    {

    //文件下载异步事件的创建与开启

    

                  FileAsy fileAsy = new FileAsy(tv, pb, btn, position);

    

                  fileAsy.execute(fileurl.get(position));//带上了行号。

    }

    });

    return view;

    }

  }

    

    // 下载文件的异步任务

    class FileAsy extends AsyncTask 

     {

        TextView tv;

        ProgressBar pb;

        Button btn;// 把Button 传入到下载文件的异步任务里,为了能够控制Button的状态

        private int position;

        

        public FileAsy(TextView tv, ProgressBar pb, Button btn, int position) {

        super();

        this.tv = tv;

        this.pb = pb;

        this.btn = btn;

        this.position = position;

    }

    

    // 保存路径

    String savepath = Environment.getExternalStorageDirectory() + "/wenjian.apk";

    private String result;

    

    // 执行在doInBackgound之前

    @Override

    protected void onPreExecute()

     {

          btn.setEnabled(false);

          btn.setText("下载");

          stateMap.put(position, false);//文件每一行的下载状态

          super.onPreExecute();

      }

    

    @SuppressWarnings("resource")

    @Override

    protected String doInBackground(String... params)

    {

    try {

    URL url = new URL(params[0]);

    // 打开链接

    URLConnection connection = url.openConnection();

      int length = connection.getContentLength();

        InputStream is = connection.getInputStream();

          // File file = new File(savepath);

    String savePath3 = getSavePath(params[0]);

      FileOutputStream fos = new FileOutputStream(savePath3);

       byte[] buffer = new byte[1024];

        int len = 0;

          int dllength = 0;// 要写在这里

    while (-1 != (len = is.read(buffer)))// 先读取数据

    {

    fos.write(buffer, 0, len);// 在写出数据

            // 推送进度

    dllength += len;

    publishProgress(dllength * 100 / length);

    }

    } catch (MalformedURLException e) {

    e.printStackTrace();

    } catch (IOException e) {

    result = "下载失败";

    e.printStackTrace();

    }

    result = "下载成功";

    return result;

    }

    

    protected void onProgressUpdate(Integer... values) {

    

    //该判断语句为了解决BUG:文件在下载过程中,缓慢上下滑动LiseView,没有点下载的行出现了对应的下载的状态。

               //只要Button当前行去

    if (isVisiablePositon(listView, position)) {

    pb.setProgress(values[0]);

    tv.setText("已下载" + values[0] + "%");

    }

    super.onProgressUpdate(values);

    }

    

    // 下载完成后,根据位图去更新图片

    protected void onPostExecute(String result)

      {

        Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();

        if (isVisiablePositon(listView, position)) 

         {

            btn.setEnabled(true);

            btn.setText("下载完成");

        }

        stateMap.put(position, true);

        super.onPostExecute(result);

      }

    }

    

    // 下载图片

    class ImgAsy extends AsyncTask 

     {

        private Bitmap bitmap;

        private ImageView iv;

        private int position;// 加入一个行号的参数

        

        public ImgAsy(ImageView iv, int position) {

        super();

        this.iv = iv;

        this.position = position;

    }

    

/**

* 问题:下载过程中,上下滑动ListView,等待下载完成,有些行的图片不显示

        原因:上下滑动ListView,创建新的ImageView对象,没有创建新的 AsyncTask

        解决:   1.写一个方法重新给AsyncTask中的ImageView赋值 

* 通知AsyncTask更新ImageView对象   

* 2.在getView()中调用

*/

    public void changView(ImageView iv) {

    this.iv = iv;

    }

    

    @Override

    protected Bitmap doInBackground(String... params) {

    try {

    URL url = new URL(params[0]);

    InputStream is = url.openStream();// 开流

    bitmap = BitmapFactory.decodeStream(is);// 下载位图

    } catch (MalformedURLException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    return bitmap;// 相当于handlermessage

    }

// 下载完成后,根据位图去更新图片(写在内部类里)

    protected void onPostExecute(Bitmap bitmap) {

    isVisiablePositon(listView, position);

    if (isVisiablePositon(listView, position)) {

    iv.setImageBitmap(bitmap);

    }

    imgmap.put(dataList.get(position), bitmap);// 保存

    super.onPostExecute(bitmap);

    }

    }

// 写一个方法判断当前行是否可见。让数据更新只在当前行进行。返回值是Boolean类型

public Boolean isVisiablePositon(ListView listView, int position) {

    // 获得可见行第一行行号

    int fp = listView.getFirstVisiblePosition();

    // 获得可见行最后一行行号

    int lp = listView.getLastVisiblePosition();

    // 在构造方法里面去添加position

    // 判断当前行是否在可见范围

    if (position >= fp && position <= lp) {// 让可见行去更新控件

      // iv.setImageBitmap(bitmap);

    return true;//返回真,结束方法

    }

    return false;

    }

    

    // 优化2,减少控件的查找.封装一个类包含所有的控件

    class ViewHolder {

    ImageView iv;

    Button btn;

    TextView tv;

    ProgressBar pb;

    }

    }



当前标题:使用ListView时的几个BUG与相应的优化
文章URL:http://scyanting.com/article/jcpjjc.html