如何使用不同高度的单元格处理GridView?

时间:2014-01-18 12:14:37

标签: android textview android-gridview marquee

注意:好的,我承认标题有点模糊,但英语不是我的主要语言,我不知道如何在一个标准中描述问题。

背景

我正在尝试创建一个在gridView上显示所有应用信息的应用。

gridView将其numColumns设置为auto_fit,以便为所有类型的屏幕很好地设置其列数。

问题

由于每个应用程序的信息有时可能很长,因此单元格高度可能与其旁边的不同。

我希望网格单元具有相同的宽度(在所有情况下均可正常工作),并且对于每一行,高度应由行的单元格的最大高度确定。

这会导致奇怪的事情发生:

  1. 罕见:有些细胞变空了。有时,当滚动并返回时,细胞会被填满......
  2. 很常见:有些细胞汲取其他细胞。
  3. 很常见:有些单元格没有占用空间,留下无法点击的空白区域。
  4. 非常罕见:在使用滚动播放时,如果您一直滚动到顶部,则整个网格都是空的而不能滚动...
  5. 以下是显示#2和#3问题的屏幕截图(它们通常不会一起显示):

    enter image description here

    代码我用过

    这是网格单元格的xml:

    <?xml version="1.0" encoding="utf-8"?>
    <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:background="@drawable/list_divider_holo_dark" >
    
        <ImageView
            android:id="@+id/appIconImageView"
            android:layout_width="48dip"
            android:layout_height="48dip"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:adjustViewBounds="true"
            android:contentDescription="@string/app_icon"
            android:src="@drawable/ic_menu_help" />
    
        <TextView
            android:id="@+id/appLabelTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_toLeftOf="@+id/isSystemAppImageView"
            android:layout_toRightOf="@+id/appIconImageView"
            android:ellipsize="marquee"
            android:text="label"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textStyle="bold"
            tools:ignore="HardcodedText" />
    
        <TextView
            android:id="@+id/appDescriptionTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/appLabelTextView"
            android:layout_below="@+id/appLabelTextView"
            android:layout_toLeftOf="@+id/isSystemAppImageView"
            android:ellipsize="marquee"
            android:text="description"
            tools:ignore="HardcodedText" />
    
        <ImageView
            android:id="@+id/isSystemAppImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/appDescriptionTextView"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:adjustViewBounds="true"
            android:contentDescription="@string/content_description_this_is_a_system_app"
            android:src="@drawable/stat_sys_warning" />
    
    </RelativeLayout>
    

    我尝试了什么

    我尝试了多种可能的解决方案:

    1. 使用Horizo​​ntalListView似乎可以解决这个问题,但是它不允许点击/长按项目。

    2. 我找到了一个很好的库,可以让textViews有一个“选框”效果(here),但是它的许可证并不那么宽松。

    3. 将单元格高度设置为固定高度或将textViews设置为固定行数也可以,但是它会减少(部分)textViews的内容而无法显示它们。

    4. 我也尝试使用linearLayouts而不是RelativeLayout,但它没有帮助。

    5. 一个可能的解决方案是获取gridView上所有视图使用的最大高度(在所有项目上使用getView),类似于this code,但它假设您知道列数,并且你确实希望所有细胞都具有相同的高度。

    6. 问题

      我该如何处理?

      我真的应该使用像StaggeredGrid这样的东西吗?

      有没有办法让textViews保持固定大小但允许他们使用滚动(手动或自动)显示其内容?

3 个答案:

答案 0 :(得分:5)

上述解决方案的替代方法是使用RecyclerViewGridLayoutManager

recyclerView.setHasFixedSize(false);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));

答案 1 :(得分:0)

