博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android之GridView控制显示多少行以及遇到的怪事
阅读量:6784 次
发布时间:2019-06-26

本文共 5149 字,大约阅读时间需要 17 分钟。

前段时间接到一个需求,要求GridView超过两行只显示两行多余的不显示。但是GridView没有设置多少行的api,只有设置多少列的方法,到处查找资料都类似的case,stakeoverfrow上面也没什么有价值的答案,不过在百度知道竟然看到了一个思路。

知道:http://zhidao.baidu.com/link?url=f-F4MnuKApNqgTzcW0r7nUDljHch3v-iGV7LspkMFW97ftxgh0JJSwhdRYkipzK4zdyBoWfJZ9ZakJoAnBppKq

 

第一个方案:说定死列数,算出总数,只有显然不科学,因为我的gridview列数十需要autofit的,随手机屏幕多宽就相应显示多少列。

gridview的xml属性 android:numColumns="6"在adapter里重写@Override public int getCount() { return 12; }这样应该行吧

第二个方案:限定gridview高度,因为项目时间很紧所以脑子里也曾蹦出这个想法,试着写死一个固定高度,但事实上这高度是不起作用的,因为它是根据adapte的条目来计算的。

第三个方案:其实就是第一个方案改良版,虽然是很朴素的思路但是可以改活。在探索过程中发现gridview在高版api中有一个获取列数的方法,如果知道到列数那就好办了,列数*2就得到了要显示两行的count,这样既满足列数自适应又很优雅的控制要显示的区域。

=====================================================================

于是代码可以写下去了:

int numberOfColumns = mGridView.getNumColumns();if (numberOfColumns > 0) {
//adapter item 数据已填充 int countOf2Row = numberOfColumns * 2; for (int i = count2Row - 1; i < couponList.size(); i++) mDataSources.remove(i);//多出2列的数据cut掉 if (mAdapter != null) mAdapter.notifyDataSetChanged();}

这个if死活没进去啊,打印出来看到获取到的NumColumn是-1,然后看到方法doc:

Get the number of columns in the grid. * Returns {@link #AUTO_FIT} if the Grid has never been laid out.

我明明是在adapte.notifyDatasetChanged()方法之后去获取的啊,怎么还是拿不到,查阅了一些资料之后就监听gridview布局变化,在listener里面果然能获取到了。

刚才说到getNumColumns()是高版方法,API11以上才有,所以还得back compat。这个只要网上搜索一下一大把,我也是摘抄了一段。

/**     * 获取列数的兼容方法.     * @return     */    public int getNumColumnsCompat() {        if (Build.VERSION.SDK_INT >= 11) {            return getNumColumnsCompat11();        } else {            int columns = 0;            int children = getChildCount();            if (children > 0) {                int width = getChildAt(0).getMeasuredWidth();                if (width > 0) {                    columns = getWidth() / width;                }            }            return columns > 0 ? columns : AUTO_FIT;        }    }    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    private int getNumColumnsCompat11(){        return getNumColumns();    }

那最后代码变成这样了:

mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                int numberOfColumns = ((MyGridView) mGridView).getNumColumnsCompat();                if (numberOfColumns > 0) {
//adapte item 数据已填充 int count2Row = numberOfColumns * 2; for (int i = count2Row - 1; i < couponList.size(); i++) list.remove(i);//多出2列的数据cut掉 if (mAdapter != null) mAdapter.notifyDataSetChanged(); ApiVersionCompat.removeOnGlobalLayoutListener(mGridView.getViewTreeObserver(), this); } } });

注意在onGlobalLayout处理好移除数据的逻辑之后需要移除监听器哦,移除监听也有一个版本差异,代码送上:

public static void removeOnGlobalLayoutListener(ViewTreeObserver observer, ViewTreeObserver.OnGlobalLayoutListener victim) {        if (Build.VERSION.SDK_INT >= 16) {            observer.removeOnGlobalLayoutListener(victim);        } else {            observer.removeGlobalOnLayoutListener(victim);        }    }

本以为就perfect了事了,feature也实现了,资源也节约了(因GroblaLayoutListener会多次触发,所以一定要做好限制,用完就释放掉不要让代码重复执行了)。代码交上去,apk发给测试测了之后bug就丢过来了,测试出来说还是有不止两行的grid出现啊,我心里怎么也不相信啊,怎么可能我明明自测过的。

当我重新打开app,发现确实有超出2行多出一个Item掉在第三行,后来就怀疑是不是cut数据没控制好,代码明明是for (int i = count2Row - 1; i < list.size(); i++)这样循环啊。每次都多出一个,难道i<=list.size(),我也曾很多次怀疑过怎么可能会list.remove(list.size()),不可能不可能不可能啊,这样绝对会数组越界啊,难不成是i初始还要count2Row-2,试过之后还是如此多一个(因为总数是有9个,从第六个开始剪掉2个也还是有一个)。

log出size让我惊讶了,size竟然是6,但接口出来明明是9啊。然后就猛然一醒,加载数据和触发布局改变事件应该是异步的,也就是说当我在布局改变事件中获取到列数>0时,并不一定数据全部加载完,而这时我已经移除监听了。当加载到第1行数据的时候就可以获取到列数了,恰好此时我移除了其中几个(如果第一次获取到的size大于2列之和小于最终size),但当处理完这块逻辑的同时数据其实还是在加载的,虽然都是微乎其微的瞬间,但也有先来后到的顺序,所以移除掉一部分后面又添加了一部分,结果就出现了超过两行的情况。

 

mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                int numberOfColumns = ((MyGridView) mGridView).getNumColumnsCompat();//有可能每次会有变动                if (numberOfColumns > 0) {
//adapter item 数据已填充 int countOf2Row = numberOfColumns * 2; if (list.size() > countOf2Row) { //超出2行(第三行开始)的数据cut掉 for (int i = countOf2Row; i < list.size(); i++) { mAdapter.remove(i); } if (mAdapter != null) mAdapter.notifyDataSetChanged(); } else { //每次检测到数据集合大小<=countOf2Row的时候就可以移除监听了 ApiVersionCompat.removeOnGlobalLayoutListener(mGridView.getViewTreeObserver(), this); } } } });

好了,代码写好了,自测一下也都OK了,多测几个不一样的数据也正常了。可该死的,不小心按到了home键,当打开recent列表点回app的时候发现大事不好,又整整齐齐得出现三行满满的。

原来这个界面是会在onResure刷新数据的,View都还是那些View,但是数据会重新添加,第一次完美显示2行,但数据一刷新,就会先clear掉数据源重新添加数据,而此时的GlobalLayoutListener已经移除了无法监听处理多出2行的逻辑了,所以如上图所示,完完整整的出现了3行数据。因为我addOnGlobalLayoutListener是在initViews时候去注册的,当数据加载完毕就移除监听,原来以为很完美没想到会有这种情况发生。那知道了问题在哪就好对症下药了,直接把添加监听的方法放在拿到数据后执行就好了,每次到数据就监听处理,处理完了就移除监听,下次数据来了依然也可以依次执行。

代码就不贴了,说的很清楚了,此处应扫二维码关注公众号了!

 

 

 

转载地址:http://xzdgo.baihongyu.com/

你可能感兴趣的文章
linux X界面 输入密码正确,但是无法登陆系统,命令行界面可以登陆
查看>>
杨中科老师-C语言也能干大事链接
查看>>
查看linux分区占用空间情况
查看>>
理解flexible.js所需的viewport知识
查看>>
rman 操作
查看>>
5种最流行的IO策略
查看>>
自反ACL(2)
查看>>
MySQL基础【MySQL运维实践】
查看>>
人工智能教程001:什么是人工智能以及相关知识要求
查看>>
30Mysql 的配置
查看>>
关于摄影的技巧,摄影爱好者们都好好学习吧
查看>>
Mac tips - 隐藏窗口及恢复
查看>>
dvbbs论坛的安装
查看>>
linux管道
查看>>
Apache web目录修改
查看>>
Android存储子系统
查看>>
kafka学习笔记五
查看>>
CentOS 6 VNC详细配置
查看>>
html返回json响应键盘?
查看>>
Ubuntu SSH Algorithm negotiation failed
查看>>