按比例缩放的Layout

Android官方貌似没有按照比例计算自己大小的View,有个按照父控件计算自己大小的百分比View。而往往我们用到自身需要比例的时候还是比较多的,比如显示一张特定照片,宽度match_parent,高度是多少呢,交给ImageView自己测量,要么有白边,要么被部分切割。

其实计算View比例很简单,在View的生命周期中用来计算View大小的的方法是onMeasure,它有两个int参数widthMeasureSpec和heightMeasureSpec,每个分别表示长的大小和模式、宽的大小和模式。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

通过MeasureSpec.getModeMeasureSpec.getSize获取模式和值。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

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

        super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
    }

Mode分为MeasureSpec.EXACTLYMeasureSpec.AT_MOSTMeasureSpec.UNSPECIFIED三种,具体意思请百度。我们在布局中如果需要用比例必定长宽有一个是固定值(某dp或者match_parent),所以可以通过上面三种Mode中的MeasureSpec.EXACTLYMeasureSpec.AT_MOST来判别哪一边是固定值。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

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

        if (widthMode != MeasureSpec.UNSPECIFIED) {
            //  宽是固定的
            heightSize = (int) (widthSize / widthScale * heightScale);
            heightMode = MeasureSpec.EXACTLY;
        } else if (heightMode != MeasureSpec.UNSPECIFIED) {
            //  高是固定的
            widthSize = (int) (heightSize / heightScale * widthScale);
            widthMode = MeasureSpec.EXACTLY;
        }
        super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
    }

但是在Android Studio中预览并不是成比例的,真机正常,可能由于我不是很了解MeasureSpec.UNSPECIFIED,或者是AS预览的bug。然后我换了另一种方式,通过LayoutParams是否不是WRAP_CONTENT来判断是不是固定。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

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

        int widthParam  = getLayoutParams().width;
        int heightParam = getLayoutParams().height;

        if (widthScale > 0 && heightScale > 0) {
            if (widthParam != ViewGroup.LayoutParams.WRAP_CONTENT) {
                heightSize = (int) (widthSize / widthScale * heightScale);
                heightMode = MeasureSpec.EXACTLY;
            } else if (heightParam != ViewGroup.LayoutParams.WRAP_CONTENT) {
                widthSize = (int) (heightSize / heightScale * widthScale);
                widthMode = MeasureSpec.EXACTLY;
            }
        }
        super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
    }

这样预览也正常了。

所以最终在布局中引用

<cn.xuzhijun.proportional.ProportionalFrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#123456"
        app:scaleHeight="9"
        app:scaleWidth="16">

        <!--You View-->

</cn.xuzhijun.proportional.ProportionalFrameLayout>

而且高度和宽度只要有一边是固定值或者match_parent就可以了。
很简单。

有需要的可以直接引用。

compile 'cn.xuzhijun:xproportional-layout:1.0.0'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容