如何处理具有不同高度单元格的GridView? [英] How to handle GridView with cell of different heights?

查看:26
本文介绍了如何处理具有不同高度单元格的GridView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:好吧,我承认标题有点含糊,但英语不是我的主要语言,我不确定如何用一句话描述问题.

NOTE: OK, I admit the title is a bit vague, but English is not my main language, and I'm not sure how to describe the problem in one sentance.

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

I'm trying to create an app that shows all apps information on a gridView.

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

The gridView has its numColumns set to auto_fit , so that its number of columns will be set nicely for all types of screens.

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

Since each app's information can sometimes be long, the cell height can be different from those that are next to it.

我希望网格单元的宽度相同(适用于所有情况),并且对于每一行,高度应由该行单元格的最大高度确定.

I want the grid cells to be of the same width (which works fine on all cases), and for each row, the height should be determined by the max height of the row's cells.

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

This causes weird things to occur:

  1. 罕见:一些单元格变空了.有时,当滚动并返回那里时,单元格会被填满......
  2. 很常见:一些单元格会覆盖其他单元格.
  3. 很常见:有些单元格没有占用它们可以占用的空间,留下无法点击的空白空间.
  4. 非常罕见:在玩滚动时,如果一直滚动到顶部,则整个网格都为空且不可滚动......

这是显示 #2 和 #3 问题的屏幕截图(它们通常不会一起显示):

Here is a screenshot showing both #2 and #3 issues (they don't usually show together) :

这是网格单元的xml:

This is the xml of the grid cell :

<?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>

我的尝试

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

What I've tried

I've tried multiple possible solutions:

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

  1. using HorizontalListView seems to solve this, but then it doesn't allow clicking/long clicking on items.

我找到了一个不错的库,可以使 textView 具有字幕"效果(here) ,但它的许可并不那么宽松.

I've found a nice library for making the textViews have a "marquee" effect (here) , but its license isn't so permissive.

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

Setting the cell height as fixed one or setting the textViews to have fixed lines count also work, but then it cuts down (part of) the content of the textViews without the ability to show them.

我也尝试过使用linearLayouts代替RelativeLayout,但没有帮助.

I've also tried using linearLayouts instead of RelativeLayout, but it didn't help.

一个可能的解决方案是获取 gridView 上所有视图使用的最大高度(在所有项目上使用 getView),类似于 这段代码,但它假设您知道列数,并且您确实希望所有单元格都具有相同的高度.

a possible solution is to get the max height used by all views on the gridView (using getView on all items) , similar to this code, but it assumes you know the number of columns , and that you do want all cells to be of the same height.

问题

我该如何处理?

我真的应该使用 StaggeredGrid 之类的东西吗?

Should I really use something like StaggeredGrid ?

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

Is there a way to put the textViews to stay fixed in size yet allow them to show their content using scrolling (manually or automatically) ?

推荐答案

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

OK, i've found a nice solution for this, which will make the rows depend on their children, but for this I had to make some compromises :

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

  1. it's an adapter for a listView , which has horizontal linear layouts for its rows.

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

whoever uses this code should set the selector of the cells and choose what to do on clicking or long clicking.

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

whoever uses this code should be ready to prepare an empty cell for the extra last cells of the listView.

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

the listView that is used should have its listSelector set to transparent, to disallow clicking on rows.

我希望任何人都可以提出一个不需要额外视图的更好的解决方案.

I hope anyone could make a better solution that won't need extra views.

示例用法:

_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;
    }
  }

这篇关于如何处理具有不同高度单元格的GridView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