Android - 如何缩放视图以完全适合全屏应用程序的高度和宽度?

时间:2013-05-11 13:18:21

标签: android

如果你有一个计算器应用程序,并且想要编写一个看起来像this的布局,那么如何缩放按钮和显示以适合所有屏幕尺寸?

我已经研究过的想法:

  1. 以编程方式计算每个组件的高度和宽度。以编程方式创建视图可以为您提供最大的功能,但并不完全理想。我更喜欢用XML编写我的UI。

  2. 使用布局权重嵌套LinearLayouts。这是有效的,但lint给出了性能警告,因为我正在嵌套权重。最重要的是,它没有考虑文本大小。所以在小屏幕上,文字被砍掉了。相反,在大屏幕上,文字太小。

  3. 编辑: 3.使用具有嵌套权重的TableLayout。从LinearLayout考虑这些延伸,我认为缺乏lint警告是无关紧要的,这仍然会导致性能损失吗?

    有更好的方法吗?我觉得我错过了一些明显的东西

    编辑2: 如果有人对此解决方案感兴趣,我已经创建了一个自定义布局(如raphw建议的那样)并将在此处发布源代码:

    EvenSpaceGridLayout.java:

    package com.example.evenspacegridlayout;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class EvenSpaceGridLayout extends ViewGroup {
    
        private int mNumColumns;
    
        public EvenSpaceGridLayout(Context context) {
            super(context);
        }
    
        public EvenSpaceGridLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.EvenSpaceGridLayout);
            try {
                mNumColumns = a.getInteger(
                        R.styleable.EvenSpaceGridLayout_num_columns, 1);
            } finally {
                a.recycle();
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            // Calculate how many cells we need
            int cellCount = countCellsNeeded();
    
            // Calculate number of rows needed given the number of cells
            int numRows = cellCount / mNumColumns;
    
            // Calculate width/height of each individual cell
            int cellWidth = widthSize / mNumColumns;
            int cellHeight = heightSize / numRows;
    
            // Measure children
            measureChildrenViews(cellWidth, cellHeight);
    
            setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
            }
        }
    
        private int countCellsNeeded() {
    
            int cellCount = 0;
            final int childCount = getChildCount();
    
            for (int i = 0; i < childCount; i++) {
    
                View child = getChildAt(i);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
    
                int spanColumns = lp.spanColumns;
    
                // If it's trying to span too far, make it span the maximum possible
                if (spanColumns > mNumColumns) {
                    spanColumns = mNumColumns;
                }
    
                int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns);
                if (remainingCellsInRow - spanColumns < 0) {
                    cellCount += remainingCellsInRow + spanColumns;
                } else {
                    cellCount += spanColumns;
                }
            }
    
            // Round off the last row
            if ((cellCount % mNumColumns) != 0) {
                cellCount += mNumColumns - (cellCount % mNumColumns);
            }
    
            return cellCount;
        }
    
        private void measureChildrenViews(int cellWidth, int cellHeight) {
    
            int cellCount = 0;
            final int childCount = getChildCount();
    
            for (int i = 0; i < childCount; i++) {
    
                View child = getChildAt(i);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
    
                int spanColumns = lp.spanColumns;
    
                // If it's trying to span too far, make it span the maximum possible
                if (spanColumns > mNumColumns) {
                    spanColumns = mNumColumns;
                }
    
                // If it can't fit on the current row, skip those cells
                int remainingCellsInRow = mNumColumns - (cellCount % mNumColumns);
                if (remainingCellsInRow - spanColumns < 0) {
                    cellCount += remainingCellsInRow;
                }
    
                // Calculate x and y coordinates of the view
                int x = (cellCount % mNumColumns) * cellWidth;
                int y = (cellCount / mNumColumns) * cellHeight;
    
                lp.x = x;
                lp.y = y;
    
                child.measure(MeasureSpec.makeMeasureSpec(cellWidth * spanColumns, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(cellHeight, MeasureSpec.EXACTLY));
    
                cellCount += spanColumns;
            }
        }
    
        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
            return p instanceof LayoutParams;
        }
    
        @Override
        protected LayoutParams generateDefaultLayoutParams() {
            return new LayoutParams(LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT);
        }
    
        @Override
        public LayoutParams generateLayoutParams(AttributeSet attrs) {
            return new LayoutParams(getContext(), attrs);
        }
    
        @Override
        protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
            return new LayoutParams(p.width, p.height);
        }
    
        public static class LayoutParams extends ViewGroup.LayoutParams {
    
            int x, y;
    
            public int spanColumns;
    
            public LayoutParams(Context context, AttributeSet attrs) {
                super(context, attrs);
                TypedArray a = context.obtainStyledAttributes(attrs,
                        R.styleable.EvenSpaceGridLayout_LayoutParams);
                try {
                    spanColumns = a
                            .getInteger(
                                    R.styleable.EvenSpaceGridLayout_LayoutParams_span_columns,
                                    1);
    
                    // Can't span less than one column
                    if (spanColumns < 1) {
                        spanColumns = 1;
                    }
                } finally {
                    a.recycle();
                }
            }
    
            public LayoutParams(int w, int h) {
                super(w, h);
            }
        }
    }
    

    attrs.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="EvenSpaceGridLayout">
            <attr name="num_columns" format="integer" />
        </declare-styleable>
    
        <declare-styleable name="EvenSpaceGridLayout_LayoutParams">
            <attr name="span_columns" format="integer" />
        </declare-styleable>
    
    </resources>
    

    用法如下:

    <com.example.evenspacegridlayout.EvenSpaceGridLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:grid="http://schemas.android.com/apk/res/com.example.evenspacegridlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        grid:num_columns="4" >
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="CL" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="Del" />
    
        <!-- empty cell -->
        <View 
            android:layout_width="0dp"
            android:layout_height="0dp" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="/" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="7" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="8" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="9" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="*" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="4" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="5" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="6" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="-" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="1" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="2" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="3" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="+" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="." />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="0" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="="
            grid:span_columns="2" />
    
    </com.example.evenspacegridlayout.EvenSpaceGridLayout>
    

    最终结果:

    portrait landscape

