【问题标题】:Blurry image after canvas rotate, only in Android 6画布旋转后图像模糊,仅在 Android 6 中
【发布时间】:2016-08-20 12:13:57
【问题描述】:

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

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);
}

大多数情况下,这会产生非常清晰的图像。但是,有时结果如下所示: 这似乎只发生在我的两个测试设备之一上。该设备是摩托罗拉 moto G,升级了 Android 6。另一台似乎总是能产生非常清晰的图像的测试设备是 Oneplus X,Android 5。它也不一致,有时会发生,然后下一刻不会再次发生。从我能够测试的情况来看,它甚至不取决于应用的旋转量。不过,我从未见过它发生在直角(0、90、180 度)上,而且在接近 45 或 135 度的角度似乎更糟。

有问题的图像是一个导入的 SVG,直接放在 res/drawable 文件夹中。因此它不可能是分辨率。 (此外,gauge_outer 被放置在完全相同的文件夹中,并以完全相同的方式制作,尽管这个不会变得模糊。)

关于如何解决这个问题的任何想法?


编辑:

好的,不要介意我所说的完全不一致。它似乎是完全一致的,并且当旋转越来越接近 90 度时最差。此外,只要旋转正好 90 度,指示器就会完全消失。


编辑:

看:两个模拟器,一个运行 Android 5,一个运行 Android 6:

完整源码如下:

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);
    }
}

带有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>

和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>

【问题讨论】:

  • 图片在资源文件夹中的什么位置?
  • 真的是canvas.rotate引起的吗?如果你删除那些.rotate 行,它看起来还可以吗?
  • @weston 我更新了我的问题,不幸的是,由于问题的性质不一致,删除 .rotate 并不是真正可行的测试策略。看起来像 0 度的旋转不会导致任何问题,但实际上是相同的。为了回答你的问题,我不认为 canvas.rotate 是原因,而是一个因素。
  • 好的,然后将outerGauge.draw(canvas);移动到旋转后。想知道它们是否一起模糊?
  • @weston 否定的,它没有。它产生完全相同的图像。

标签: android android-6.0-marshmallow java-canvas


【解决方案1】:

虽然不是答案,但我设法找到了解决方法。此解决方法依赖于将图像绘制到画布上,该画布链接到位图,然后在 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 提交错误报告

【讨论】:

  • 谢谢,提到 svg 旋转引起的麻烦真的很有帮助
猜你喜欢
  • 1970-01-01
  • 2017-09-26
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
  • 2013-07-25
  • 1970-01-01
  • 2021-09-27
  • 2015-10-01
相关资源
最近更新 更多