滑动解锁 android Canvas自定义控件中硬件加速引起的canvas.clipPath问题

来源:互联网 发布:xmind mac怎么安装 编辑:程序博客网 时间:2024/06/05 13:21

我们知道,android中的图形都是矩形的。

要绘制一个如下的椭圆形控件(圆形图像,圆角listview,圆角按钮),我们需要裁剪画布


在4.4一下的设备上 使用canvas.clipPath裁剪画布以使得控件中绘制的内容能被不规则化


//裁剪画布
mContentRect.set(0, 0, getWidth(), getHeight());mPath.addRoundRect(mContentRect, r/2,r/2, Path.Direction.CCW);
canvas.clipPath(mPath,android.graphics.Region.Op.REPLACE);
//绘制其他控件元素,灯泡 文字 背景


但是 在部分真是设备中,它的效果缺是这样的



裁剪失效,有没有?


问题在于canvas默认开启了硬件加速  

硬件加速具体的介绍见官方文档

http://developer.android.com/guide/topics/graphics/hardware-accel.html


下面是硬件加速不支持的API和SDK等级对照表




     API level< 171718Support for large scale factorsdrawText()✗✗✓drawPosText()✗✗✗drawTextOnPath()✗✗✗Simple Shapes*✗✓✓Complex Shapes*✗✗✗drawPath()✗✗✗Shadow layer✗✗✗

      API level< 16161718CanvasdrawBitmapMesh() (colors array)✗✗✗✓drawPicture()✗✗✗✗drawPosText()✗✓✓✓drawTextOnPath()✗✓✓✓drawVertices()✗✗✗✗setDrawFilter()✗✓✓✓clipPath()✗✗✗✓clipRegion()✗✗✗✓clipRect(Region.Op.XOR)✗✗✗✓clipRect(Region.Op.Difference)✗✗✗✓clipRect(Region.Op.ReverseDifference)✗✗✗✓clipRect() with rotation/perspective✗✗✗✓PaintsetAntiAlias() (for text)✗✗✗✓setAntiAlias() (for lines)✗✓✓✓setFilterBitmap()✗✗✓✓setLinearText()✗✗✗✗setMaskFilter()✗✗✗✗setPathEffect() (for lines)✗✗✗✗setRasterizer()✗✗✗✗setShadowLayer() (other than text)✗✗✗✗setStrokeCap() (for lines)✗✗✗✓setStrokeCap() (for points)✗✗✗✗setSubpixelText()✗✗✗✗XfermodeAvoidXfermode✗✗✗✗PixelXorXfermode✗✗✗✗PorterDuff.Mode.DARKEN (framebuffer)✗✗✗✗PorterDuff.Mode.LIGHTEN (framebuffer)✗✗✗✗PorterDuff.Mode.OVERLAY (framebuffer)✗✗✗✗ShaderComposeShader inside ComposeShader✗✗✗✗Same type shaders inside ComposeShader✗✗✗✗Local matrix on ComposeShader✗✗✗✓

clipPath 赫然就在其中

解决的办法的有2个;


1:依照上面的表不在不支持的设备上使用这个API,

2:对这个自定义的view 禁用硬件加速

setLayerType(View.LAYER_TYPE_SOFTWARE, null);




