Xfermode в бета-версии Android P

Слева - скриншот, сделанный на бета-версии Android P, а справа - на Android 26. Кривые различия Кажется, есть несоответствия в том, как Xfermode работает на Android P бета.

Ниже приведен соответствующий код.

public class CropView extends View {

    private Paint paint;
    private Path clipPath;
    private int arcHeight;

    public CropView(Context context) {
        super(context);
        init();
    }

    public CropView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CropView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        arcHeight = getResources().getDimensionPixelOffset(R.dimen.row_size);
        setLayerType();
    }

    private void setLayerType() {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
            // looks like this is happening in some devices with lollipop and kitkat
            // trying to fix https://github.com/lifesum/bugs/issues/7040
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        } else {
            setLayerType(LAYER_TYPE_HARDWARE, null);
        }
    }

    private Path createClipPath(int height, int width) {
        final Path path = new Path();

        path.moveTo(0, 0);
        path.lineTo(0, height);
        path.quadTo(width / 2, height + arcHeight, width, height - arcHeight);

        path.lineTo(width, 0);
        path.close();

        return path;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            int height = getMeasuredHeight();
            int width = getMeasuredWidth();
            clipPath = createClipPath(height, width);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (clipPath != null) {
            canvas.drawPath(clipPath, paint);
        }
    }
}

Ниже приведены скриншоты, когда запускается пример Apidemos от Android.

Android P Android P Android 27 Android 27

2 ответа

Согласно ответу, данному Google, это предполагаемое поведение.
https://issuetracker.google.com/issues/111819103

Они дали 3 варианта

  1. Используйте android.view.ViewOutlineProvider (согласно примеру здесь https://github.com/googlesamples/android-ClippingBasic).
  2. Замените ImageView на BitmapShader, который является быстрым (каждый пиксель рисуется один раз) и более переносимым способом, поскольку BitmapShader поддерживается на уровне API 1. Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStyle(Paint.Style.FILL); Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.spiderman); Shader shader = новый BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paint.setShader(шейдер); Углы пути = новый путь (); corners.addRoundRect(границы, радиусы, Path.Direction.CW); canvas.drawPath(углы, краска);
  3. Нарисуйте обратный путь с SRC_OVER поверх ImageView. Это работает, если фон сплошного цвета и он медленнее, потому что некоторые пиксели отрисовываются дважды.

Первый из них не может быть использован для рендеринга сложного сложного пути, как указано в https://issuetracker.google.com/issues/37064491

Попробуйте два других варианта и опубликуйте результаты здесь.

Да, я столкнулся с той же проблемой.

Может быть, Android-Pie, похоже, не поддерживает BoxDuff.Mode на drawPath ...

Но вы все еще можете использовать drawBitmap, чтобы вырезать слои.

Например:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (clipPathBitmap != null) {
        canvas.drawBitmap(clipPathBitmap, 0, 0, paint);
    }
}

private void makeClipPathBitmap() {
    clipPathBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(mShapeBitmap);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    p.setColor(Color.BLACK);
    clipPath.draw(c, p);
}
Другие вопросы по тегам