ViewStub的Pro版本;
参考https://blog.csdn.net/blankmargin/article/details/128158459 和阅读源码ViewStub优化实现。
0518增加cornerSize。
支持ViewStub本身的功能;
可以动态添加单个View,不用再为单个View写一个xml布局
inflatedId的作用是避免ConstraintLayout的不传递不生效问题。
用法eg:
注意:暂时不支持标签
/**
* ViewStub的Pro版本;
* 参考https://blog.csdn.net/blankmargin/article/details/128158459 和ViewStub实现。
*
* <p>
* 支持ViewStub本身的功能;
* 可以动态添加单个View,不用再为单个View写一个xml布局
* inflatedId的作用是避免ConstraintLayout的不传递不生效问题。
* <p>
* 用法eg:
* <p>
* 1. xml中设置replaceLayout(layout资源id)和inflatedId。然后viewStubPro.launch()。
* 2. xml中设置replaceViewClass (class全名) 和 inflatedId 。然后viewStubPro.launch()。
* 3. view = viewStubPro.setReplaceLayoutResource(R.layout.layout_any_layout).launch()。
* 4. view = viewStubPro.setReplaceViewClass(class Or ClassFullName).launch()。
* <p>
* 注意:暂时不支持<merge>标签
*/
public final class ViewStubPro extends View {
private interface IReplace { }
private static final class ReplaceLayout implements IReplace {
@LayoutRes
private int mLayoutResource;
}
private static final class ReplaceView implements IReplace {
private Class<? extends View> mViewClass;
}
private static final String TAG = "ViewStubPro";
private final Context mContext;
private WeakReference<View> mInflatedViewRef;
private IReplace mReplace;
/**
* 从布局或者手动设定inflatedId。
*/
@IdRes
public int inflatedId;
/**
* 从外部设置进来;或者xml中。
* 注意
* 1. 如果是代码的话,自行转换了dp以后传入。即通过context.resource.getDimen(R.dimen.xx)进来
* 2. 必须在未实例化Stub之前设置
*
*/
public int allCornerSize;
//圆角矩形
private ViewOutlineProvider mCustomViewOutlineProvider;
private ViewOutlineProvider getCustomViewOutlineProvider() {
if (mCustomViewOutlineProvider != null) {
return mCustomViewOutlineProvider;
}
mCustomViewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), allCornerSize);
}
};
return mCustomViewOutlineProvider;
}
public ViewStubPro(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewStubPro(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubPro);
//从xml初始化
int layout = a.getResourceId(R.styleable.ViewStubPro_replaceLayout, 0);
if (layout > 0) {
setReplaceLayoutResource(layout);
} else {
String viewClass = a.getString(R.styleable.ViewStubPro_replaceViewClass);
setReplaceViewClass(viewClass);
}
inflatedId = a.getResourceId(R.styleable.ViewStubPro_inflatedId, NO_ID);
allCornerSize = (int) a.getDimension(R.styleable.ViewStubPro_inflatedCornerSize, 0);
a.recycle();
setVisibility(GONE);
setWillNotDraw(true);
}
/**
* 代码中设定为layout模式。设置布局id模式。
*/
public ViewStubPro setReplaceLayoutResource(@LayoutRes int layoutResource) {
ReplaceLayout r = new ReplaceLayout();
r.mLayoutResource = layoutResource;
mReplace = r;
return this;
}
/**
* 代码中设定为layout模式。设置为单View模式。传入Class我将帮你new出来。
*/
public ViewStubPro setReplaceViewClass(Class<? extends View> replaceView) {
ReplaceView v = new ReplaceView();
v.mViewClass = replaceView;
mReplace = v;
return this;
}
/**
* 代码中设定为layout模式。设置为单View模式。传入Class我将帮你new出来。
*/
public ViewStubPro setReplaceViewClass(String replaceView) {
ReplaceView v = new ReplaceView();
try {
v.mViewClass = (Class<? extends View>) Class.forName(replaceView);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
mReplace = v;
return this;
}
public View launch() {
if (mReplace == null) {
Log.e(TAG, "launch: ");
return null;
}
if (mIsLaunched) {
Log.e(TAG, "sendView: The rocket is disposable and cannot be fired repeatedly");
return mInflatedViewRef.get();
}
View view = null;
if (mReplace instanceof ReplaceLayout) {
view = inflateViewNoAdd((ViewGroup) getParent(), ((ReplaceLayout) mReplace).mLayoutResource);
} else if (mReplace instanceof ReplaceView) {
/*以下调用带参的、私有构造函数*/
try {
Constructor<? extends View> c = ((ReplaceView) mReplace).mViewClass.getDeclaredConstructor(Context.class);
view = (View) c.newInstance(mContext);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
if (view != null) {
launch(view);
return view;
}
return null;
}
private void launch(View view) {
final ViewGroup parent = (ViewGroup) getParent();
final int index = parent.indexOfChild(this);
parent.removeViewInLayout(this);
if (parent instanceof ConstraintLayout) {
//如果parentView是约束布局,把自身id设置给运送的View
inflatedId = this.getId();
view.setId(inflatedId);
}
//如果View没有id,就给view生成一个id
if (view.getId() == View.NO_ID) {
inflatedId = View.generateViewId();
view.setId(inflatedId);
}
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams != null) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
if (allCornerSize > 0) {
//圆角矩形
view.setOutlineProvider(getCustomViewOutlineProvider());
view.setClipToOutline(true);
}
mIsLaunched = true;
mInflatedViewRef = new WeakReference<>(view);
}
private View inflateViewNoAdd(ViewGroup parent, int layoutRes) {
final LayoutInflater factory = LayoutInflater.from(mContext);
final View view = factory.inflate(layoutRes, parent, false);
if (layoutRes != NO_ID) {
view.setId(layoutRes);
}
return view;
}
@Override
public void setVisibility(int visibility) {
// 当真正的布局文件被加载之后
if (mInflatedViewRef != null) {
// 获取到当前的View
View view = mInflatedViewRef.get();
if (view != null) {
//操纵当前View的可见行
view.setVisibility(visibility);
} else {
throw new IllegalStateException("setVisibility called on un-referenced view");
}
} else {
//没有调用inflate的话,会设置可见性
super.setVisibility(visibility);
//当 当前设置可见性为 VISIBLE或者INVISIBLE的时候,会调用inflate方法。
if (visibility == VISIBLE || visibility == INVISIBLE) {
launch();
}
}
}
@Override
public int getVisibility() {
if (mInflatedViewRef != null) {
View view = mInflatedViewRef.get();
if (view != null) {
return view.getVisibility();
}
}
return View.GONE;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
super.draw(null);
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
private boolean mIsLaunched = false;
public boolean isLaunched() {
return mIsLaunched;
}
添加xml styleable 申明。
<declare-styleable name="ViewStubPro">
<attr name="inflatedId" format="reference" />
<attr name="replaceLayout" format="reference" />
<attr name="replaceViewClass" format="string" />
<attr name="inflatedCornerSize" format="dimension"/>
</declare-styleable>
举例:
<com.xxx.xxx.ViewStubPro
android:id="@+id/videoStub"
app:inflatedId="@+id/videoStub"
tools:visibility="invisible"
app:replaceViewClass="com.xxx.module.xxx.view.xxxCustomView"
app:layout_constraintTop_toTopOf="parent"
app:inflatedCornerSize="8dp"
android:layout_width="match_parent"
android:layout_height="425dp"/>
保持(保持与id相同)的id。
然后调用,binding.videoStub.launch() 并强转为 XXXCustomView即可使用。