首页 文章

在Android(初级水平)Listview上延迟加载图像? [重复]

提问于
浏览
46

可能重复:Android - 如何在ListView中延迟加载图像

我正在使用自定义适配器处理listview . 我想加载图像和文本视图 . 图像是从互联网网址加载的 . 我只是想显示可见列表项的图像以供用户使用 . 我提到Shelves opensource project example from romainguy,但是要理解代码很复杂 . 对于初学者级别,我只想知道如何处理适配器和活动之间的标记 . 从commonsware example我可以在适配器上设置标签,但无法在列表视图的空闲状态下显示相应的图像 . 请帮我解决你的想法 . 示例代码更容易理解 .

谢谢 .

编辑:

ApiDemos中EfficientSlow适配器的组合更有助于理解 .

对高效适配器示例所做的更改如下:

public class List14 extends ListActivity implements ListView.OnScrollListener {
// private TextView mStatus;

private static boolean mBusy = false;
static ViewHolder holder;

public static class EfficientAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    private Bitmap mIcon1;
    private Bitmap mIcon2;

    public EfficientAdapter(Context context) {
        // Cache the LayoutInflate to avoid asking for a new one each time.
        mInflater = LayoutInflater.from(context);

        // Icons bound to the rows.
        mIcon1 = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.icon48x48_1);
        mIcon2 = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.icon48x48_2);
    }

    /**
     * The number of items in the list is determined by the number of
     * speeches in our array.
     * 
     * @see android.widget.ListAdapter#getCount()
     */
    public int getCount() {
        return DATA.length;
    }

    /**
     * Since the data comes from an array, just returning the index is
     * sufficent to get at the data. If we were using a more complex data
     * structure, we would return whatever object represents one row in the
     * list.
     * 
     * @see android.widget.ListAdapter#getItem(int)
     */
    public Object getItem(int position) {
        return position;
    }

    /**
     * Use the array index as a unique id.
     * 
     * @see android.widget.ListAdapter#getItemId(int)
     */
    public long getItemId(int position) {
        return position;
    }

    /**
     * Make a view to hold each row.
     * 
     * @see android.widget.ListAdapter#getView(int, android.view.View,
     *      android.view.ViewGroup)
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        // A ViewHolder keeps references to children views to avoid
        // unneccessary calls
        // to findViewById() on each row.

        // When convertView is not null, we can reuse it directly, there is
        // no need
        // to reinflate it. We only inflate a new View when the convertView
        // supplied
        // by ListView is null.
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item_icon_text,
                    null);

            // Creates a ViewHolder and store references to the two children
            // views
            // we want to bind data to.
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.text);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);

            convertView.setTag(holder);
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();
        }
        if (!mBusy) {
            holder.icon.setImageBitmap(mIcon1);

            // Null tag means the view has the correct data
            holder.icon.setTag(null);
        } else {
            holder.icon.setImageBitmap(mIcon2);

            // Non-null tag means the view still needs to load it's data
            holder.icon.setTag(this);
        }
        holder.text.setText(DATA[position]);

        // Bind the data efficiently with the holder.
        // holder.text.setText(DATA[position]);

        return convertView;
    }

    static class ViewHolder {
        TextView text;
        ImageView icon;
    }
}

private Bitmap mIcon1;
private Bitmap mIcon2;
@Override
public void onCreate(Bundle savedInstanceState) {
    mIcon1 = BitmapFactory.decodeResource(this.getResources(),
            R.drawable.icon48x48_1);
    mIcon2 = BitmapFactory.decodeResource(this.getResources(),
            R.drawable.icon48x48_2);
    super.onCreate(savedInstanceState);
    setListAdapter(new EfficientAdapter(this));
    getListView().setOnScrollListener(this);
}

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

public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState) {
    case OnScrollListener.SCROLL_STATE_IDLE:
        mBusy = false;

        int first = view.getFirstVisiblePosition();
        int count = view.getChildCount();
        for (int i = 0; i < count; i++) {

            holder.icon = (ImageView) view.getChildAt(i).findViewById(
                    R.id.icon);
            if (holder.icon.getTag() != null) {
                holder.icon.setImageBitmap(mIcon1);
                holder.icon.setTag(null);
            } 
        }

        // mStatus.setText("Idle");
        break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        mBusy = true;
        // mStatus.setText("Touch scroll");
        break;
    case OnScrollListener.SCROLL_STATE_FLING:
        mBusy = true;
        // mStatus.setText("Fling");
        break;
    }
}
private static final String[] DATA = { "Abbaye de Belloc",
        "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
        "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu"};
}

它现在工作正常 . 但是当滚动状态时它没有正确地重新加载图像 . 某些项目间隔未显示图像2 . 这是加载图像的正确顺序 . 但不是列表中的所有项目 . 固体项间隔之间发生不匹配 . 怎么纠正呢?

5 回答

  • 0

    Praveen -

    正如您已经在此发现我的博客文章,我只是想将其推回到Stackoverflow,以便其他人可以使用它 .

    以下是基本讨论:http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

    还有一个我后来记录的类,它使用一个线程和一个回调来加载图像:

    http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/

    Update: 为了解决您的特定异常,我认为 getChildAt 列表中返回的视图不是 ImageView - 它是用于保存图像和文本的任何布局视图 .

    Update to include relevant code :( Per @ george-stocker的推荐)

    这是我使用的适配器:

    public class MediaItemAdapter extends ArrayAdapter<MediaItem> {
      private final static String TAG = "MediaItemAdapter";
      private int resourceId = 0;
      private LayoutInflater inflater;
      private Context context;
    
      private ImageThreadLoader imageLoader = new ImageThreadLoader();
    
      public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems) {
        super(context, 0, mediaItems);
        this.resourceId = resourceId;
        inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.context = context;
      }
    
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
    
        View view;
        TextView textTitle;
        TextView textTimer;
        final ImageView image;
    
        view = inflater.inflate(resourceId, parent, false);
    
        try {
          textTitle = (TextView)view.findViewById(R.id.text);
          image = (ImageView)view.findViewById(R.id.icon);
        } catch( ClassCastException e ) {
          Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
          throw e;
        }
    
        MediaItem item = getItem(position);
        Bitmap cachedImage = null;
        try {
          cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener() {
          public void imageLoaded(Bitmap imageBitmap) {
          image.setImageBitmap(imageBitmap);
          notifyDataSetChanged();                }
          });
        } catch (MalformedURLException e) {
          Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e);
        }
    
        textTitle.setText(item.name);
    
        if( cachedImage != null ) {
          image.setImageBitmap(cachedImage);
        }
    
        return view;
      }
    }
    
  • 7

    我知道了 . 这是我想要的完美代码 . 延迟加载适用于自定义适配器只是可见列表项的图标 . 希望它对初学者有所帮助

    public class List14 extends ListActivity implements ListView.OnScrollListener {
    // private TextView mStatus;
    
    private static boolean mBusy = false;
    static ViewHolder holder;
    
    public static class EfficientAdapter extends BaseAdapter {
        private LayoutInflater mInflater;
        private Bitmap mIcon1;
        private Bitmap mIcon2;
        private Context mContext;
    
        public EfficientAdapter(Context context) {
            // Cache the LayoutInflate to avoid asking for a new one each time.
            mInflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mContext = context;
            // Icons bound to the rows.
            mIcon1 = BitmapFactory.decodeResource(context.getResources(),
                    R.drawable.icon48x48_1);
            mIcon2 = BitmapFactory.decodeResource(context.getResources(),
                    R.drawable.icon48x48_2);
        }
    
        /**
         * The number of items in the list is determined by the number of
         * speeches in our array.
         * 
         * @see android.widget.ListAdapter#getCount()
         */
        public int getCount() {
            return DATA.length;
        }
    
        /**
         * Since the data comes from an array, just returning the index is
         * sufficent to get at the data. If we were using a more complex data
         * structure, we would return whatever object represents one row in the
         * list.
         * 
         * @see android.widget.ListAdapter#getItem(int)
         */
        public Object getItem(int position) {
            return position;
        }
    
        /**
         * Use the array index as a unique id.
         * 
         * @see android.widget.ListAdapter#getItemId(int)
         */
        public long getItemId(int position) {
            return position;
        }
    
        /**
         * Make a view to hold each row.
         * 
         * @see android.widget.ListAdapter#getView(int, android.view.View,
         *      android.view.ViewGroup)
         */
        public View getView(int position, View convertView, ViewGroup parent) {
            // A ViewHolder keeps references to children views to avoid
            // unneccessary calls
            // to findViewById() on each row.
    
            // When convertView is not null, we can reuse it directly, there is
            // no need
            // to reinflate it. We only inflate a new View when the convertView
            // supplied
            // by ListView is null.
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text,
                        parent, false);
    
                // Creates a ViewHolder and store references to the two children
                // views
                // we want to bind data to.
                holder = new ViewHolder();
                holder.text = (TextView) convertView.findViewById(R.id.text);
                holder.icon = (ImageView) convertView.findViewById(R.id.icon);
    
                 convertView.setTag(holder);
            } else {
                // Get the ViewHolder back to get fast access to the TextView
                // and the ImageView.
                 holder = (ViewHolder) convertView.getTag();
            }
    
            if (!mBusy) {
    
                holder.icon.setImageBitmap(mIcon1);
    
                // Null tag means the view has the correct data
                holder.icon.setTag(null);
    
            } else {
                holder.icon.setImageBitmap(mIcon2);
    
                // Non-null tag means the view still needs to load it's data
                holder.icon.setTag(this);
            }
            holder.text.setText(DATA[position]);
    
            // Bind the data efficiently with the holder.
            // holder.text.setText(DATA[position]);
    
            return convertView;
        }
    
        static class ViewHolder {
            TextView text;
            ImageView icon;
        }
    }
    
    private Bitmap mIcon1;
    private Bitmap mIcon2;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        mIcon1 = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.icon48x48_1);
        mIcon2 = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.icon48x48_2);
        super.onCreate(savedInstanceState);
        setListAdapter(new EfficientAdapter(this));
        getListView().setOnScrollListener(this);
    }
    
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
    }
    
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            mBusy = false;
    
            int first = view.getFirstVisiblePosition();
            int count = view.getChildCount();
    
            for (int i = 0; i < count; i++) {
    
                holder.icon = (ImageView) view.getChildAt(i).findViewById(
                        R.id.icon);
                if (holder.icon.getTag() != null) {
                    holder.icon.setImageBitmap(IMAGE[first+i]);// this is the image url array.
                    holder.icon.setTag(null);
                }
            }
    
            // mStatus.setText("Idle");
            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            mBusy = true;
            // mStatus.setText("Touch scroll");
            break;
        case OnScrollListener.SCROLL_STATE_FLING:
            mBusy = true;
            // mStatus.setText("Fling");
            break;
        }
    }
    
    private static final String[] DATA = { "Abbaye de Belloc",
            "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
            "Yarra Valley Pyramid", "Yorkshire Blue", "Zamorano",
            "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" };
      }
    
  • 0

    据我了解,滚动完成后需要更新列表 . 这很简单 . 这是您的固定代码:

    EfficientAdapter adapter;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        mIcon1 = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.icon48x48_1);
        mIcon2 = BitmapFactory.decodeResource(this.getResources(),
                R.drawable.icon48x48_2);
        super.onCreate(savedInstanceState);
        adapter=new EfficientAdapter(this);
        setListAdapter(adapter);
        getListView().setOnScrollListener(this);
    }
    
    public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount) {
    }
    
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            mBusy = false;
            adapter.notifyDataSetChanged() 
            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            mBusy = true;
            // mStatus.setText("Touch scroll");
            break;
        case OnScrollListener.SCROLL_STATE_FLING:
            mBusy = true;
            // mStatus.setText("Fling");
            break;
        }
    }
    

    notifyDataSetChanged将告诉适配器重新显示所有可见项,因此它们将与image2一起显示 .

  • 17

    据我所见,静态 ViewHolder 没有任何帮助 . 尝试将整个 onScrollStateChanged 函数放在 /**/ 之间,删除 static ViewHolder 行,并将 holder = new ViewHolder(); 更改为 ViewHolder holder = new ViewHolder(); .

  • 7

    啊,检查你的logcat以确保你的应用程序没有被杀死并重新启动 . 大多数手机将您的总应用程序大小限制为16mb或24mb . 很容易加载一堆图像,运行,杀死,重新启动,并让你的onPause不会在屏幕上加载大数据 . 这是穷人的收集 .

相关问题