【问题标题】:List View Item Recycling列表视图项目回收
【发布时间】:2013-11-23 06:31:16
【问题描述】:

我有一个包含 14 行的列表视图,每个项目都作为 View Pager。这是我的 getView() 方法:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        FindMyBeerPager findMyBeerPager;
        if(convertView==null){
            holder = new ViewHolder();
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.beer_list_item, parent,false);

            findMyBeerPager = new FindMyBeerPager(context, findBeerDataList.get(position));
            holder.beerPager = (ViewPager)convertView.findViewById(R.id.mypager);
            holder.beerPager.setAdapter(findMyBeerPager);

            System.out.println("Recycling view position"+position);
            holder.beerPager.setTag(findMyBeerPager);

            convertView.setTag(holder);
        }
        else{
            holder = (ViewHolder) convertView.getTag();
            System.out.println("Recycled"+position);
        }

        return convertView; 
    }
}

class ViewHolder{
    ViewPager beerPager;
}

问题:我面临的问题是,当我向下滚动列表视图时,我没有得到更多的行值,即列表视图与以前的值一起回收。我不知道为什么会这样。我必须发送 View Pager(每个位置的数据项)。在向下滚动之前一切正常。

【问题讨论】:

    标签: android android-listview android-viewpager


    【解决方案1】:

    将 getView 更改为

     @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            FindMyBeerPager findMyBeerPager;
            if(convertView==null){
                holder = new ViewHolder();
                LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.beer_list_item, parent,false);
                holder.beerPager = (ViewPager)convertView.findViewById(R.id.mypager);
                convertView.setTag(holder);
            }
            else{
                holder = (ViewHolder) convertView.getTag();
                }
    
             findMyBeerPager = new FindMyBeerPager(context, findBeerDataList.get(position));
                 holder.beerPager.setTag(findMyBeerPager);
                 holder.beerPager.setAdapter(findMyBeerPager);
            return convertView; 
        }
    }
    

    你可以将LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);移动到适配器类的构造函数中。

    还可以使用Log 代替 System.out.println 进行日志记录

    How ListView's recycling mechanism works

    ViewPager and OnItemClickListener in ListView

    查看上面链接中 Dianne Hackborn 的答案。

    【讨论】:

    • 我知道这个解决方案,但是使用这个解决方案并不顺利,因为每次都在设置适配器
    • @GauravArora 列表视图回收视图,这就是滚动时看不到任何内容的原因。如果您希望数据显示在 lisview 中,则需要每次都进行设置。
    • 我理解你,但一次又一次地设置适配器使列表视图难以滚动。或者您能告诉我另一种将视图寻呼机添加为列表视图中的每个项目的方法吗?
    • 我终于在 View Flipper 的帮助下解决了我的问题。谢谢
    【解决方案2】:

    您可以尝试在 esle 块中设置适配器吗?

      @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            FindMyBeerPager findMyBeerPager;
            if(convertView==null){
                holder = new ViewHolder();
                LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.beer_list_item, parent,false);
    
                holder.beerPager = (ViewPager)convertView.findViewById(R.id.mypager);
    
                System.out.println("Recycling view position"+position);
                holder.beerPager.setTag(findMyBeerPager);
    
                convertView.setTag(holder);
            }
            else{
                holder = (ViewHolder) convertView.getTag();
                System.out.println("Recycled"+position);
                findMyBeerPager = new FindMyBeerPager(context, findBeerDataList.get(position));
                holder.beerPager.setAdapter(findMyBeerPager);
            }
    
            return convertView; 
        }
    }
    

    【讨论】:

    • 我知道这个解决方案,但是使用这个解决方案并不顺利,因为每次都在设置适配器
    【解决方案3】:

    它可以在每个 getView() 上不设置参数的情况下工作,但为此你需要扩展 BaseAdapter 并重载以下方法:

    @Override
    public boolean hasStableIds() {
        return true;
    }
    
    @Override
    public int getCount() {
        return items.size();
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public int getItemViewType(int position) {
        return position;
    }
    
    @Override
    public int getViewTypeCount() {
        return MAXIMUM_NUMBER_OF_ITEMS_IN_LIST_VIEW;
    }
    

    这样,BaseAdapter 将在回收器中为每个元素保留单独的视图。对于每个项目都有自定义视图的短列表来说,这是一个很好的解决方案。 (但在膨胀视图相同时也可以工作)

    请注意,getViewTypeCount() 仅在构造函数中调用一次,因此 BaseAdapter 必须知道您的列表将包含多少项。

    这个想法的关键是 getItemViewType() 为每个项目返回不同的值。由于 getView() 将为每个列表项获得唯一的 convertView。

    然后就可以完全跳过条件(convertView != null),将所有代码放入(convertView == null){}

    【讨论】:

    • @Makachiasz。很好的答案,但是当我将 getViewTypeCount 设置为 arraylist.size(); ,然后它给了我异常-“getViewCount”不能小于1。如果我对我的ViewCount值进行硬编码,那么它运行正常。我不想硬编码它。请告诉我这样做的方法
    • 你明白我的问题了吗?
    • 正如我所写,getViewTypeCount() 必须是硬编码的,因为它在 BaseAdapter 的构造函数内部用于创建视图数组(View views[getViewTypeCount()])。因此,或者您可以尝试传递 MAXIMUM_NUMBER_OF_ITEMS_IN_LIST_VIEW 的值;在 BaseAdapter 构造函数的开头(在 super 之前)。就我个人而言,我有 MAXIMUM_NUMBER_OF_ITEMS_IN_LIST_VIEW = 100,而且我从来没有更多的项目。这个数字不应该太大,因为 Views 数组会消耗内存。甚至是空视图。
    • 没关系。那么这个解决方案对我没有帮助,因为我没有固定数量的值。谢谢
    • 但是如果你知道你的最大值是多少并且它不是很大,那么你可以硬编码它。 getViewTypeCount() 的数量可以大于 ListView 中的项目数。