ViewStub原理,解决约束布局( constraintLayout)ViewStub约束失效问题,解决ViewStub约束失效约束不起作用的问题

本文介绍了一个名为ViewRocket的自定义View组件,它解决了ViewStub存在的两大问题:无法动态添加自定义View及约束布局约束失效。通过扩展ViewStub功能,使得动态添加View变得简单,并确保了在约束布局中正确应用约束。

解决ViewStub约束布局 约束失效的问题

1.背景

在项目中进行性能优化或包体积控制的时候,就要进行xml或布局优化。
通常我们进行布局优化或xml加载优化的时候,就不得不提到到的几个标签mergeincludeViewStub,这几个标签作用不做赘述,重点讲一下本期的ViewStub

ViewStub使用场景是,需要复用layout但第一时间用不到,使用ViewStub做延迟加载。其原理特性就是:将目标layout引入后,替换自己,并将自己的属性LayoutParams设置给目标View/layout

ViewStub在使用的时候,有两种方式:

  1. 在在xml中通过layout属性引入目标layout;
  2. 或者在代码中动态使用setLayoutResource注入;

最后在代码调用ViewStub.inflate()ViewStub.setVisibility(View.VISIBLE)使引入的布局替换ViewStub。

2.问题

上面使用方式没有任何问题,使用起来也非常快捷方便,但问题也随之而来。

  • 一定要有一个layout
    ViewStub引入布局,一定要有一个额外的Layout,即使是Layout里面只有一个View,也需要单独为它提供一个Layout;

  • 它只能引入Layout
    有时候我们在代码中,需要动态的添加一个View,如果ViewGroup比较简单例如线性布局或者FrameLayout还好收,但是复杂的View,相对布局就需要动态设置Rule,而约束布局就相对来说比较复杂了,不是太了解约束布局动态更改约束规则的朋友就头疼了(我是自己研究了一下午才完全搞明白的),需要动态的加好几行代码控制约束,碰到View动态变换位置的场景更是要写好多代码。

于是,我就在想,如何解决这两个问题呢?为什么不能让ViewStub 把这两者结合呢

为什么ViewStub不能用在动态添加View,结合自己的特性,把ViewStub本身的layoutParams设置给动态添加的View呢?

3.寻找可行性

带着上面的问题,我重新看了一下它的源码。

@RemoteView
public final class ViewStub extends View {
   
   
    private int mInflatedId;
    private int mLayoutResource;

    private WeakReference<View> mInflatedViewRef;

    private LayoutInflater mInflater;
    private OnInflateListener mInflateListener;

    public ViewStub(Context context) {
   
   
        this(context, 0);
    }

    /**
     * Creates a new ViewStub with the specified layout resource.
     *
     * @param context The application's environment.
     * @param layoutResource The reference to a layout resource that will be inflated.
     */
    public ViewStub(Context context, @LayoutRes int layoutResource) {
   
   
        this(context, null);

        mLayoutResource = layoutResource;
    }

    public ViewStub(Context context, AttributeSet attrs) {
   
   
        this(context, attrs, 0);
    }

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {
   
   
        this(context, attrs, defStyleAttr, 0);
    }

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
   
   
        super(context);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,
                defStyleRes);

        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();

        setVisibility(GONE);
        setWillNotDraw(true);
    }

    @IdRes
    public int getInflatedId() {
   
   
        return mInflatedId;
    }

    /**
     * Defines the id taken by the inflated view. If the inflated id is
     * {@link View#NO_ID}, the inflated view keeps its original id.
     * 为引入的layout设置一个id
     */
    @android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync")
    public void setInflatedId(@IdRes int inflatedId) {
   
   
        mInflatedId = inflatedId;
    }

    /** @hide **/
    public Runnable setInflatedIdAsync(@IdRes int inflatedId) {
   
   
        mInflatedId = inflatedId;
        return null;
    }

    @LayoutRes
    public int getLayoutResource() {
   
   
        return mLayoutResource;
    }

    /**
     * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
     * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
     * used to replace this StubbedView in its parent.
     * 注入需要引入的layoutId
     */
    @android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync")
    public void setLayoutResource(@LayoutRes int layoutResource) {
   
   
        mLayoutResource = layoutResource;
    }

    /** @hide **/
    public Runnable setLayoutResourceAsync(@LayoutRes int layoutResource) {
   
   
        mLayoutResource = layoutResource;
        return null;
    }

    public void setLayoutInflater(LayoutInflater inflater) {
   
   
        mInflater = inflater;
    }

    /**
     * Get current {@link LayoutInflater} used in {@link #inflate()}.
     */
    public LayoutInflater getLayoutInflater() {
   
   
        return mInflater;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
   
        setMeasuredDimension(0, 0);
    }

    @Override
    public void draw(Canvas canvas) {
   
   
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
   
   
    }

    /**
     * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
     * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
     * by the inflated layout resource. After that calls to this function are passed
     * through to the inflated view.
     * 设置visibility  为VISIBLE 或者INVISIBLE时,会直接引入布局
     */
    @Override
    @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
    public void setVisibility(int visibility) {
   
   
        if (mInflatedViewRef != null) {
   
   
            View view = mInflatedViewRef.get();
            if (view != null) {
   
   
                view.setVisibility(visibility);
            } else {
   
   
                throw new IllegalStateException("setVisibility called on un-referenced view");
            }
        } else {
   
   
            super.setVisibility(visibility);
            if (visibility == VISIBLE || visibility == INVISIBLE) {
   
   
                inflate();
            }
        }
    }

    /** @hide **/
    public Runnable setVisibilityAsync(int visibility) {
   
   
        if (visibility == VISIBLE || visibility == INVISIBLE) {
   
   
            ViewGroup parent = (ViewGroup) getParent();
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值