Android - 滚动时洗牌的ListView图像

时间:2014-12-05 18:12:45

标签: android image listview android-listview

我正在尝试使用动态加载的图像制作ListView,使用AsyncTask下载图像,然后将其设置为ListView。我的问题是,在向下滚动时,图像会随机变化。

public class GetAllCustomerListViewAdapter extends BaseAdapter {

private JSONArray dataArray;
private Activity activity;

private static LayoutInflater inflater = null;
private static final String baseUrlForImage = "http://192.168.254.1/contact/images/";

public GetAllCustomerListViewAdapter(JSONArray jsonArray, Activity a)
{
    this.dataArray = jsonArray;
    this.activity = a;


    inflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return this.dataArray.length();
}

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

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

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

    // set up convert view if it is null
    final ListCell cell;
    if (convertView == null)
    {
        convertView = inflater.inflate(R.layout.get_all_customer_list_view_cell, null);
        cell = new ListCell();

        cell.FullName = (TextView) convertView.findViewById(R.id.customer_full_name);
        cell.Age = (TextView) convertView.findViewById(R.id.customer_age);

        cell.mobile = (ImageView) convertView.findViewById(R.id.customer_mobile);

        convertView.setTag(cell);
    }
    else
    {
        cell = (ListCell) convertView.getTag();
    }

    // change the data of cell

    try
    {
        JSONObject jsonObject = this.dataArray.getJSONObject(position);
        cell.FullName.setText(jsonObject.getString("FirstName")+" "+jsonObject.getString("LastName"));
        cell.Age.setText(" "+jsonObject.getInt("Age"));

        String nameOfImage = jsonObject.getString("id");

        String urlForImageInServer = "http://192.168.254.1/contact/getImage.php?id="+nameOfImage;

        new AsyncTask<String, Void, Bitmap>() {

            @Override
            protected Bitmap doInBackground(String... params) {
                String url = params[0];
                Bitmap icon = null;

                try {
                    InputStream in = new java.net.URL(url).openStream();
                    icon = BitmapFactory.decodeStream(in);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                return icon;
            }

            protected void onPostExecute(Bitmap result) {
                cell.mobile.setImageBitmap(result);             
            }               


        }.execute(urlForImageInServer);           



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


    return convertView;
}



private  class  ListCell
{
    private TextView FullName;
    private TextView Age;

    private ImageView mobile;

}    

}

1 个答案:

答案 0 :(得分:1)

我最近刚刚做了这个(实际上凌晨1点) - 问题是ListView正在积极地回收视图。因此,它获得了仍然保留位图的旧视图。

以下是您的需求:

[注意:检查Bitmap是否先前被缓存了。如果没有,请执行以下步骤]

  1. 在尝试加载之前先缓存位图
  2. 缓存目标ImageView(并跟踪Bitmap应该使用什么Bitmap)。此外,请清空ImageView,以防它仍包含先前回收项目(imageView.setImageBitmap(null);)中的位图
  3. 仔细检查缓存的ImageView是否尚未针对上一个项目进行缓存。如果先前已经缓存,那么以某种方式表明不应该加载正在接收的位图(我通过为每个点存储一个ImageView来做到这一点,并且一旦接收到Bitmap,如果该行的ImageView再次为null,那么我不会加载在Bitmap中)
  4. 加载位图(如上所述)如果仍然应该 - 即,如果ImageView没有再次回收。正如我前面提到的那样
  5. 解决方案虽然有点复杂,但如果正确实施,效果会非常顺利。如果您想要一个示例,请随意查看一个实际操作here in my current project

    编辑所请求的示例:

    这是一些不那么伪的伪代码。您仍然需要做的主要部分实际上是实现延迟加载系统(这是一个完整的其他主题)。再一次,随时访问我上面链接的项目,特别是"ImageLoaderNoCache" NetworkUtil

    public class GetAllCustomerListViewAdapter extends BaseAdapter {
    
        private JSONArray dataArray;
        private Activity activity;
    
        private static LayoutInflater inflater = null;
        private static final String baseUrlForImage = "http://192.168.254.1/contact/images/";
    
        // The list caches bitmaps as well as target imageViews, in order of the rows
        private ArrayList<RowViewData> rowViewDataList;
    
        public GetAllCustomerListViewAdapter(JSONArray jsonArray, Activity a)
        {
            this.dataArray = jsonArray;
            this.activity = a;
    
    
            inflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
            // Initialize the list
            rowViewDataList = new ArrayList<RowViewData>(dataArray.length());
    
            // This is more of a preference, but add in every item for the rowViewDataList
            for(int i = 0; i < dataArray.length(); ++i) {
                // Create a new, empty rowViewData instance and put it into the list
                RowViewData rowData = new RowViewData();
                rowViewDataList.add(rowData);
            }
        }
    
