ViewStub详解

ViewStub详解首先上源码:packageandroid.view;importandroid.annotation.IdRes;importandroid.annotation.LayoutRes;importandroid.content.Context;importandroid.content.res.TypedArray;importandroid.graphics.Canvas;importandroid.util.AttributeSet;importandroid.widg

大家好,又见面了,我是你们的朋友全栈君。

源码:

package android.view;

import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.RemoteViews.RemoteView;

import com.android.internal.R;

import java.lang.ref.WeakReference;

/** * ViewStub是一个用户不可见的,大小为0的view,用于在app运行时懒加载一些布局。 *当ViewStub设置用户可见(setVisibility)时,或者当调用inflate()方法时,布局会就被初始化。ViewStub会用初始化后的布局文件替换自己放入其父布局中(参考inflate中的replaceSelfWithView方法)。 * * 因此ViewStub会在view布局中存在一直到setVisibility或inflate被调用了。 * * 当被初始化的View添加到ViewStub的父布局时会使用ViewStub的布局属性,因此你可以使用ViewStub的inflatedId属性定义/重写View的id。例如: * * * <ViewStub android:id="@+id/stub" * android:inflatedId="@+id/subTree" * android:layout="@layout/mySubTree" * android:layout_width="120dip" * android:layout_height="40dip" /> * * * 上面例子里边定义了一个id为stub的ViewStub。在初始化布局subTree之后,这个ViewStub就会从它父布局中移除。新创建的mySubTree布局的View的id就会使用布局中定义的subTree,最后这个新创建的view的宽为120dip,高为40dip. * * * 如下方式是最标准的初始化布局View的方式: * * * ViewStub stub = findViewById(R.id.stub); * View inflated = stub.inflate(); * * * 当inflate()方法被调用后,ViewStub会被替换为初始化后的布局View,并且View会被返回。从而让app获取到view的引用而不用在调用一次findViewById(). * * @attr ref android.R.styleable#ViewStub_inflatedId * @attr ref android.R.styleable#ViewStub_layout */
@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);
    }

    /** * 通过传入布局id创建ViewStub(猜测是用于代码中动态初始化时调用的)。 * * @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);
    }

	/** * 解析布局文件创建ViewStub,并获取mInflatedId及mLayoutResource等属性。最后设置不可见及不绘制自身。 * */
    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);
    }

    /** * Returns the id taken by the inflated view. If the inflated id is * {@link View#NO_ID}, the inflated view keeps its original id. * * @return A positive integer used to identify the inflated view or * {@link #NO_ID} if the inflated view should keep its id. * * @see #setInflatedId(int) * @attr ref android.R.styleable#ViewStub_inflatedId */
    @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. * * @param inflatedId A positive integer used to identify the inflated view or * {@link #NO_ID} if the inflated view should keep its id. * * @see #getInflatedId() * @attr ref android.R.styleable#ViewStub_inflatedId */
    @android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync")
    public void setInflatedId(@IdRes int inflatedId) { 
   
        mInflatedId = inflatedId;
    }

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

    /** * Returns the layout resource that will be used by {@link #setVisibility(int)} or * {@link #inflate()} to replace this StubbedView * in its parent by another view. * * @return The layout resource identifier used to inflate the new View. * * @see #setLayoutResource(int) * @see #setVisibility(int) * @see #inflate() * @attr ref android.R.styleable#ViewStub_layout */
    @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. * * @param layoutResource A valid layout resource identifier (different from 0.) * * @see #getLayoutResource() * @see #setVisibility(int) * @see #inflate() * @attr ref android.R.styleable#ViewStub_layout */
    @android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync")
    public void setLayoutResource(@LayoutRes int layoutResource) { 
   
        mLayoutResource = layoutResource;
    }

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

    /** * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null} * to use the default. */
    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);
    }

	/** * 不做任何绘制,所以比普通view节省了时间与内存。 */
    @Override
    public void draw(Canvas canvas) { 
   
    }

    @Override
    protected void dispatchDraw(Canvas canvas) { 
   
    }

    /** * 当设置visibility为VISIBLE或INVISIBLE时,inflate方法就会被调用 * ,且初始化出来的view对象会替换StubView在其父布局中的位置。 * * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * * @see #inflate() */
    @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();
            return new ViewReplaceRunnable(inflateViewNoAdd(parent));
        } else { 
   
            return null;
        }
    }

    private View inflateViewNoAdd(ViewGroup parent) { 
   
        final LayoutInflater factory;
        if (mInflater != null) { 
   
            factory = mInflater;
        } else { 
   
            factory = LayoutInflater.from(mContext);
        }
        final View view = factory.inflate(mLayoutResource, parent, false);

        if (mInflatedId != NO_ID) { 
   
            view.setId(mInflatedId);
        }
        return view;
    }

    private void replaceSelfWithView(View view, ViewGroup parent) { 
   
        final int index = parent.indexOfChild(this);
        parent.removeViewInLayout(this);

        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams != null) { 
   
            parent.addView(view, index, layoutParams);
        } else { 
   
            parent.addView(view, index);
        }
    }

    /** * 初始化布局view,替换stub在父布局中的位置(注:stub被替换之后getparent会为空,因此会抛出IllegalArgumentException异常)。 * * * @return The inflated layout resource. * */
    public View inflate() { 
   
        final ViewParent viewParent = getParent();

        if (viewParent != null && viewParent instanceof ViewGroup) { 
   
            if (mLayoutResource != 0) { 
   
                final ViewGroup parent = (ViewGroup) viewParent;
                final View view = inflateViewNoAdd(parent);
                replaceSelfWithView(view, parent);

                mInflatedViewRef = new WeakReference<>(view);
                if (mInflateListener != null) { 
   
                    mInflateListener.onInflate(this, view);
                }

                return view;
            } else { 
   
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else { 
   
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }

    /** * Specifies the inflate listener to be notified after this ViewStub successfully * inflated its layout resource. * * @param inflateListener The OnInflateListener to notify of successful inflation. * * @see android.view.ViewStub.OnInflateListener */
    public void setOnInflateListener(OnInflateListener inflateListener) { 
   
        mInflateListener = inflateListener;
    }

    /** * 接收布局view被创建的观察者,如果设置了会在布局view被初始化之后回调 listener的onInflate方法。 * * * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) */
    public static interface OnInflateListener { 
   
        /** * Invoked after a ViewStub successfully inflated its layout resource. * This method is invoked after the inflated view was added to the * hierarchy but before the layout pass. * * @param stub The ViewStub that initiated the inflation. * @param inflated The inflated View. */
        void onInflate(ViewStub stub, View inflated);
    }

    /** @hide **/
    public class ViewReplaceRunnable implements Runnable { 
   
        public final View view;

        ViewReplaceRunnable(View view) { 
   
            this.view = view;
        }

        @Override
        public void run() { 
   
            replaceSelfWithView(view, (ViewGroup) getParent());
        }
    }
}

使用方法:
1.在布局中定义ViewStub:

<ViewStub android:id="@+id/stub" android:inflatedId="@+id/subTree" android:layout="@layout/mySubTree" android:layout_width="120dip" android:layout_height="40dip" />

2.布局中使用:

	 ViewStub stub = findViewById(R.id.stub);
	 //inflated就是初始化后的布局view对象
     View inflated = stub.inflate();
     //对inflated操作,设置文字,图片等。
     

抛砖引玉,不当之处敬请之处,万分感谢?。

注:其实也可以也可以用动态添加的方法添加View:在java/kotlin代码中动态初始化View,然后添加到对应的viewgroup中。

1.在需要的时候通过layoutInflater.inflate(mLayoutResource, parent, false);

2.方法初始化要添加的view,然后调用对应viewGroup.addView(View child/View child, int index/View child, LayoutParams params);方法添加到布局中;

这样的话在布局中就不用多了个ViewStub的view了,性能也更好些。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153965.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • C语言——五子棋人机对战

    C语言——五子棋人机对战         先说下背景吧,写下这篇博客时,博主大一在读,C语言初学者,寒假无事,便计划写几个由C语言实现的小游戏以提升编程能力。在这篇博客里分享的是可人机对战的五子棋游戏。         棋类游戏要实现初级的机器智能,其核心思想便是:感知(SENSE)-&gt;思考(THINK)-&gt;行动(ACT)。所以,本文将尽量以这个顺序介绍实现过程。(1)前期准备:    此程序中,机器…

    2022年6月29日
    30
  • Idea激活码最新教程2023.2版本,永久有效激活码,亲测可用,记得收藏

    Idea激活码最新教程2023.2版本,永久有效激活码,亲测可用,记得收藏Idea 激活码教程永久有效 2023 2 激活码教程 Windows 版永久激活 持续更新 Idea 激活码 2023 2 成功激活

    2025年5月26日
    4
  • android刘海屏适配(刘海屏或圆角适配)

    安卓P版本也就是刚发布的安卓9.0其中就加入了刘海屏相关的API。其相关行为与功能变更也是非常多的,从其变更的趋势及功能来看,google在进一步的收紧权限。其P版本变更行为中就加入了对非SDK接口使用的新限制。无论是直接,通过反射还是通过JNI。只要应用程序引用非SDK接口或尝试使用反射或JNI获取其句柄,就会应用这些限制。通常,应用程序应仅使用SDK中类的官方记录部分。特别是,这意味着…

    2022年4月10日
    173
  • IE内存溢出报错Stack overflow at line[通俗易懂]

    IE内存溢出报错Stack overflow at line[通俗易懂]该错误只在IE中出现,出现该提示的原因主要有两种:1.重定义了系统的触发事件名称作为自定义函数名如: onclick/onsubmit… 都是系统保留的事件名称,不允许作为重定义函数名称。2.出现死循环,都提示:Stackoverflowatline:0,如:在图片对象定义了onerror事件的循环处理、onload这里并不是说/im

    2022年7月15日
    19
  • 单工,半双工,全双工区别以及TDD和FDD区别

    单工,半双工,全双工区别以及TDD和FDD区别作为一名学通信的,居然对这个概念还是没搞清楚,兼职就是丢了大脸了!现在总结如下,理解比较浅,大部分网上查的,有不对的,请批评指正!单工,半双工,全双工区别单工单工就是指A只能发信号,而B只能接收信号,通信是单向的,就象灯塔之于航船——灯塔发出光信号而航船只能接收信号以确保自己行驶在正确的航线上。半双工指一个时间段内只有一个动作发生,举个简单例子,一天窄窄的马路,同时只能有一辆车通过,

    2022年6月12日
    56
  • 计算机分子模拟聚乙烯,高分子物理实验思考题@中科大.pdf[通俗易懂]

    计算机分子模拟聚乙烯,高分子物理实验思考题@中科大.pdf[通俗易懂]高分子物理实验思考题@中科大1.为什么在计算机模拟实验1(用“分子模拟”软件构建全同立构聚丙烯分子、聚乙烯分子并计算它们末端的直线距离)中我们一再把第一个碳原子到最后一个碳原子的距离叫做末端距离,而不称通常所说的根均方末端距?2.你对计算机在高分子科学中的应用有多少了解?3.在考虑高分子链内旋转空间位阻时,高分子链的尺寸如何变化?4.在“二维高分子链形态的计算机模拟”实验中采用的是改进后…

    2022年5月25日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号