Android: Xfermode маскировка исчезает на 180 градусов вращения холста
Меня вдохновляют новые анимации Material Design, и я работал над созданием аналогичного рисунка, который используется в новой поддержке v7 Action Bar Drawer Toggle.
Я создал CustomDrawable. Все, что я на самом деле сделал, - это создал треугольник "Воспроизведение" на холсте и приостановил логотип слева от левого поля видимого холста. Я вращаю холст в соответствии с прогрессом и восстанавливаю его. Затем я использовал Xfermode, чтобы обрезать повернутый результат в круг.
Я не могу найти решение проблемы.
Проблема в том, что xFermode не применяется к результату, повернутому на 180 градусов (после вызова canvas.restore()).
Вот код активности.
public class MainActivity extends Activity{
ImageView iv;
CustomDrawable drawable = new CustomDrawable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
iv = (ImageView) findViewById(R.id.button);
iv.setLayerType(View.LAYER_TYPE_HARDWARE, null);
iv.setBackgroundDrawable(drawable);
iv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
float[] values = { 0, 1 };
if (drawable.getProgress() != 0) {
values[0] = drawable.getProgress();
values[1] = 0;
}
ObjectAnimator animator = ObjectAnimator.ofFloat(drawable,
"progress", values);
animator.setDuration(2000);
animator.start();
}
});
}
}
И код для CustomDrawable
public class CustomDrawable extends Drawable {
private float mProgress = 0;
private Paint mPaint = new Paint();
private Path mPath = new Path();
private final float rootTwo = (float) Math.sqrt(2);
private final float rootThree = (float) Math.sqrt(3);
private float radius = 0;
private float side = 0;
private Point[] triangle = new Point[3];
Paint xferpaint = new Paint();
Canvas cropper;
Bitmap bitmap;
Interpolator interpolator = new AnticipateOvershootInterpolator();
private float width;
Rect rec1, rec2;
public CustomDrawable() {
mPaint.setAntiAlias(true);
mPaint.setStyle(Style.FILL);
xferpaint.setColor(Color.RED);
xferpaint.setStyle(Style.FILL);
xferpaint.setAntiAlias(true);
}
@Override
public void draw(Canvas canvas) {
canvas.getClipBounds(bound);
boundsf.set(bound);
if (radius == 0) {
radius = Math.min(bound.centerX(), bound.centerY());
radius -= 5;
bitmap = Bitmap.createBitmap(bound.width(), bound.height(),
Config.ARGB_8888);
cropper = new Canvas(bitmap);
cropper.drawCircle(bound.centerX(), bound.centerY(), radius,
xferpaint);
xferpaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
side = rootTwo * radius;
triangle[0] = new Point(
(int) (bound.centerX() + (side / rootThree)),
bound.centerY());
triangle[1] = new Point(bound.centerX()
- (int) (side / (2 * rootThree)), bound.centerY()
- (int) (side / 2));
triangle[2] = new Point(bound.centerX()
- (int) (side / (2 * rootThree)), bound.centerY()
+ (int) (side / 2));
width = side / 4;
rec1 = new Rect((int) (-bound.centerX() - (3 * width / 2)),
(int) (bound.centerY() - (side / 2)),
(int) (-bound.centerX() - (width / 2)),
(int) (bound.centerY() + (side / 2)));
rec2 = new Rect((int) (-bound.centerX() + (width / 2)),
(int) (bound.centerY() - (side / 2)),
(int) (-bound.centerX() + (3 * width / 2)),
(int) (bound.centerY() + (side / 2)));
}
mPath.rewind();
mPath.moveTo(triangle[0].x, triangle[0].y);
mPath.lineTo(triangle[1].x, triangle[1].y);
mPath.lineTo(triangle[2].x, triangle[2].y);
mPath.close();
mPaint.setColor(Color.parseColor("#378585"));
canvas.drawPaint(mPaint);
mPaint.setColor(Color.parseColor("#FF0400"));
canvas.rotate(180 * interpolator.getInterpolation(mProgress), 0,
bound.centerY());
canvas.drawPath(mPath, mPaint);
canvas.drawRect(rec1, mPaint);
canvas.drawRect(rec2, mPaint);
canvas.restore();
canvas.drawBitmap(bitmap, 0, 0, xferpaint);
}
@Override
public int getOpacity() {
return mPaint.getAlpha();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter filter) {
mPaint.setColorFilter(filter);
}
public float getProgress() {
return mProgress;
}
public void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
}
1 ответ
Это ваш упрощенный Drawable:
class CustomDrawable extends Drawable implements ValueAnimator.AnimatorUpdateListener {
private float mProgress = 0;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Path mPath;
private Path mClipPath;
public CustomDrawable() {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(0xffFF0400);
}
@Override
protected void onBoundsChange(Rect bounds) {
mClipPath = new Path();
int cx = bounds.centerX();
int cy = bounds.centerY();
int radius = Math.min(cx, cy) - 5;
mClipPath.addCircle(cx, cy, radius, Path.Direction.CCW);
final float rootTwo = (float) Math.sqrt(2);
final float rootThree = (float) Math.sqrt(3);
float side = rootTwo * radius;
mPath = new Path();
mPath.moveTo(cx + (side / rootThree), cy);
mPath.lineTo(cx - side / (2 * rootThree), cy - side / 2);
mPath.lineTo(cx - side / (2 * rootThree), cy + side / 2);
mPath.close();
float width = side / 4;
addRect(-cx - (3 * width / 2), cy - (side / 2), width, side);
addRect(-cx + (width / 2), cy - (side / 2), width, side);
}
private void addRect(float l, float t, float dx, float dy) {
mPath.addRect(l, t, l + dx, t + dy, Path.Direction.CCW);
}
@Override
public void draw(Canvas canvas) {
canvas.clipPath(mClipPath);
canvas.drawColor(0xff378585);
Rect bounds = getBounds();
canvas.rotate(mProgress, 0, bounds.centerY());
canvas.drawPath(mPath, mPaint);
}
public void switchIcons() {
float[] values = { 0, 180 };
if (mProgress != 0) {
values[0] = mProgress;
values[1] = 0;
}
ValueAnimator animator = ValueAnimator.ofFloat(values).setDuration(2000);
animator.setInterpolator(new AnticipateOvershootInterpolator());
animator.addUpdateListener(this);
animator.start();
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mProgress = (Float) animation.getAnimatedValue();
invalidateSelf();
}
@Override
public int getOpacity() {
return mPaint.getAlpha();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter filter) {
mPaint.setColorFilter(filter);
}
}
РЕДАКТИРОВАТЬ: это draw()
метод без Canvas.clipPath
и без создания "маски" Bitmap
:
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xff378585);
canvas.save();
canvas.rotate(mProgress, 0, cy);
canvas.drawPath(mPath, mPaint);
canvas.restore();
canvas.saveLayer(null, mDstInPaint, 0);
canvas.drawCircle(cx, cy, radius, mPaint);
canvas.restore();
}
где: mDstInPaint является Paint
объект с установленным xfermode в конструкторе Drawable:
mDstInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));