使用GridView的auto_fit遇到的坑

给项目进行平板的适配。找到平板运行了一下,看看效果,基本问题不大。只是对于首页的GridView显示的列数需要改一下。原先我是使用android:numColumns="3",一行显示3列。那么运行在了平板上就显得间隔太大,所以我准备显示4列。


那么我立马想到的是使用:

android:numColumns="auto_fit"

android:columnWidth="120dp"

大家都清楚这是设置GridView列宽并尽可能的填满可用空间。会随着屏幕的大小自动调节列数的。


运行在了手机与平板上以后,手机显示3列正常,两个平板,一个4列,一个5列。抓狂

无语了。。明明两个平板基本一样大啊!只是分辨率不同。一个1080*1920,一个1200*1920。

那么没办法,只有查看一下GridView源码,看看auto_fit怎么实现的。

找了一会发现了determineColumns方法,就如名字一样“确定列数”

private boolean determineColumns(int availableSpace) {
        final int requestedHorizontalSpacing = mRequestedHorizontalSpacing;
        final int stretchMode = mStretchMode;
        final int requestedColumnWidth = mRequestedColumnWidth;
        boolean didNotInitiallyFit = false;
        
        if (mRequestedNumColumns == AUTO_FIT) {
            if (requestedColumnWidth > 0) {
                // Client told us to pick the number of columns
                mNumColumns = (availableSpace + requestedHorizontalSpacing) /
                        (requestedColumnWidth + requestedHorizontalSpacing);
            } else {
                // Just make up a number if we don't have enough info
                mNumColumns = 2;
            }
        } else {
            // We picked the columns
            mNumColumns = mRequestedNumColumns;
        }
        
        if (mNumColumns <= 0) {
            mNumColumns = 1;
        }

这里只截取了部分。写得很清楚,如果没有设置auto_fit那么就用设置的固定值,如果固定值小于等于0,列数就是1。如果设置了auto_fit,但是没有设置列宽,默认两列。有设置列宽,则根据availableSpace除列宽计算列数。

追踪availableSpace

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Sets up mListPadding
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.UNSPECIFIED) {
            if (mColumnWidth > 0) {
                widthSize = mColumnWidth + mListPadding.left + mListPadding.right;
            } else {
                widthSize = mListPadding.left + mListPadding.right;
            }
            widthSize += getVerticalScrollbarWidth();
        }
        
        int childWidth = widthSize - mListPadding.left - mListPadding.right;
        boolean didNotInitiallyFit = determineColumns(childWidth);

由上面可以看到availableSpace是GridView的宽度,单位是px,我们设置的宽度单位是dp。也就是第一段的requestColumnWidth

追踪requestColumnWidth

    /**
     * Set the width of columns in the grid.
     *
     * @param columnWidth The column width, in pixels.
     *
     * @attr ref android.R.styleable#GridView_columnWidth
     */
    public void setColumnWidth(int columnWidth) {
        if (columnWidth != mRequestedColumnWidth) {
            mRequestedColumnWidth = columnWidth;
            requestLayoutIfNecessary();
        }
    }

追踪columnWidth

int columnWidth = a.getDimensionPixelOffset(R.styleable.GridView_columnWidth, -1);
        if (columnWidth > 0) {
            setColumnWidth(columnWidth);
        }

那么到这里很清楚了,dp转化为了px。

dp转化为px这个工具方法大家一定没有少用,如下:

/**
	 * * 将dip或dp值转换为px值,保证尺寸大小不变 * * @param dipValue * @param scale *
	 * (DisplayMetrics类中属性density) * @return
	 */
	public static int dip2px(Context context, float dipValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dipValue * scale + 0.5f);
	}

这里有个destiny,这个是什么。老规矩查看源码:

/**
     * The logical density of the display.  This is a scaling factor for the
     * Density Independent Pixel unit, where one DIP is one pixel on an
     * approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), 
     * providing the baseline of the system's display. Thus on a 160dpi screen 
     * this density value will be 1; on a 120 dpi screen it would be .75; etc.
     *  
     * <p>This value does not exactly follow the real screen size (as given by 
     * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
     * the overall UI in steps based on gross changes in the display dpi.  For 
     * example, a 240x320 screen will have a density of 1 even if its width is 
     * 1.8", 1.3", etc. However, if the screen resolution is increased to 
     * 320x480 but the screen size remained 1.5"x2" then the density would be 
     * increased (probably to 1.5).
     *
     * @see #DENSITY_DEFAULT
     */
    public float density;

大概意思是说他是一个相对的屏幕密度。在160dpi的屏幕上1dp=1px,也就是mdpi。hdpi时1dp=1.5px,xhdpi时1dp=2px,xxhdpi时1dp=3px。

总结一下:px与dp之间的倍数就是density,而density的值由分辨率和屏幕尺寸决定。


之后我打Log看看手机和平板的density。

那么手机(分辨率1920*1080)是2.75,屏幕宽1080/2.75=393dp(3列)

平板1号(分辨率1920*1200)是2,屏幕宽1200/2=600dp  (5列)

平板2号(分辨率1920*1080)是2,屏幕宽1080/2=540dp  (4列)

还记得我columnWidth设置的是多少?120dp大哭

真是无语了600/120=5列,这个平板的分辨率我也是醉了。安卓的碎片化。。


那么找到了原因,也就好修改了,我通过计算屏幕宽的dp,如果大于480dp就设置4列。


其实解决这个适配需求还有许多好的方法。

分享出来,仅供参考。



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值