您的位置:首页 > 移动开发 > Android开发

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

2014-10-15 10:25 369 查看
我们知道,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默认开启了硬件加速

Hardware Acceleration

硬件加速具体的介绍见官方文档
http://developer.android.com/guide/topics/graphics/hardware-accel.html
下面是硬件加速不支持的API和SDK等级对照表

API level
< 171718
Support for large scale factors
drawText()
drawPosText()
drawTextOnPath()
Simple Shapes*
Complex Shapes*
drawPath()
Shadow layer
API level
< 16161718
Canvas
drawBitmapMesh() (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
Paint
setAntiAlias() (for text)
setAntiAlias() (for lines)
setFilterBitmap()
setLinearText()
setMaskFilter()
setPathEffect() (for lines)
setRasterizer()
setShadowLayer() (other than text)
setStrokeCap() (for lines)
setStrokeCap() (for points)
setSubpixelText()
Xfermode
AvoidXfermode
PixelXorXfermode
PorterDuff.Mode.DARKEN (framebuffer)
PorterDuff.Mode.LIGHTEN (framebuffer)
PorterDuff.Mode.OVERLAY (framebuffer)
Shader
ComposeShader 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 attributes
final 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 object
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextAlign(Paint.Align.LEFT);
// Set up a default TextPaint object
mMaskPaint = 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 attributes
invalidateTextPaintAndMeasurements();

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;

@Override
protected 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);
}

@Override
public 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();
}

}

@Override
public 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;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
onDraw(canvas);
}
@Override
protected 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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: