Код Android Custom View перестал правильно работать в Jelly Bean 4.2

Приведенный ниже код представляет собой пользовательское представление — оно рисует круг, добавляет выемки в соответствии с масштабом и добавляет текст масштаба. Это было взято из превосходного руководства Mind The Robot по созданию старинного термометра. http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/

Этот код отлично работает на устройствах с Jelly Bean 4.1.2, но не работает на 4.2. В версии 4.2 числа больше не рисуются по кругу, а кажутся разбросанными по всему экрану. Код отлично работал на Nexus 7, пока не было установлено обновление 4.2, так что это не может быть проблемой устройства. Я тестировал его на Nexus S с 4.1.2 и Nexus 4 с 4.2, он отлично работает на Nexus S, но не на Nexus 4.

К сожалению как новый пользователь не могу выложить скрины, попробую описать: Цифры отображаются корректно для первой половины циферблата, остальные цифры разбросаны по экрану.

Я просмотрел журнал изменений 4.2, но не вижу ничего, что могло бы вызвать это. Я искал похожие проблемы в Интернете, но все они, похоже, связаны с аппаратным ускорением — я пробовал различные комбинации настройки аппаратного ускорения в манифесте, но ничего не повлияло.

Я был бы очень признателен за любую информацию о том, что может быть причиной этого.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;

public class AneroidView extends View {


    // drawing tools
    private RectF rimRect;

    private RectF faceRect;

    private Paint scalePaint;
    private RectF scaleRect;

    private Paint backgroundPaint; 
    // end drawing tools

    private Bitmap background; // holds the cached static part

    private int totalNotches = 130;
    private int incrementPerLargeNotch = 10;
    private int incrementPerSmallNotch = 1;
    private float degreesPerNotch = 360.0f / totalNotches;  

    private int scaleCenterValue = 1000; // the one in the top center (12 o'clock)
    private int scaleMinValue = 935;
    private int scaleMaxValue = 1065;


    public AneroidView(Context context) {
        super(context);
        init(context, null);
    }

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

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

    private void init(Context context, AttributeSet attrs) {

        rimRect = new RectF(0.1f, 0.1f, 0.9f, 0.9f);

        float rimSize = 0.02f;
        faceRect = new RectF();
        faceRect.set(rimRect.left + rimSize, rimRect.top + rimSize, 
                 rimRect.right - rimSize, rimRect.bottom - rimSize);        


        scalePaint = new Paint();
        scalePaint.setStyle(Paint.Style.STROKE);
        scalePaint.setColor(Color.rgb(49, 79, 79));
        scalePaint.setStrokeWidth(0.005f);
        scalePaint.setAntiAlias(true);

        scalePaint.setTextSize(0.045f);
        scalePaint.setTypeface(Typeface.SANS_SERIF);
        scalePaint.setTextScaleX(0.8f);
        scalePaint.setTextAlign(Paint.Align.CENTER);        

        // The scale rectangular is located .10 from the outer rim.
        float scalePosition = 0.10f;

        scaleRect = new RectF();
        scaleRect.set(faceRect.left + scalePosition, faceRect.top + scalePosition,
                      faceRect.right - scalePosition, faceRect.bottom - scalePosition);

            }

