LinearLayout布局动态调整

需求:

实现响应式的LinearLayout,主要要求有两点
1、可以设置两套布局(一套主布局,一套子布局)根据屏宽度动态展示子布局  
2、主布局上可以动态调整布局
    1、界面大调整:java层开放调节接口   
    2、界面小调整:组件内部对子View进行调整

验证过程:

在一块屏上显示对应布局,然后将屏横向展示查看布局改变情况

验证效果:

 原理:

在布局时设置需要宽度和调整类型,在组件测量时对布局进行调整。有几个值得注意的问题

1、在增加子布局或者做布局调整的时候都需要在原有的布局基础上增加布局,本人采用的是以原有的LinearLayout为根布局新增LinearLayout存放原有布局和调整以后的布局,之后将最终布局一起塞入到根LinearLayout中。但是存在问题,就是LinearLayout有些属性需要和根LinearLayout一样,这块还不知道如何复制。

2、在java回调处理的时候,因为组件测量的时候还没有完成回调的初始化,得做延时触发处理,这个地方会出现布局的闪动。

具体代码:

自定义属性部分:

<declare-styleable name="XCAutoLinearLayout">
    <attr name="xc_auto_type">
        <flag name="no_auto" value="0" />
        <flag name="child_layout" value="1" />
        <flag name="auto_match" value="2" />
        <flag name="java_control" value="3" />
    </attr>
    <attr name="xc_auto_self_width" format="dimension" />
    <attr name="xc_auto_child_layout" format="reference" />
</declare-styleable>

子布局部分:

<?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">

    <TextView
        android:id="@+id/hello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textColor="@color/design_default_color_error"
        android:layout_marginTop="20dp"
        android:text="右侧子布局" />

</LinearLayout>

主要布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.XCLinearLayoutActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#00aaaa"
        android:gravity="center"
        android:text="响应式XCAutoLinearLayout组件"
        android:textColor="#EEE"
        tools:ignore="MissingConstraints" />

    <com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="50dp"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:xc_auto_child_layout="@layout/item_linear"
        app:xc_auto_self_width="1000dp"
        app:xc_auto_type="child_layout">

        <TextView
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="15dp"
            android:text="主布局" />
    </com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout>

    <com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout
        android:id="@+id/xcAutollAutoMath"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="100dp"
        android:orientation="vertical"
        android:visibility="visible"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:xc_auto_self_width="1000dp"
        app:xc_auto_type="auto_match">

        <TextView
            android:id="@+id/helloam1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="组件调控 111" />

        <TextView
            android:id="@+id/helloam2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="组件调控 222" />

        <TextView
            android:id="@+id/helloam3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="组件调控 333" />

        <TextView
            android:id="@+id/helloam4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="组件调控 444" />
    </com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout>

    <com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout
        android:id="@+id/xcAutollJavaControl"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="200dp"
        android:orientation="vertical"
        android:visibility="visible"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:xc_auto_self_width="1000dp"
        app:xc_auto_type="java_control">

        <TextView
            android:id="@+id/hellojc1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="java 调控111" />

        <TextView
            android:id="@+id/hellojc2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="java 调控222" />

        <TextView
            android:id="@+id/hellojc3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="java 调控333" />

        <TextView
            android:id="@+id/hellojc4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="5dp"
            android:text="java 调控444" />
    </com.rcl.recyclerviewdemo.widget.XCAutoLinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 activity部分主要是java调控:

public class XCLinearLayoutActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_xclinear_layout);
        XCAutoLinearLayout xcAutoLinearLayout = findViewById(R.id.xcAutollJavaControl);
        xcAutoLinearLayout.setXcWidthConfigChange(new XCAutoLinearLayout.WidthConfigChange() {
            @Override
            public void onWidthConfigChange(boolean isDoubleWidht) {
                if (isDoubleWidht) {
                    findViewById(R.id.hellojc1).setVisibility(View.GONE);
                    findViewById(R.id.hellojc3).setVisibility(View.GONE);
                } else {
                    findViewById(R.id.hellojc1).setVisibility(View.VISIBLE);
                    findViewById(R.id.hellojc3).setVisibility(View.VISIBLE);
                }
            }
        });
    }
}

 自定义组件XCAutoLinearLayout

