Clip可以避免with和height被扭曲,利用clip机制,可以实现卷轴、圆圈扩散、前一页翻页动画等。
public class ClipTransitionLayout extends FrameLayout implements ViewTreeObserver.OnPreDrawListener, ValueAnimator.AnimatorUpdateListener { private static final String TAG = "ClipTransitionLayout"; private final Rect mClipRect; private float mRoundRadius; private Path path = new Path(); private ValueAnimator valueAnimator; public ClipTransitionLayout(Context context) { this(context,null); } public ClipTransitionLayout(Context context, AttributeSet attrs) { this(context, attrs,0); } public ClipTransitionLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setClipChildren(true); mClipRect = new Rect(0, 0, 0, 0); } @Override public boolean onPreDraw() { mRoundRadius = Math.min(getWidth(),getHeight())/2; getViewTreeObserver().removeOnPreDrawListener(this); valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.setDuration(1000L); valueAnimator.addUpdateListener(this); valueAnimator.start(); return false; } @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction = animation.getAnimatedFraction(); mClipRect.left = 0; mClipRect.top = 0; mClipRect.right = (int) (getWidth()*fraction); mClipRect.bottom = getHeight(); if(fraction==1){ mRoundRadius = 0; } postInvalidate(); } //硬件加速时,虽然每个View都通过自己的RenderNode获得和其他View无交集的Canvas,但是在合成View时,依然遵循了由Parent->Child的合成逻辑,因此dispatchDraw中, //clipath在合成时自动处理,保证父布局可以“裁剪”子view的绘制区域 @Override protected void dispatchDraw(Canvas canvas) { if(isInEditMode()){ mRoundRadius = Math.min(getWidth(),getHeight())/2; mClipRect.right = getWidth()/2; } int saveCount = 0; if(canvas.isHardwareAccelerated()) { saveCount = canvas.save(); //硬件加速时,android官方不推荐使用saveLayer离屏渲染 }else{ //离屏渲染 saveCount = canvas.saveLayer(0,0,getWidth(),getHeight(),null,Canvas.ALL_SAVE_FLAG); } if(mRoundRadius==0) { canvas.clipRect(mClipRect); }else{ path.reset(); path.addRoundRect(new RectF(mClipRect.left,mClipRect.top,mClipRect.right,mClipRect.bottom), new float[]{0,0,0,0,mRoundRadius,mRoundRadius,0,0}, Path.Direction.CCW); canvas.clipPath(path); } super.dispatchDraw(canvas); canvas.restoreToCount(saveCount); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnPreDrawListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnPreDrawListener(this); if(valueAnimator!=null){ valueAnimator.cancel(); } } }