好的,我找到了一个很好的解决方案,这将使行依赖于他们的孩子,但为此我必须做出一些妥协:

  1. 它是listView的适配器,它的行具有水平线性布局。

  2. 使用此代码的人应设置单元格的选择器,并选择单击或长按时的操作。

  3. 使用此代码的人应准备好为listView的额外最后一个单元格准备一个空单元格。

  4. 使用的listView应将其listSelector设置为透明,以禁止单击行。

  5. 我希望任何人都能做出更好的解决方案,不需要额外的观点。

    样本用法:

    _listAdapter=new GriddedListViewAdapter();
    _listAdapter.setNumberOfColumns(numberOfColumns);
    -listAdapter.setItems(items);
    _listView.setAdapter(_listAdapter);
    

    以下是代码:

    public abstract class GriddedListViewAdapter<ItemType> extends BaseAdapter
      {
      private int                       _numColumns =1;
      private final List<Row<ItemType>> _rows       =new ArrayList<Row<ItemType>>();
      protected final Context           _context    =App.global();
      private List<ItemType>            _items;
    
      public void setItems(final List<ItemType> items)
        {
        _items=items;
        prepareRows();
        notifyDataSetChanged();
        }
    
      private void prepareRows()
        {
        _rows.clear();
        final int itemsCount=_items.size();
        for(int i=0;i<itemsCount;i+=_numColumns)
          {
          final Row<ItemType> row=new Row<ItemType>();
          row.startIndex=i;
          row.items=new ArrayList<ItemType>(_numColumns);
          for(int j=0;j<_numColumns;++j)
            {
            ItemType item;
            if(i+j<itemsCount)
              item=_items.get(i+j);
            else item=null;
            row.items.add(item);
            }
          _rows.add(row);
          }
        }
    
      public void setNumberOfColumns(final int numColumns)
        {
        if(_numColumns==numColumns)
          return;
        _numColumns=numColumns;
        if(_items!=null)
          {
          prepareRows();
          notifyDataSetChanged();
          }
        }
    
      @Override
      public final int getCount()
        {
        return _rows.size();
        }
    
      @Override
      public final Row<ItemType> getItem(final int position)
        {
        return _rows.get(position);
        }
    
      @Override
      public final long getItemId(final int position)
        {
        return position;
        }
    
      @TargetApi(Build.VERSION_CODES.HONEYCOMB)
      @Override
      public final View getView(final int position,final View convertView,final ViewGroup parent)
        {
        final Row<ItemType> row=getItem(position);
        IcsLinearLayout rowLayout=(IcsLinearLayout)convertView;
        if(rowLayout==null)
          {
          rowLayout=new IcsLinearLayout(_context,null);
          rowLayout.setMeasureWithLargestChildEnabled(true);
          rowLayout.setShowDividers(IcsLinearLayout.SHOW_DIVIDER_MIDDLE);
          rowLayout.setDividerDrawable(_context.getResources().getDrawable(R.drawable.list_divider_holo_dark));
          rowLayout.setOrientation(LinearLayout.HORIZONTAL);
          rowLayout.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
          }
        final int childCount=rowLayout.getChildCount();
        for(int i=childCount;i>_numColumns;--i)
          rowLayout.removeViewAt(i-1);
        // reuse previous views of the row if possible
        for(int i=0;i<_numColumns;++i)
          {
          // reuse old views if possible
          final View cellConvertView=i<childCount ? rowLayout.getChildAt(i) : null;
          // fill cell with data
          final View cellView=getCellView(row.items.get(i),row.startIndex+i,cellConvertView,rowLayout);
          LinearLayout.LayoutParams layoutParams=(LinearLayout.LayoutParams)cellView.getLayoutParams();
          if(layoutParams==null)
            {
            layoutParams=new LinearLayout.LayoutParams(0,LayoutParams.MATCH_PARENT,1);
            layoutParams.gravity=Gravity.CENTER_VERTICAL;
            cellView.setLayoutParams(layoutParams);
            }
          else
            {
            final boolean needSetting=layoutParams.weight!=1||layoutParams.width!=0||layoutParams.height!=LayoutParams.MATCH_PARENT;
            if(needSetting)
              {
              layoutParams.width=0;
              layoutParams.height=LayoutParams.MATCH_PARENT;
              layoutParams.gravity=Gravity.CENTER_VERTICAL;
              layoutParams.weight=1;
              cellView.setLayoutParams(layoutParams);
              }
            }
          if(cellConvertView==null)
            rowLayout.addView(cellView);
          }
        return rowLayout;
        }
    
      @Override
      public final int getViewTypeCount()
        {
        return super.getViewTypeCount();
        }
    
      @Override
      public final int getItemViewType(final int position)
        {
        return super.getItemViewType(position);
        }
    
      /**
       * should handle getting a single cell view. <br/>
       * NOTE:read the parameters description carefully !
       * 
       * @param item
       * the item that is associated with the cell. if null, you should prepare an empty cell
       * @param rawPosition the position within the original list of items that is associated with the cell
       * @param convertView
       * a recycled cell. you must use it when it's not null, fill it with data, and return it
       * @param parent
       * the parent of the view. you should use it for inflating the view (but don't attach the view to the
       * parent)
       */
      public abstract View getCellView(ItemType item,int rawPosition,View convertView,ViewGroup parent);
    
      // ////////////////////////////////////
      // Row//
      // /////
      private static class Row<ItemType>
        {
        int                 startIndex;
        ArrayList<ItemType> items;
        }
      }
    

答案 2 :(得分:0)

这是一个有效的解决方案:http://obduro.nl/blog/the-solution-of-android-gridview-overlap/

解决方案解释说,链接是根据所有元素的高度计算每个行高(使用最大高度)。

通过捕获onScrollChanged事件并在FirstVisiblePosition每次更改时更新高度来实现这一点。

经过测试后,除了一个捕获之外,它的工作效果非常好:一旦行大小增加,它就不会减少。

可以在此处找到实际代码:https://github.com/JJdeGroot/AutoGridView