public class XCAutoLinearLayout extends LinearLayout {
    private static final String TAG = "XCAutoLinearLayout";
    private static final int JAVA_CONTROL_UPDATE_DELAY_TIME = 10;
    /**
     * 不需要处理自适应,默认情况
     */
    private static final int XC_AUTO_TYPE_NO = 0;
    /**
     * 展示右侧子布局,需要同时设置  xc_auto_child_layout属性  例如:app:xc_auto_child_layout="@layout/item_linear"
     */
    private static final int XC_AUTO_TYPE_CHILD_LAYOUT = 1;
    /**
     * 组件自设定逻辑适配
     */
    private static final int XC_AUTO_TYPE_AUTO_MATH = 2;
    /**
     * 开放接口给java层,在Java层做适配
     */
    private static final int XC_AUTO_TYPE_JAVA_CONTROL = 3;
    /**
     * layout自适应的模式
     */
    private int xcAutoType = 0;
    /**
     * 自适应的宽度
     */
    private int xcAutoChildLayout = 0;
    /**
     * 右侧子布局
     */
    private View xcAutoChildContainer;
    /**
     * 是否已经展示了右侧子布局(设置了weight,会出现两次测量)
     */
    private Boolean isChildContainerShow = false;
    /**
     * 是否已经做了组件自适应(设置了weight,会出现两次测量)
     */
    private Boolean isDoAutoMatch = false;
    /**
     * item自适应的最大宽度
     */
    private float xcAutoSelfWidth = 0;
    private Context xcContext;
    private XCAutoLinearLayout mySelf;
    /**
     * 布局的属性参数,用来赋值用
     */
    private AttributeSet xcAttributeSet;
    /**
     * 宽度变化回调
     */
    private WidthConfigChange xcWidthConfigChange = null;

    /**
     * 是否需要出发java逻辑调控事件
     * 由于组件的初始化和界面的初始化是双线程的
     * 所以组件测量时可能还没有完成事件监听的初始化
     * 于是将事件触发逻辑下移到布局中,同时开启延迟触发机制
     */
    private boolean isNeedDoJavaControlAction = false;

