画布旋转后图像模糊,仅在Android 6中

丹尼尔·范登伯格

我有一个使用以下代码的自定义视图:

private final Drawable outerGauge;
private final Drawable innerGauge;
private float rotateX;
private float rotateY;
private int rotation = 0;

{
    outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
    innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    outerGauge.draw(canvas);
    canvas.rotate(rotation, rotateX, rotateY);
    innerGauge.draw(canvas);
    canvas.rotate(-rotation, rotateX, rotateY);
}

在大多数情况下,这会产生清晰的图像。但是,有时结果看起来像这样:不太清楚这似乎只发生在我的两个测试设备之一上。该设备是配备了Android 6升级功能的Motorola motoG。另一个似乎总是能产生清晰图像的测试设备是Oneplus X,Android5。它也不是一贯的,有时会发生,然后在下一刻不再出现。根据我的测试,它甚至不取决于所应用的旋转量。我从未见过它会在直角(0、90、180度)上发生,并且在接近45度或135度的角度看来确实更糟。

The image in question is an imported SVG, placed directly in the res/drawable folder. Therefore it can't be the resolution. (Also, gauge_outer is placed in exactly the same folder and made exactly the same way, though this one does not become blurry.)

Any ideas on how to solve this?


Edit:

Okay, never mind what I said about the complete inconsistency. It appears to be fully consistent, and be worst when the rotation comes closer and closer to 90 degrees. Also, as soon as the rotation is exactly 90 degrees, the indicator completely disappears.


Edit:

Behold: two emulators, one running Android 5 and one running Android 6:

安卓5安卓6

The full source code is as follows:

package nl.dvandenberg.gauge;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

public class GaugeView extends View {
    private static final int ORIGINAL_ROTATE_Y = 510;
    private static final int ORIGINAL_IMAGE_HEIGHT = 613;
    private static final int ORIGINAL_IMAGE_WIDTH = 1046;
    private final Drawable outerGauge;
    private final Drawable innerGauge;
    private float rotateX;
    private float rotateY;
    private int rotation = 0;

    {
        outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
        innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
    }

    public GaugeView(Context context) {
        super(context);
        setProgress(48);
    }

    public GaugeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setProgress(48);
    }

    public GaugeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setProgress(48);
    }

    public void setProgress(double percentage) {
        this.rotation = (int) (180 * Math.min(100, Math.max(0, percentage)) / 100);
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        double width = MeasureSpec.getSize(widthMeasureSpec);
        double idealHeight = ORIGINAL_IMAGE_HEIGHT * width / ORIGINAL_IMAGE_WIDTH;
        double height = Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec));
        width = width * height / idealHeight;
        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.getMode(heightMeasureSpec));

        rotateX = (float) (width / 2f);
        rotateY = (float) (height / ORIGINAL_IMAGE_HEIGHT * ORIGINAL_ROTATE_Y);

        outerGauge.setBounds(0, 0, (int) width, (int) height);
        innerGauge.setBounds(0, 0, (int) width, (int) height);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        outerGauge.draw(canvas);
        canvas.rotate(rotation, rotateX, rotateY);
        innerGauge.draw(canvas);
    }
}

with drawable/gauge_inner.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="1046dp"
    android:height="613dp"
    android:viewportWidth="1046"
    android:viewportHeight="613">

    <path
        android:fillColor="#aa3939"
        android:pathData="M142.541,516.071 C145.053,517.623,156.088,519.334,183.255,522.586
C203.832,525.024,251.438,530.676,289.03,535.184
C326.708,539.641,359.782,543.523,362.537,543.896
C365.292,544.268,388.127,547.018,413.445,550.067 L459.289,555.468
L462.946,560.401 C468.075,567.485,479.691,577.405,489.255,582.968
C499.701,589.062,520.069,594.737,531.817,594.883
C571.623,595.225,607.57,570.083,620.01,533.226
C624.956,518.592,626.123,507.412,624.269,492.201
C622.686,479.259,620.262,472.461,612.212,458.518
C602.012,440.852,592.681,431.69,575.424,422.602
C537.988,402.763,489.163,413.401,462.78,447.108 L458.957,452.086
L449.523,453.146 C444.316,453.727,420.115,456.614,395.829,459.552
C371.456,462.538,346.451,465.429,340.177,466.165
C333.904,466.9,293.067,471.772,249.427,476.991
C205.788,482.211,164.951,487.082,158.678,487.817
C144.122,489.408,139.036,491.998,136.796,498.719
C134.433,505.626,136.72,512.388,142.541,516.07 Z" />
</vector>

and drawable/gauge_outer.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="1046dp"
    android:height="613dp"
    android:viewportWidth="1046"
    android:viewportHeight="613">

    <path
        android:fillColor="#aa3939"
        android:pathData="M488.981,0.56719 C465.882,2.06727,430.783,6.96753,412.984,11.0677
