解析AndroidProject 可設置寬高比的Layout
-
效果演示

此時屏幕的寬度是固定的,通過屏幕的寬度計算FrameLayout的高度。除了單一的寬度為屏幕寬度外,還有其他寬度或者高度固定的比例顯示。
-
實現方式
布局文件find_fragment.xml
<com.hjq.widget.layout.RatioFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/common_accent_color" app:sizeRatio="2:1"> <androidx.appcompat.widget.AppCompatTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="這是一個寬高比 2:1 的FrameLayout" android:textColor="@color/white" /> </com.hjq.widget.layout.RatioFrameLayout>
RatioFrameLayout:自定義比例FrameLayout
自定義View的方式去實現:onMeasure重新計算控件寬高
package com.hjq.widget.layout; import android.content.Context; import android.content.res.TypedArray; import android.text.TextUtils; import android.util.AttributeSet; import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.hjq.widget.R; /** * author : Android 輪子哥 * github : https://github.com/getActivity/AndroidProject * time : 2019/08/23 * desc : 按照比例顯示的 FrameLayout */ public final class RatioFrameLayout extends FrameLayout { /** 寬高比例 */ private float mWidthRatio; private float mHeightRatio; public RatioFrameLayout(Context context) { this(context, null); } public RatioFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RatioFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public RatioFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout); String sizeRatio = array.getString(R.styleable.RatioFrameLayout_sizeRatio); if (!TextUtils.isEmpty(sizeRatio)) { String[] split = sizeRatio.split(":"); switch (split.length) { case 1: mWidthRatio = Float.parseFloat(split[0]); mHeightRatio = 1; break; case 2: mWidthRatio = Float.parseFloat(split[0]); mHeightRatio = Float.parseFloat(split[1]); break; default: throw new IllegalArgumentException("are you ok?"); } } array.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mWidthRatio != 0 && mHeightRatio != 0) { float sizeRatio = getSizeRatio(); ViewGroup.LayoutParams layoutParams = getLayoutParams(); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); // 一般情況下 LayoutParams.WRAP_CONTENT 對應著 MeasureSpec.AT_MOST(自適應),但是由于我們在代碼中強制修改了測量模式為 MeasureSpec.EXACTLY(固定值) // 這樣會有可能重新觸發一次 onMeasure 方法,這個時候傳入測量模式的就不是 MeasureSpec.AT_MOST(自適應) 模式,而是 MeasureSpec.EXACTLY(固定值)模式 // 所以我們要進行雙重判斷,首先判斷 LayoutParams,再判斷測量模式,這樣就能避免因為修改了測量模式觸發對寬高的重新計算,最終導致計算結果和上次計算的不同 if (layoutParams.width != LayoutParams.WRAP_CONTENT && layoutParams.height != LayoutParams.WRAP_CONTENT && widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { // 如果當前寬度和高度都是寫死的 if (widthSpecSize / sizeRatio <= heightSpecSize) { // 如果寬度經過比例換算不超過原有的高度 heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (widthSpecSize / sizeRatio), MeasureSpec.EXACTLY); } else if (heightSpecSize * sizeRatio <= widthSpecSize) { // 如果高度經過比例換算不超過原有的寬度 widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (heightSpecSize * sizeRatio), MeasureSpec.EXACTLY); } } else if (layoutParams.width != LayoutParams.WRAP_CONTENT && widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode != MeasureSpec.EXACTLY) { // 如果當前寬度是寫死的,但是高度不寫死 heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (widthSpecSize / sizeRatio), MeasureSpec.EXACTLY); } else if (layoutParams.height != LayoutParams.WRAP_CONTENT && heightSpecMode == MeasureSpec.EXACTLY && widthSpecMode != MeasureSpec.EXACTLY) { // 如果當前高度是寫死的,但是寬度不寫死 widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (heightSpecSize * sizeRatio), MeasureSpec.EXACTLY); } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public float getWidthRatio() { return mWidthRatio; } public float getHeightRatio() { return mHeightRatio; } /** * 獲取寬高比 */ public float getSizeRatio() { return mWidthRatio / mHeightRatio; } /** * 設置寬高比 */ public void setSizeRatio(float widthRatio, float heightRatio) { mWidthRatio = widthRatio; mHeightRatio = heightRatio; invalidate(); } }
attrs.xml 控件屬性:寬高比例

<!-- 按照比例顯示的 FrameLayout --> <declare-styleable name="RatioFrameLayout"> <!-- 寬高比例 --> <attr name="sizeRatio" format="string" /> </declare-styleable>

浙公網安備 33010602011771號