RelativeLayout圆角处理以后,可以变相对子view进行圆角处理,如ImageView,VideoView等
RoundRelativeLayout具体实现
比较简单,只需要在初始化时设置一下layout的ViewOutlineProvider,方便起见,这里写死radius为20dp
/*** 一个可以把RelativeLayout裁剪成圆角的自定义RelativeLayout布局* 由于layout里面的子view同样会被裁剪,例如VideoView,ImageView等,很实用** created by luninggithub*/
public class RoundRelativeLayout extends RelativeLayout {private static final int DEFAULT_ROUND_SIZE = 20;public RoundRelativeLayout(Context context) {this(context, null);}public RoundRelativeLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public RoundRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public RoundRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init();}/*** 初始化*/private void init() {RoundViewOutlineProvider outlineProvider = new RoundViewOutlineProvider(ScreenUtil.dp2px(DEFAULT_ROUND_SIZE));setOutlineProvider(outlineProvider);setClipToOutline(true);}/*** 圆角ViewOutlineProvider*/private static class RoundViewOutlineProvider extends ViewOutlineProvider {private final int roundSize;public RoundViewOutlineProvider(int roundSize) {this.roundSize = roundSize;}@Overridepublic void getOutline(View view, Outline outline) {outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), roundSize);}}
}
关键类ViewOutlineProvider
ViewOutlineProvider是一个抽象类,系统预定义了三种不同的outlineprovider,我们只需要实现getOutline方法,在方法内部设置自定义的outline即可。
package android.view;import android.graphics.Outline;
import android.graphics.drawable.Drawable;/*** Interface by which a View builds its {@link Outline}, used for shadow casting and clipping.*/
public abstract class ViewOutlineProvider {// 省略系统预定义的ViewOutlineProvider代码.../*** Called to get the provider to populate the Outline.** This method will be called by a View when its owned Drawables are invalidated, when the* View's size changes, or if {@link View#invalidateOutline()} is called* explicitly.** The input outline is empty and has an alpha of <code>1.0f</code>.** @param view The view building the outline.* @param outline The empty outline to be populated.*/public abstract void getOutline(View view, Outline outline);
}
由注释可知,getOutline会在view主动调invalidateOutline()方法是被调用
RoundRelativeLayout在初始化int是,会设置自定义的outlineprovider:setOutlineProvider(outlineProvider);
有系统代码可知,setOutlineProvider方法会主动调用,如下:
// 省略View的其他代码......
/*** Sets the {@link ViewOutlineProvider} of the view, which generates the Outline that defines* the shape of the shadow it casts, and enables outline clipping.* <p>* The default ViewOutlineProvider, {@link ViewOutlineProvider#BACKGROUND}, queries the Outline* from the View's background drawable, via {@link Drawable#getOutline(Outline)}. Changing the* outline provider with this method allows this behavior to be overridden.* <p>* If the ViewOutlineProvider is null, if querying it for an outline returns false,* or if the produced Outline is {@link Outline#isEmpty()}, shadows will not be cast.* <p>* Only outlines that return true from {@link Outline#canClip()} may be used for clipping.** @see #setClipToOutline(boolean)* @see #getClipToOutline()* @see #getOutlineProvider()*/
public void setOutlineProvider(ViewOutlineProvider provider) {mOutlineProvider = provider;invalidateOutline(); // 这里会主动调用自定义的getOutline方法
}
// 省略View的其他代码......
/*** Called to rebuild this View's Outline from its {@link ViewOutlineProvider outline provider}** @see #setOutlineProvider(ViewOutlineProvider)*/
public void invalidateOutline() {rebuildOutline();// 见下面方法notifySubtreeAccessibilityStateChangedIfNeeded();invalidateViewProperty(false, false);
}
// 省略View其他代码......
private void rebuildOutline() {// Unattached views ignore this signal, and outline is recomputed in onAttachedToWindow()if (mAttachInfo == null) return;if (mOutlineProvider == null) {// no provider, remove outlinemRenderNode.setOutline(null);} else {final Outline outline = mAttachInfo.mTmpOutline;outline.setEmpty();outline.setAlpha(1.0f);mOutlineProvider.getOutline(this, outline); // 这里调用mRenderNode.setOutline(outline); // 见下面方法}
}/*** Sets the outline, defining the shape that casts a shadow, and the path to* be clipped if setClipToOutline is set.** This will make a copy of the provided {@link Outline}, so any future modifications* to the outline will need to call {@link #setOutline(Outline)} with the modified* outline for those changes to be applied.** @param outline The outline to use for this RenderNode.* @return True if the value changed, false if the new value was the same as the previous value.*/
public boolean setOutline(@Nullable Outline outline) {if (outline == null) {return nSetOutlineNone(mNativeRenderNode);}switch (outline.mMode) {case Outline.MODE_EMPTY:return nSetOutlineEmpty(mNativeRenderNode);case Outline.MODE_ROUND_RECT: // 走到这里return nSetOutlineRoundRect(mNativeRenderNode,outline.mRect.left, outline.mRect.top,outline.mRect.right, outline.mRect.bottom,outline.mRadius, outline.mAlpha);case Outline.MODE_PATH:return nSetOutlinePath(mNativeRenderNode, outline.mPath.mNativePath,outline.mAlpha);}throw new IllegalArgumentException("Unrecognized outline?");
}
// 省略View代码......public final class RenderNode {// 省略无关代码...@CriticalNativeprivate static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,int right, int bottom, float radius, float alpha);
}
这样一看就比较清晰了
init -→ setOutlineProvider -→ invalidateOutline -→ rebuildOutline -→ setOutline -→ nSetOutlineRoundRect(Native接口)
Demo参考github:LnCustomViews