2 个答案:

答案 0 :(得分:3)

其他人建议的GridLayout不够灵活,无法做到这一点。为此,请使用android:layout_weight属性。这允许您根据指定的分数填充可用空间。

等权重的示例:

<LinearLayout android:layout_width="match_parent" android:layout_height="100dp">
    <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="A" />
    <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="B" />    
    <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="C" />
</LinearLayout>

不同重量的示例:

<LinearLayout android:layout_width="match_parent" android:layout_height="100dp">
    <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="A" />
    <Button android:layout_weight="2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="B" />    
    <Button android:layout_weight="2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="C" />
</LinearLayout>

更复杂的例子

以下是使用多个LinearLayout s的计算器应用中使用的布局的更复杂示例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
    <LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="7" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="8" />    
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="9" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="x" />
    </LinearLayout>

    <LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="4" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="5" />    
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="6" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="-" />
    </LinearLayout>

    <LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="1" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="2" />    
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="3" />
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="+" />
    </LinearLayout>

    <LinearLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="0" />
        <Button android:layout_weight="1.5" android:layout_width="match_parent" android:layout_height="match_parent" android:text="." />    
        <Button android:layout_weight="1.5" android:layout_width="match_parent" android:layout_height="match_parent" android:text="=" />
    </LinearLayout>
</LinearLayout>

答案 1 :(得分:2)

您可以自己编写一个ViewGroup子类,它可以完成您想要的操作,并且仍然可以在 XML 布局定义中使用此布局,就像使用任何预定义布局一样。或者,查看GridLayout类。也许这个ViewGroup实现已经完成了你想要的。 (http://developer.android.com/reference/android/widget/GridLayout.html)最后,这些ViewGroup布局以编程方式计算其包含的View组件的大小,如果没有预定义的布局提供您所需的功能,除了实现您的功能之外别无他法。个人要求。

但是,按钮View实例仍应负责将其内容保持在最近一次调用onMeasure期间所收到的范围内。

使用示例图片将布局嵌套到扩展中,确实应该避免LinearLayout类。应该指出的是,这仍然是一种常见的做法,因为这在使用TableLayout时是隐含的。