    private Handler xcHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (xcWidthConfigChange == null) {
                Log.e("jamie", "setXcWidthConfigChange---还没初始化");
                xcHandler.sendEmptyMessageDelayed(1, JAVA_CONTROL_UPDATE_DELAY_TIME);
            } else {
                Log.e("jamie", "setXcWidthConfigChange---初始化完成");
                xcWidthConfigChange.onWidthConfigChange(true);
            }
            return true;
        }
    });

    public void setXcWidthConfigChange(WidthConfigChange xcWidthConfigChange) {
        Log.e("jamie", "setXcWidthConfigChange---" + new Date().getTime());
        this.xcWidthConfigChange = xcWidthConfigChange;
    }

    public XCAutoLinearLayout(@NonNull Context context) {
        super(context);
        xcContext = context;
        mySelf = this;
    }

    public XCAutoLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initParams(context, attrs);
    }

    public XCAutoLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initParams(context, attrs);
    }

    /**
     * 获取xml中属性值
     */
    private void initParams(Context context, AttributeSet attrs) {
        xcContext = context;
        xcAttributeSet = attrs;
        mySelf = this;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XCAutoLinearLayout);

        if (typedArray != null) {
            xcAutoType = typedArray.getInt(R.styleable.XCAutoLinearLayout_xc_auto_type, XC_AUTO_TYPE_NO);
            xcAutoSelfWidth = px2dp(typedArray.getDimension(R.styleable.XCAutoLinearLayout_xc_auto_self_width, 0));
            xcAutoChildLayout = typedArray.getResourceId(R.styleable.XCAutoLinearLayout_xc_auto_child_layout, 0);
            Log.i("jamie", "xcAutoSelfWidth:" + xcAutoSelfWidth + "---xcAutoChildLayout:" + xcAutoChildLayout);
            typedArray.recycle();
        }
    }

    /**
     * 在测量前,更改对应配置
     */
    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (xcAutoType != XC_AUTO_TYPE_NO) {
            final int width = RecyclerView.LayoutManager.chooseSize(widthSpec, getPaddingLeft() + getPaddingRight(), ViewCompat.getMinimumWidth(this));
            final int height = RecyclerView.LayoutManager.chooseSize(heightSpec, getPaddingTop() + getPaddingBottom(), ViewCompat.getMinimumHeight(this));
            if (width / xcAutoSelfWidth >= 2) {
                doWidthAuto(width, height);
            } else {
                isDoAutoMatch = false;
                isChildContainerShow = false;
                isNeedDoJavaControlAction = false;
            }
        } else {
            isDoAutoMatch = false;
            isChildContainerShow = false;
            isNeedDoJavaControlAction = false;
        }
        super.onMeasure(widthSpec, heightSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.e("jamie", "onLayout---" + new Date().getTime());
        if (isNeedDoJavaControlAction) {
            if (xcWidthConfigChange != null) {
                xcWidthConfigChange.onWidthConfigChange(true);
            } else {
                xcHandler.sendEmptyMessageDelayed(1, JAVA_CONTROL_UPDATE_DELAY_TIME);
            }
        } else {
            if (xcWidthConfigChange != null) {
                xcWidthConfigChange.onWidthConfigChange(false);
            }
        }

        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.e("jamie", "onDraw---" + new Date().getTime());
        super.onDraw(canvas);
    }

    /**
     * 对不同类型做分离
     */
    private void doWidthAuto(int width, int height) {
        switch (xcAutoType) {
            case XC_AUTO_TYPE_CHILD_LAYOUT: {
                childLayoutMatch(width, height);
                break;
            }
            case XC_AUTO_TYPE_JAVA_CONTROL: {
                isNeedDoJavaControlAction = true;
                break;
            }
            case XC_AUTO_TYPE_AUTO_MATH: {
                autoMatch(width, height);
                break;
            }
        }
    }

    /**
     * 组件自适应逻辑处理
     */
    private void autoMatch(int width, int height) {
        if (!isDoAutoMatch) {
            LinearLayout.LayoutParams leftParams = new LinearLayout.LayoutParams(width / 2, height, 1);

            LinearLayout leftLinear = new LinearLayout(xcContext, xcAttributeSet);
            leftLinear.setLayoutParams(leftParams);
            leftLinear.setOrientation(VERTICAL);
            LinearLayout rightLinear = new LinearLayout(xcContext, xcAttributeSet);
            rightLinear.setLayoutParams(leftParams);
            rightLinear.setOrientation(VERTICAL);

            int i = 0;
            while (this.getChildCount() > 0) {
                View tempView = this.getChildAt(0);
                this.removeView(tempView);
                if (i % 2 == 0) {
                    leftLinear.addView(tempView);
                } else {
                    rightLinear.addView(tempView);
                }
                i++;
            }

            this.removeAllViews();
            this.setOrientation(HORIZONTAL);
            this.addView(leftLinear);
            this.addView(rightLinear);

            isDoAutoMatch = true;
        }
    }

    /**
     * 右侧子布局适配逻辑处理
     */
    private void childLayoutMatch(int width, int height) {
        if (xcAutoChildLayout != 0 && !isChildContainerShow) {
            LayoutInflater inflater = (LayoutInflater) xcContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            xcAutoChildContainer = inflater.inflate(xcAutoChildLayout, null);

            LinearLayout.LayoutParams leftParams = new LinearLayout.LayoutParams(width / 2, height, 1);
            LinearLayout.LayoutParams rightParams = new LinearLayout.LayoutParams(width / 2, height, 1);
            xcAutoChildContainer.setLayoutParams(rightParams);

            LinearLayout tempLinear = new LinearLayout(xcContext, xcAttributeSet);
            tempLinear.setLayoutParams(leftParams);
            tempLinear.setOrientation(VERTICAL);

            while (this.getChildCount() > 0) {
                View tempView = this.getChildAt(0);
                this.removeView(tempView);
                tempLinear.addView(tempView);
            }

            this.setOrientation(HORIZONTAL);
            this.addView(tempLinear);
            this.addView(xcAutoChildContainer);
            isChildContainerShow = true;
        }
    }

    /**
     * 将px值转换为dp值
     */
    public int px2dp(float pxValue) {
        final float scale = xcContext.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public interface WidthConfigChange {
        void onWidthConfigChange(boolean isDoubleWidht);
    }
}

                
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值