C392.285,15.768,387.285,17.0681,375.285,20.6683
C231.691,63.4706,113.696,164.376,49.898,299.183
C16.6993,369.187,0,444.491,0,523.495
C0,540.296,0.0999961,541.696,1.99992,543.596
C3.99984,545.596,5.29979,545.596,59.4977,545.596
C113.696,545.596,114.996,545.596,116.995,543.596
C118.895,541.696,118.995,540.296,118.995,522.595
C118.995,504.894,118.895,503.494,116.995,501.594
C115.095,499.694,113.695,499.594,85.2962,499.594 L55.6974,499.594
L56.2974,489.793 C60.0973,433.69,76.3966,372.387,101.396,320.384
C103.996,314.984,106.496,310.383,106.896,310.183
C107.396,309.883,110.796,311.483,114.596,313.683
C118.396,315.983,124.396,319.483,127.995,321.583
C131.595,323.583,139.195,328.083,144.994,331.484
C155.694,337.684,159.993,338.884,163.193,336.284
C164.893,334.984,171.293,324.483,177.992,312.083
C183.292,302.282,183.092,299.882,176.492,295.782
C173.992,294.282,162.593,287.582,151.093,281.081 L130.294,269.08 L135.294,261.58
C166.593,214.877,210.691,170.375,258.589,137.273
C268.189,130.673,269.889,129.873,270.489,131.273
C272.389,136.273,298.388,179.776,299.988,180.576
C300.988,181.176,302.788,181.576,303.888,181.576
C306.288,181.576,334.787,165.275,336.787,162.775
C339.187,159.575,337.987,155.575,330.887,143.274
C326.987,136.574,322.987,129.773,322.087,128.273
C321.187,126.673,318.087,121.273,315.287,116.372
C312.387,111.372,309.987,107.072,309.987,106.671
C309.987,105.371,342.586,90.7702,360.385,84.0698
C388.684,73.5692,427.382,63.5687,455.981,59.6685
C468.68,57.8684,490.98,55.5683,495.579,55.5683 L499.979,55.5683 L499.979,85.0699
C499.979,113.271,500.079,114.671,501.979,116.572
C503.879,118.472,505.279,118.572,522.978,118.572
C540.677,118.572,542.077,118.472,543.977,116.572
C545.877,114.672,545.977,113.272,545.977,84.8703 L545.977,55.2687
L555.977,55.9687 C581.776,57.5688,617.875,63.7691,644.874,71.0695
C670.273,77.9699,702.072,89.7705,722.771,99.871
C729.071,102.971,734.671,105.671,735.271,105.871
C735.871,106.071,730.171,117.072,722.172,131.072
C713.772,145.773,707.973,156.973,707.973,158.573
C707.973,162.273,709.373,163.573,718.973,169.274
C741.272,182.375,743.072,183.075,746.772,179.775
C748.472,178.375,765.571,149.773,773.871,134.373 L776.471,129.773
L787.471,137.373 C834.969,170.075,877.067,212.377,910.266,260.98
C912.866,264.78,914.866,268.28,914.766,268.78
C914.566,269.28,903.866,275.78,890.967,283.181
C878.068,290.581,866.668,297.582,865.768,298.782
C862.268,302.782,863.268,305.182,878.268,330.084
C884.168,339.785,886.468,339.885,900.967,331.484
C906.767,328.084,914.366,323.584,917.966,321.583
C921.566,319.483,927.566,315.983,931.365,313.683
C935.265,311.383,938.565,309.583,938.865,309.583
C939.565,309.583,946.665,324.184,952.164,337.084
C972.463,383.986,986.363,440.49,989.663,489.792 L990.263,499.592
L960.664,499.592 C932.265,499.592,930.865,499.692,928.965,501.592
C927.065,503.492,926.965,504.892,926.965,522.593
C926.965,540.294,927.065,541.694,928.965,543.594
C930.965,545.594,932.265,545.594,986.463,545.594
C1041.86,545.594,1041.96,545.594,1044.06,543.494
C1046.26,541.294,1046.26,540.994,1045.66,513.192
C1044.76,470.69,1040.36,436.088,1031.36,398.586
C1027.46,382.685,1026.86,380.485,1020.26,360.084
C1009.06,325.382,990.461,284.58,971.762,253.578
C923.864,174.276,855.866,108.873,775.07,64.3706
C712.572,29.8688,645.075,8.96764,574.477,2.06727
C555.278,0.16716,507.68,-0.63288,488.981,0.56719 Z" />
</vector>
Daniël van den Berg

虽然不是答案,但我设法找到了解决方法。此解决方法依赖于将图像绘制到画布上,该画布链接到位图,然后在onDraw方法中绘制到最终旋转的画布上。

