日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android ViewStub动态加载View

發布時間:2023/12/20 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android ViewStub动态加载View 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.第一種使用方式:

在xml布局中指定要膨脹的布局:

<ViewStubandroid:id="@+id/statusBar"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout="@layout/base_view_status_bar" />

在代碼中執行膨脹:

viewStub.inflate()

2.第二種使用方式:

不在xml中指定布局,在代碼中動態指定layout布局:

viewStub.layoutResource = R.layout.xxx viewStub.inflate()

3.第三種使用方式:

除了加載指定布局,我們可以通過反射加載指定的View對象:

?ViewStub通過inflate()函數膨脹一個布局,那我們看看inflate()函數的源碼:

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");}}

其中,?重點函數為inflateViewNoAdd()和replaceSelfWithView(),我們看看這倆個方法的實現:

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);}}

?我們可以看到,inflateViewNoAdd()的作用是通過layoutId去加載一個view;replaceSelfWithView()是將自己從父布局中移除,然后添加View進去;至此,我們知道了我們所需要執行的代碼為:

public View inflate() {final ViewParent viewParent = getParent();final ViewGroup parent = (ViewGroup) viewParent;replaceSelfWithView(view, parent);mInflatedViewRef = new WeakReference<>(view);if (mInflateListener != null) {mInflateListener.onInflate(this, view);}}

到這,事情就好辦了,我們只需要手動調用我們需要的方法即可,但ViewStub中這些方法是私有的,所以我們只能通過反射來調用了,反射代碼如下:

fun invokeInflate(view: View) {//當版本大于29時,我們可以直接通過view獲取到layoutId,則可以直接設置if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {viewStub.layoutResource = view.sourceLayoutResIdviewStub.inflate()return}//低版本則反射加載布局val viewGroup = viewStub.parent as ViewGroupval method =viewStub::class.java.getDeclaredMethod("replaceSelfWithView",View::class.java,ViewGroup::class.java)method.isAccessible = truemethod.invoke(viewStub, view, viewGroup)//對viewStub的mInflatedViewRef進行賦值,否則影響viewStub的#setVisibility()的調用val mInflatedViewRef = viewStub::class.java.getDeclaredField("mInflatedViewRef")mInflatedViewRef.isAccessible = truemInflatedViewRef[viewStub] = WeakReference(view)//通知viewStub已經膨脹,否則會影響ViewStubProxyval mInflateListener = viewStub::class.java.getDeclaredField("mInflateListener")mInflateListener.isAccessible = true(mInflateListener[viewStub] as ViewStub.OnInflateListener?)?.onInflate(viewStub,view)}

?以上代碼只在Android8.0以上生效,因為ViewStub在Android5.0至Android7.0中,inflate()方法的源碼為:

public View inflate() {final ViewParent viewParent = getParent();if (viewParent != null && viewParent instanceof ViewGroup) {if (mLayoutResource != 0) {final ViewGroup parent = (ViewGroup) viewParent;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);}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);}mInflatedViewRef = new WeakReference<View>(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");}}

我們可以看到,在inflate()中直接添加View,并沒有replaceSelfWithView()方法,所以在低版本中我們無法反射replaceSelfWithView(),因此,我們需要適配低版本,既然實現原理我們以及知道了,那只要我們自己實現一遍replaceSelfWithView()方法的內容即可:

fun invokeInflate(view: View) {//當版本大于29時,我們可以直接通過view獲取到layoutId,則可以直接設置if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {viewStub.layoutResource = view.sourceLayoutResIdviewStub.inflate()return}//低版本則反射加載布局val viewGroup = viewStub.parent as ViewGroup//不再使用反射,自己實現replaceSelfWithView()方法val index = viewGroup.indexOfChild(viewStub)viewGroup.removeViewInLayout(viewStub)viewStub.getLayoutParams()?.let { viewGroup.addView(view, index, it) }?: run { viewGroup.addView(view, index) }//對viewStub的mInflatedViewRef進行賦值,否則影響viewStub的#setVisibility()的調用val mInflatedViewRef = viewStub::class.java.getDeclaredField("mInflatedViewRef")mInflatedViewRef.isAccessible = truemInflatedViewRef[viewStub] = WeakReference(view)//通知viewStub已經膨脹,否則會影響ViewStubProxyval mInflateListener = viewStub::class.java.getDeclaredField("mInflateListener")mInflateListener.isAccessible = true(mInflateListener[viewStub] as ViewStub.OnInflateListener?)?.onInflate(viewStub,view) }

至此,我們就完成了往ViewStub動態添加View的操作了,其實,一通操作下來,你會發現沒什么用處,畢竟你都有View了,不使用ViewStub,而是直接加載View進布局中即可了?


如果以上方法對你有用的話,記得點個贊噢!!!

總結

以上是生活随笔為你收集整理的Android ViewStub动态加载View的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。