        @Override
        public int getCount() {
            return this.dataArray.length();
        }
    
        @Override
        public Object getItem(int position) {
            return position;
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            // set up convert view if it is null
            final ListCell cell;
            if (convertView == null)
            {
                convertView = inflater.inflate(R.layout.get_all_customer_list_view_cell, null);
                cell = new ListCell();
    
                cell.FullName = (TextView) convertView.findViewById(R.id.customer_full_name);
                cell.Age = (TextView) convertView.findViewById(R.id.customer_age);
    
                cell.mobile = (ImageView) convertView.findViewById(R.id.customer_mobile);
    
                convertView.setTag(cell);
            }
            else
            {
                cell = (ListCell) convertView.getTag();
            }
    
            // change the data of cell
    
            try
            {
                JSONObject jsonObject = this.dataArray.getJSONObject(position);
                cell.FullName.setText(jsonObject.getString("FirstName")+" "+jsonObject.getString("LastName"));
                cell.Age.setText(" "+jsonObject.getInt("Age"));
    
    
                // Get the rowViewData for this position
                RowViewData curRowViewData = rowViewDataList.get(position);
    
                // If the bitmap is already cached, just use it
                if(curRowViewData.bitmap != null) {
                    cell.mobile.setImageBitmap(curRowViewData.bitmap);
                }
                // Otherwise, gotta do some real work
                else {
                    // First, set the cell's imageView's bitmap to null
                    //      [in case this is a recycled view already with a bitmap]
                    cell.mobile.setImageBitmap(null);
    
                    // Then, start up the caching system
                    String nameOfImage = jsonObject.getString("id");
                    String urlForImageInServer = "http://192.168.254.1/contact/getImage.php?id="+nameOfImage;
    
                    // Create a system to load images lazily and then receive the bitmaps
                    // Look at my ImageLoaderNoCache for an example. Feel free to reuse it
                    /**
                     * this = this adapter should implement an interface. Look below for the corresponding method
                     * url = image source url
                     * position = position this bitmap belongs to. Will get returned along with bitmap for reference
                     */
                    LazyImageLoader.GetBitmap(this, url, position);
    
                    // Cache/target this imageView for later
                    curRowViewData.targetImageView = cell.mobile;
    
                    // Double check this imageView is not being targeted already elsewhere
                    //      ie, one of the recycling fixes [else, two bitmaps would load at once, etc]
                    //  Also, probably could be more efficient
                    for(int i = 0; i < rowViewDataList.size(); ++i) {
                        // If this is the current position, then skip all the logic for this round
                        if(i == position) continue;
    
                        // Get the rowViewData for this round
                            // Yeah... I should've used "cell" instead of row for you...
                        RowViewData checkRowData = rowViewDataList.get(i);
    
                        // If the targeted ImageView is the same, null-ify it
                        if(checkRowData.targetImageView == curRowViewData.targetImageView) {
                            // The old one should be null as the bitmap should not load suddenly into
                            //      the current recycled item
                            checkRowData.targetImageView = null;
    
                            // Personally, I knew that there would only be one copy at any time
                            //      so I tried to save time by breaking out here. Feel free to be
                            //          cautious and comment out the break statement
                            break;
                        }
                    }
                }
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
    
    
            return convertView;
        }
    
        /**
         * Should be called as part of an interface with the LazyImageLoader
         * @param bitmap - The lazily loaded bitmap that was requested earlier
         * @param position - The row/cell position that the bitmap was requested for
         */
        public void getBitmapForPosition(Bitmap bitmap, int position) {
            // Get the rowViewData instance for this row/position
            RowViewData curRowData = rowViewDataList.get(position);
    
            // Cache the bitmap
            curRowData.bitmap = bitmap;
    
            // Check if the bitmap should be loaded still
            if(curRowData.targetImageView != null) {
                curRowData.targetImageView.setImageBitmap(bitmap);
    
                // Not sure if strictly necessary, but good practice to make the cached view null now
                curRowData.targetImageView = null;
            }
        }
    
        private  class  ListCell
        {
            private TextView FullName;
            private TextView Age;
    
            private ImageView mobile;
    
        }
    
        // Holds all the data for lazily loading an image with a bitmap
        private class RowViewData {
            public ImageView targetImageView;
            public Bitmap bitmap;
        }
    }