例子中的自定义控件完整代码如下
/** * slide to control */public class SlideControllerView extends RelativeLayout {private static final int STATE_DRAGGING = 1;private String mExampleString;private int mExampleColor = Color.RED;private float mExampleDimension = 0;private Drawable mExampleDrawable;private TextPaint mTextPaint;private Paint mMaskPaint;private float mTextWidth;private float mTextHeight;private Path mPath;private Drawable mDrawable_button;private Drawable mDrawable_background_button;int COLOR_BLUE_LIGHT = 0xff3094ff;private ScrollerCompat mScroller;public SlideControllerView(Context context) {super(context);init(null, 0);}public SlideControllerView(Context context, AttributeSet attrs) {super(context, attrs);init(attrs, 0);}public SlideControllerView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs, defStyle);}private void init(AttributeSet attrs, int defStyle) {// Load attributesfinal TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.SlideControllerView, defStyle, 0);mExampleString = a.getString(R.styleable.SlideControllerView_exampleString);mExampleColor = a.getColor(R.styleable.SlideControllerView_exampleColor, mExampleColor);// Use getDimensionPixelSize or getDimensionPixelOffset when dealing// with// values that should fall on pixel boundaries.mExampleDimension = a.getDimension(R.styleable.SlideControllerView_exampleDimension,mExampleDimension);if (a.hasValue(R.styleable.SlideControllerView_exampleDrawable)) {mExampleDrawable = a.getDrawable(R.styleable.SlideControllerView_exampleDrawable);mExampleDrawable.setCallback(this);}if (a.hasValue(R.styleable.SlideControllerView_button)) {mDrawable_button = a.getDrawable(R.styleable.SlideControllerView_button);mDrawable_button.setCallback(this);}if (a.hasValue(R.styleable.SlideControllerView_background_button)) {mDrawable_background_button = a.getDrawable(R.styleable.SlideControllerView_background_button);mDrawable_background_button.setCallback(this);}a.recycle();// Set up a default TextPaint objectmTextPaint = new TextPaint();mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);mTextPaint.setTextAlign(Paint.Align.LEFT);// Set up a default TextPaint objectmMaskPaint = new Paint();mMaskPaint.setFlags(Paint.ANTI_ALIAS_FLAG);mMaskPaint.setAntiAlias(true);mMaskPaint.setDither(true);mMaskPaint.setColor(0xcccccc);mPath = new Path();paint_state = new Paint();mScroller = ScrollerCompat.create(getContext(), sInterpolator);// Update TextPaint and text measurements from attributesinvalidateTextPaintAndMeasurements();setLayerType(View.LAYER_TYPE_SOFTWARE, null);}/** * Interpolator defining the animation curve for mScroller */private static final Interpolator sInterpolator = new Interpolator() {public float getInterpolation(float t) {t -= 1.0f;return t * t * t * t * t + 1.0f;}};int r = 0;RectF mleft = new RectF();RectF mcenter = new RectF();RectF mright = new RectF();RectF mContentRect = new RectF();private LinearGradient shader_stateFouse;private Paint paint_state;private int mDragState;@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mPath.reset();mContentRect.set(0, 0, getWidth(), getHeight());mPath.addRoundRect(mContentRect, r/2,r/2, Path.Direction.CCW);}@Overridepublic boolean onTouchEvent(MotionEvent event) {return handerTouchEvent(event);}private boolean handerTouchEvent(MotionEvent ev) {final int action = MotionEventCompat.getActionMasked(ev);System.out.println("handerTouchEvent");switch (action) {case MotionEvent.ACTION_DOWN: {cancel();final float x = ev.getX();final float y = ev.getY();saveInitialMotion(x, y);saveLastMotion(x, y);mDragState = STATE_DRAGGING;break;}case MotionEvent.ACTION_MOVE: {if (mDragState == STATE_DRAGGING) {float x = ev.getX();final float y = ev.getY();if (x > getWidth() - r) {x = getWidth() - r;}if (x - getPaddingLeft() <= 0) {x = getPaddingLeft();}saveLastMotion(x, y);invalidate();} else {}break;}case MotionEvent.ACTION_UP: {if (mDragState == STATE_DRAGGING) {}reslease();break;}case MotionEvent.ACTION_CANCEL: {cancel();break;}}return true;}private void reslease() {if (isClickable()) {if (x_last > getWidth() / 2) {mScroller.startScroll((int) x_last, (int) y_last,(int) getWidth() - r - (int) x_last, 0);invalidate();} else {mScroller.startScroll((int) x_last, (int) y_last,getPaddingLeft() - (int) x_last, 0);invalidate();}}}private void cancel() {if (isClickable()) {mScroller.abortAnimation();}}@Overridepublic void computeScroll() {super.computeScroll();// 先判断mScroller滚动是否完成if (mScroller.computeScrollOffset()) {// 这里调用View的scrollTo()完成实际的滚动x_last = mScroller.getCurrX();y_last = mScroller.getCurrY();// 必须调用该方法,否则不一定能看到滚动效果invalidate();}}private float x_down;private float y_down;private void saveInitialMotion(float x, float y) {x_down = x;y_down = y;}private float x_last;private float y_last;private void saveLastMotion(float x, float y) {x_last = x;y_last = y;}private void invalidateTextPaintAndMeasurements() {mTextPaint.setTextSize(mExampleDimension);mTextPaint.setColor(mExampleColor);mTextWidth = mTextPaint.measureText(mExampleString);Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();mTextHeight = fontMetrics.bottom;}@Overrideprotected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);onDraw(canvas);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// clip canvas,r = getHeight();// mPath.reset();// mPath.moveTo(r, 0);// mleft.set(0, 0, r, r);// mPath.arcTo(mleft, 90, 180);// mcenter.set(r / 2, 0, getWidth() - r / 2, r);// mPath.addRect(mcenter, Path.Direction.CW);// mPath.lineTo(getWidth() - r / 2, 0);// mright.set(getWidth() - r, 0, getWidth(), r);// mPath.arcTo(mright, 270, 180);// boolean clip = canvas.clipPath(mPath,// android.graphics.Region.Op.REPLACE);// System.out.println(clip);canvas.clipPath(mPath,android.graphics.Region.Op.REPLACE);System.out.println(r);// clip draw state,if (shader_stateFouse == null) {shader_stateFouse = new LinearGradient(0, 0, getWidth(),getHeight(), 0xffcccccc, COLOR_BLUE_LIGHT,Shader.TileMode.CLAMP);paint_state.setShader(shader_stateFouse);}paint_state.setAlpha((int) (0xff * (x_last / getWidth())));canvas.drawRect(0, 0, getWidth(), getHeight(), paint_state);// TODO: consider storing these as member variables to reduce// allocations per draw cycle.int paddingLeft = getPaddingLeft();int paddingTop = getPaddingTop();int paddingRight = getPaddingRight();int paddingBottom = getPaddingBottom();int contentWidth = getWidth() - paddingLeft - paddingRight;int contentHeight = getHeight() - paddingTop - paddingBottom;// Draw the text.canvas.drawText(mExampleString, paddingLeft+ (contentWidth - mTextWidth) / 2, paddingTop+ (contentHeight + mTextHeight) / 2, mTextPaint);// Draw the example drawable on top of the text.// if (mExampleDrawable != null) {// mExampleDrawable.setBounds(paddingLeft, paddingTop, paddingLeft// + contentWidth, paddingTop + contentHeight);// mExampleDrawable.draw(canvas);// }if (mDrawable_background_button != null) {mDrawable_background_button.getIntrinsicWidth();mDrawable_background_button.setBounds((int) x_last + paddingLeft,getHeight() / 2 - r / 2 + paddingTop, r + (int) x_last- paddingRight, getHeight() / 2 + getHeight() / 2- paddingBottom);// mDrawable_button.setBounds((int)x_last, 0,(int)x_last+r,// getHeight());mDrawable_background_button.draw(canvas);}if (mDrawable_button != null) {mDrawable_button.getIntrinsicWidth();mDrawable_button.setBounds((int) x_last + r / 2- mDrawable_button.getIntrinsicWidth() / 2,getHeight() / 2- mDrawable_button.getIntrinsicHeight() / 2,(int) x_last + r / 2+ mDrawable_button.getIntrinsicWidth() / 2,getHeight() / 2+ mDrawable_button.getIntrinsicHeight() / 2);// mDrawable_button.setBounds((int)x_last, 0,(int)x_last+r,// getHeight());mDrawable_button.draw(canvas);}}/** * Gets the example string attribute value. *  * @return The example string attribute value. */public String getExampleString() {return mExampleString;}/** * Sets the view's example string attribute value. In the example view, this * string is the text to draw. *  * @param exampleString *            The example string attribute value to use. */public void setExampleString(String exampleString) {mExampleString = exampleString;invalidateTextPaintAndMeasurements();}/** * Gets the example color attribute value. *  * @return The example color attribute value. */public int getExampleColor() {return mExampleColor;}/** * Sets the view's example color attribute value. In the example view, this * color is the font color. *  * @param exampleColor *            The example color attribute value to use. */public void setExampleColor(int exampleColor) {mExampleColor = exampleColor;invalidateTextPaintAndMeasurements();}/** * Gets the example dimension attribute value. *  * @return The example dimension attribute value. */public float getExampleDimension() {return mExampleDimension;}/** * Sets the view's example dimension attribute value. In the example view, * this dimension is the font size. *  * @param exampleDimension *            The example dimension attribute value to use. */public void setExampleDimension(float exampleDimension) {mExampleDimension = exampleDimension;invalidateTextPaintAndMeasurements();}/** * Gets the example drawable attribute value. *  * @return The example drawable attribute value. */public Drawable getExampleDrawable() {return mExampleDrawable;}/** * Sets the view's example drawable attribute value. In the example view, * this drawable is drawn above the text. *  * @param exampleDrawable *            The example drawable attribute value to use. */public void setExampleDrawable(Drawable exampleDrawable) {mExampleDrawable = exampleDrawable;}}


0 0
原创粉丝点击