【问题标题】:Android - Very strange behaviour with GridViewAndroid - GridView 的非常奇怪的行为
【发布时间】:2014-07-22 14:08:23
【问题描述】:

考虑到页眉和页脚视图,我正在为我的 gridView 创建一个 ArrayAdapter。

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <GridView
        android:id="@+id/activity_main_gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:numColumns="4" />

</RelativeLayout>

问题是,当我滚动到网格标题/底部视图的顶部或底部时,会越过网格项目,当单击任何项​​目时,它会返回到原始位置(如有必要,我会放截图)

这是我的 GridView 适配器(参数顺序:上下文、网格项目的数组列表(没有页眉/页脚)、所有第一行的页眉视图、所有最后一行的页脚视图、gridview 列号):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mGridView = (GridView) findViewById(R.id.activity_main_gridview);

    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 500; i++) {
        list.add("item " + i);
    }
    View headerView = new View(this);
    headerView.setMinimumHeight(70);
    headerView.setBackgroundColor(Color.argb(255, 63, 81, 181));

    GridHeaderFooterAdapter adapter =
            new GridHeaderFooterAdapter(this, list, headerView, headerView, 4);
    mGridView.setAdapter(adapter);
}

private final class GridHeaderFooterAdapter extends ArrayAdapter<String> {

    private int mNumColumns;
    private int mListSize;

    private List<String> mList;
    private View mHeaderView;
    private View mFooterView;

    private LayoutInflater mInflater;

    public GridHeaderFooterAdapter(ActivityMain context, List<String> list, View headerView, View footerView, int numColumns) {
        super(context, 0);
        this.mNumColumns = numColumns;
        this.mListSize = list.size();
        this.mList = list;
        this.mHeaderView = headerView;
        this.mFooterView = footerView;

        mInflater = context.getLayoutInflater();
    }

    @Override
    public int getCount() {
        int count = 0;
        //headers
        count += mNumColumns;
        //list items
        count += mListSize;
        //footers
        count += mNumColumns + (mNumColumns - mList.size() % mNumColumns);
        Log.w("ActivityMain", "getCount() = " + count);
        return count;
    }

    @Override
    public String getItem(int position) {
        //discard header items
        return mList.get(position - mNumColumns);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //headers
        if (position < mNumColumns){
            Log.w("ActivityMain", "headers: position = " + position);
            return mHeaderView;
        }
        //listitems
        if (position >= mNumColumns && position < mNumColumns + mListSize) {
            Log.w("ActivityMain", "listitems: position = " + position);
            LinearLayout ll = (LinearLayout) mInflater.inflate(R.layout.listitem_main, null);
            TextView tv = (TextView) ll.findViewById(R.id.textView);
            tv.setText(getItem(position));
            return ll;
        }
        //footers
        if (position >= mNumColumns + mListSize){
            Log.w("ActivityMain", "footers: position = " + position);
/*TODO: some footers might be in the same row as listitem, which is wrong because Gridview uses last item of the row to determine row height... */
            return mFooterView;
        }

        return null;
    }
}

滚动前: 滚动后:

如您所见,目标是添加标题以避免第一个项目位于状态栏后面

【问题讨论】:

  • 最好有问题的快照......
  • 你在用你的适配器做一些非常错误的事情。为了帮助我解释,首先你有页脚和页眉的意图是什么?另外,这些屏幕截图中的页眉/页脚视图是什么?
  • @JaySoyer 使用页眉和页脚的目的是避免在将网格滚动到顶部/底部时第一行和最后一行低于状态和导航栏,因为我使用的是半透明状态/导航栏。 . 如您所见,页眉/页脚视图是那个蓝色矩形(应该有很多网格列,但我不知道为什么只有最后一个)
  • 哦哦。我懂了。因此,基本上在滚动期间,您不关心网格项目是否位于状态/导航栏下方,但是一旦您点击滚动的底部/顶部,您就不希望网格项目位于状态/导航栏下方。对吗?
  • @JaySoyer 是的,完全正确...我发现,如果我在 getView 方法中为每个页眉/页脚视图充气,它可以正常工作,但充气布局很昂贵,所以我更喜欢通过 headerView 和footerView...您知道任何解决方法吗?

标签: android android-arrayadapter android-gridview


【解决方案1】:

手动摆弄位置编号是一件危险的事情,但如果操作正确的话很有可能。这里的问题是您试图绕过getView() 方法的运行方式。 getView() 将维护它自己对返回视图的引用并相应地回收或销毁它。它已经进行了极大的优化以提供出色的性能,并且为了实现您的最终目标,您希望使用它......而不是反对。

最终,您希望 GridView 中的第一行(页眉)和最后一行(页脚)有一个间距,以避免与半透明状态/导航栏重叠。我会给你一个更高的概述,了解需要发生的事情。

getCount()需要返回:

mListSize + (mNumColumns * 2)

为什么?因为您需要为每一列提供一个页眉视图,并为每一列提供一个页脚视图。

getItem() 需要为所有可能的位置返回一些东西。这意味着getCount() 返回的任何值都需要能够返回一些东西……即使它是一个空字符串。所以像:

// if a header position or a footer position
if (position < mNumColumns || position > mListSize + mNumColumns) {
    return ""
} else {
    return mList.get(position - mNumColumns);
}

注意,考虑到计数不是从零开始的,但位置是......但它显示了您需要的基本要点。

getView() 需要正常渲染视图。不同之处在于,如果位置编号指示页眉/页脚位置,则返回空白视图。比如:

ViewHolder vh = null;
if (convertView == null) {
    //inflate your view
    convertView.setTag(vh);
} else {
    vh = (ViewHolder) convertView;
}

//If position is indicative of a header/footer. Alternatively you could see if getItem() returns an empty string...assuming your data can never contain an empty string.
 if (position < mNumColumns || position > mListSize + mNumColumns) {
    //Don't fill the view with any data. Make any visible elements INVISIBLE
 } else {
    //populate convertView with data
 }

摆脱将视图传递给适配器使用的整个概念。那将破坏一切。如果性能非常受关注,那么更好的选择(以及正确的选择......比我发布的更多)是使用getItemViewType()getViewTypeCount()。但是,这将要求您创建一个可以表示您的实际数据(例如字符串)的类对象和一个表明它是页眉、页脚和数据的标志。

【讨论】:

  • 试过了,但唯一能让它工作的方法是在 getView 方法中膨胀标题,如果 convertView == null...为什么我不能简单地将它作为参数传递?
  • 因为在内部,对于适配器,Android 将确定 View 何时被销毁……或回收以用于不同的位置编号。这特别发生在您滚动并且视图离开屏幕时。
  • 好的,感谢您的回复。你让我了解了更多关于适配器的事情
猜你喜欢
  • 2016-03-13
  • 2011-03-01
  • 2020-12-02
  • 1970-01-01
  • 1970-01-01
  • 2018-04-20
  • 2020-02-09
  • 1970-01-01
  • 2016-12-04
相关资源
最近更新 更多