看来这个问题实际上仅在nodpi-drawables出现,换句话说,就是导入的svg。但是,这是非常一致的。不管形状是多径矢量还是简单的正方形都没有关系,问题将始终采用完全相同的形状,当画布旋转90°时图像会完全消失。

我用来绕过此问题的完整代码如下:

package nl.dvandenberg.energymonitor.customViews;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

import nl.dvandenberg.energymonitor.R;

public class GaugeView extends View {
    private static final int ORIGINAL_ROTATE_Y = 510;
    private static final int ORIGINAL_IMAGE_HEIGHT = 613;
    private static final int ORIGINAL_IMAGE_WIDTH = 1046;
    private final Drawable outerGauge, innerGauge;
    private float rotateX;
    private float rotateY;
    private int rotation = 0;

    private Bitmap innerGaugeBitmap;

    private final Canvas innerGaugeCanvas;

    {
        outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
        innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
        innerGaugeCanvas = new Canvas();
    }

    public GaugeView(Context context) {
        super(context);
    }

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

    public GaugeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setProgress(double percentage) {
        this.rotation = (int) (180 * Math.min(100, Math.max(0, percentage)) / 100);
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        double width = MeasureSpec.getSize(widthMeasureSpec);
        double idealHeight = ORIGINAL_IMAGE_HEIGHT * width / ORIGINAL_IMAGE_WIDTH;
        double height = Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec));
        width = width * height / idealHeight;
        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.getMode(heightMeasureSpec));

        rotateX = (float) (width / 2f);
        rotateY = (float) (height / ORIGINAL_IMAGE_HEIGHT * ORIGINAL_ROTATE_Y);

        outerGauge.setBounds(0, 0, (int) width, (int) height);
        innerGauge.setBounds(0, 0, (int) width, (int) height);

        if (innerGaugeBitmap != null){
            innerGaugeBitmap.recycle();
        }
        innerGaugeBitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); // Gives LINT-warning draw-allocation, but no other way to upscale bitmaps exists.
        innerGaugeCanvas.setBitmap(innerGaugeBitmap);
        innerGaugeBitmap.eraseColor(Color.TRANSPARENT);
        innerGauge.draw(innerGaugeCanvas);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        outerGauge.draw(canvas);
        canvas.rotate(rotation, rotateX, rotateY);
        canvas.drawBitmap(innerGaugeBitmap,0,0,null);
    }
}

重要的部分出现在onMeasure方法中:

        if (innerGaugeBitmap != null){
            innerGaugeBitmap.recycle();
        }
        innerGaugeBitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); // Gives LINT-warning draw-allocation, but no other way to upscale bitmaps exists.
        innerGaugeCanvas.setBitmap(innerGaugeBitmap);
        innerGaugeBitmap.eraseColor(Color.TRANSPARENT);
        innerGauge.draw(innerGaugeCanvas);

我已在https://code.google.com/p/android/issues/detail?id=208453提交了错误报告

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

旋转后图像显示在背景中

来自分类Dev

在画布中旋转图像

来自分类Dev

在画布中旋转图像

来自分类Dev

仅旋转画布中的图像

来自分类Dev

使用鼠标旋转画布中的图像

来自分类Dev

在HTML画布中单独旋转平铺图像

来自分类Dev

使用鼠标旋转画布中的图像

来自分类Dev

在 Node Webkit 中渲染到画布的图像是模糊的

来自分类Dev

Android中的多点触控画布旋转

来自分类Dev

仅在牛轧糖设备中通过意图共享后图像不可见

来自分类Dev

如何仅在CSS中模糊背景图像

来自分类Dev

HTML 画布:旋转和平移图像以将图像中的点固定到画布中的坐标

来自分类Dev

CIFilter后图像旋转

来自分类Dev

调整图像大小,然后在画布中旋转-javascript

来自分类Dev

画布中的模糊svg

来自分类Dev

elasticsearch 6 中的模糊建议

来自分类Dev

在 android studio 中,即使导入后图像也未显示在“显示”部分中

来自分类Dev

在Android中清除画布图像或绘画

来自分类Dev

在画布中旋转精灵

来自分类Dev

单指缩放并在Android中的画布位图上旋转

来自分类Dev

Android:旋转并显示文件中的图像

来自分类Dev

画廊中的图像会自动旋转-Android

来自分类Dev

画廊中的图像会自动旋转-Android

来自分类Dev

通过从图像中心旋转并缩放来拉直HTML5画布中的图像

来自分类Dev

Swift中UIImageWriteToSavedPhotosAlbum之后图像的URL

来自分类Dev

如何在Android中模糊图像的某些部分?

来自分类Dev

在Android中模糊图像的一部分

来自分类Dev

在角度为 6 的库中创建画布

来自分类Dev

从解析加载后图像变得模糊。com