    private void drawScale(Canvas canvas) {
        // Draw a large notch every large increment, and a small
        // notch every small increment.

        canvas.drawOval(scaleRect, scalePaint);

        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        for (int i = 0; i < totalNotches; ++i) {
            float y1 = scaleRect.top;
            float y2 = y1 - 0.015f;
            float y3 = y1 - 0.025f;

            int value = notchToValue(i);

            if (i % (incrementPerLargeNotch/incrementPerSmallNotch) == 0) {
                if (value >= scaleMinValue && value <= scaleMaxValue) {
                    // draw a nick
                    canvas.drawLine(0.5f, y1, 0.5f, y3, scalePaint);

                    String valueString = Integer.toString(value);
                    // Draw the text 0.15 away from y3 which is the long nick.
                    canvas.drawText(valueString, 0.5f, y3 - 0.015f, scalePaint);
                }
            }
            else{
                if (value >= scaleMinValue && value <= scaleMaxValue) {
                    // draw a nick
                    canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);
                }
            }

            canvas.rotate(degreesPerNotch, 0.5f, 0.5f);
        }
        canvas.restore();       
    }

    private int notchToValue(int value) {
        int rawValue = ((value < totalNotches / 2) ? value : (value - totalNotches)) * incrementPerSmallNotch;
        int shiftedValue = rawValue + scaleCenterValue;
        return shiftedValue;
    }


    private void drawBackground(Canvas canvas) {
        if (background != null)         
            canvas.drawBitmap(background, 0, 0, backgroundPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {      

        drawBackground(canvas);

        float scale = (float) getWidth();       
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.scale(scale, scale); 

        canvas.restore();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        regenerateBackground();
    }

    private void regenerateBackground() {
        // free the old bitmap
        if (background != null) {
            background.recycle();
        }

        background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas backgroundCanvas = new Canvas(background);
        float scale = (float) getWidth();       
        backgroundCanvas.scale(scale, scale);

        drawScale(backgroundCanvas);
    }
}

person user675067    schedule 18.01.2013    source источник
comment
Что ты понял? Это крах? У вас есть Log.d, разбросанные вокруг источника и отображаемые в логарифме?   -  person t0mm13b    schedule 18.01.2013
comment
Ответ, вероятно, скрыт в журнале изменений.   -  person Todd Sjolander    schedule 18.01.2013
comment
Не вылетает, работает без ошибок. Проблема в том, что шкала отображается неправильно. Я просмотрел журнал изменений, я не могу найти ничего, что могло бы повлиять; ясно, что я что-то упускаю там, но я в тупике.   -  person user675067    schedule 18.01.2013
comment
Хм, это очень своеобразный эффект - и, как вы сказали, протестировав на 4.1.2 и 4.2, похоже, проблема с новой версией ОС. Я также просмотрел журнал изменений и не нашел ничего, что имело бы отношение к этой проблеме. В надежде, что кто-то еще может помочь, вот несколько скриншотов, иллюстрирующих проблему :) !Dial display корректно работает на 4.1.2 !Дисплей на циферблате делает сумасшедшие спиральные вещи на 4.2   -  person BasicPleasureModel    schedule 22.01.2013


Ответы (3)


Добавить scalePaint.setLinearText(true);

Это будет работать лучше, но интервалы между текстами могут выглядеть плохо.

Смотрите темы ниже:

Android 4.2 на Nexus 7: canvas.drawText( ) работает неправильно

Android 4.2.1 неправильный кернинг символов (интервал)

person user1913325    schedule 29.01.2013
comment
на месте - как вы говорите, теперь он будет рисовать шкалу по кругу, все это с немного хитрым интервалом между текстом - большое спасибо :) - person user675067; 30.01.2013

Мне удалось обойти проблему, используя scalePaint.setLinearText(true), чтобы обойти рисование текстовых символов в одном месте, установив textSize > 1.0f, чтобы обойти проблему кернинга, а затем используя canvas.scale(float, float) для довести шрифт до нужного размера. Это уродливо и больно, но это работает для меня.

person Kaleb    schedule 31.01.2013

Вот еще один обходной путь для kerning issue. drawTextOnPath работает так...

Замените это:
//canvas.drawText("Smushed text.", 0.5f, 0.7F, myTextPaint);

с этим:

private Path strightPath; <br>
strightPath = new Path(); <br>
strightPath.moveTo(0.1f, 0.5f);<br>
strightPath.lineTo(0.9f, 0.5f); <br>
canvas.drawTextOnPath("This text is not smushed together.", strightPath, 0.0f, 0.2f, myTextPaint);
person user2063439    schedule 12.02.2013