Неправильное поведение межстрочного интервала EditText в целевом API 28 при работе с неанглийским Unicode (например, китайский, японский) - PullRequest
5 голосов
/ 25 апреля 2019

Мы замечаем, что во время targetSdkVersion 28 EditText будет стремиться «слегка толкнуть» строку после ввода, когда вводится неанглийский юникод (например, китайский, японский, ...).

Такое поведение не происходит, когда код targetSdkVersion 27 или ниже.


Использовать targetSdkVersion 27, запускать на API эмулятора 28

(перед вводом неанглийского Unicode)

enter image description here

(после ввода неанглийского Unicode)

enter image description here

(подтвердите интервал в порядке)

enter image description here


Использовать targetSdkVersion 28, запускать на API эмулятора 28

(перед вводом неанглийского Unicode)

enter image description here

(после ввода неанглийского Unicode)

enter image description here

(Подтвердить пробел проблематично. Линии после нажатия вводятся вниз)

enter image description here


Это используемый нами XML и код. Мы наследуем от androidx.appcompat.widget.AppCompatEditText, чтобы нарисовать линии, чтобы сделать проблему более очевидной.

<com.yocto.wenote.note.LinedEditText
    android:id="@+id/body_edit_text"
    android:gravity="top"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:layout_marginBottom="12dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:scrollbars="vertical"
    android:textSize="18sp"
    android:singleLine="false"
    android:lineSpacingMultiplier="1.4"
    android:inputType="textMultiLine|textCapSentences"
    android:textCursorDrawable="?attr/shorterCursor" />

LinedEditText.java

package com.yocto.wenote.note;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;

import com.yocto.wenote.R;

/**
 * Created by yccheok on 24/3/2018.
 */

public class LinedEditText extends androidx.appcompat.widget.AppCompatEditText {
    private final Paint mPaint = new Paint();
    private int noteLineColor;
    private static final float DEFAULT_LINE_SPACING_EXTRA = 0.0f;
    private static final float DEFAULT_LINE_SPACING_MULTIPLIER = 1.4f;

    private void initResource() {
        Context context = getContext();
        TypedValue typedValue = new TypedValue();
        Resources.Theme theme = context.getTheme();
        theme.resolveAttribute(R.attr.noteLineColor, typedValue, true);
        noteLineColor = typedValue.data;
    }

    public LinedEditText(Context context) {
        super(context);
        initResource();
        initPaint();
    }

    public void setNoteLineColor(int noteLineColor) {
        this.noteLineColor = noteLineColor;
    }

    public LinedEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initResource();
        initPaint();
    }

    public LinedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initResource();
        initPaint();
    }

    private void initPaint() {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(noteLineColor);
        mPaint.setStrokeWidth(1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int left = getLeft();
        int right = getRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        final int heightWithScrollY = getHeight() + getScrollY();
        int lineHeight = getLineHeight();
        int count = (heightWithScrollY-paddingTop-paddingBottom) / lineHeight;

        mPaint.setColor(noteLineColor);
        mPaint.setTypeface(this.getTypeface());

        final float originalLineHeight;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
            originalLineHeight = lineHeight / getLineSpacingMultiplier();
        } else {
            originalLineHeight = lineHeight / DEFAULT_LINE_SPACING_MULTIPLIER;
        }

        for (int i = 0; i < count; i++) {
            float baseline = lineHeight * (i + 1) + paddingTop - mPaint.descent() - (lineHeight - originalLineHeight);
            canvas.drawLine(
                    left + paddingLeft,
                    baseline,
                    right - paddingRight,
                    baseline,
                    mPaint
            );
        }

        super.onDraw(canvas);
    }

    // https://stackoverflow.com/questions/49467579/workaround-for-edittext-ignoring-linespacingmultiplier
    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);

        if (lengthBefore != lengthAfter) {
            float add;
            float mul;

            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                add = getLineSpacingExtra();
                mul = getLineSpacingMultiplier();
            } else {
                add = DEFAULT_LINE_SPACING_EXTRA;
                mul = DEFAULT_LINE_SPACING_MULTIPLIER;
            }

            setLineSpacing(0f, 1f);
            setLineSpacing(add, mul);
        }
    }

}

Обратите внимание, что если вы используете targetSdkVersion 28, НО работать на API эмулятора 27, эта проблема также не возникнет.

Есть ли какие-либо предложения по обходному пути?

p / s Я подал проблему на https://issuetracker.google.com/issues/131284662

Ответы [ 2 ]

1 голос
/ 25 мая 2019

Ну, мне удалось сделать это следующим образом. Моя функция onDraw:

@Override
        protected void onDraw(Canvas canvas) {
            int left = getLeft();
            int right = getRight();
            int paddingTop = getPaddingTop();
            int paddingBottom = getPaddingBottom();
            int paddingLeft = getPaddingLeft();
            int paddingRight = getPaddingRight();
            final int heightWithScrollY = getHeight() + getScrollY();
            Log.d("Height Of View: ",String.valueOf(heightWithScrollY));
            int lineHeight = getLineHeight();
            Log.d("LineHeight: ",String.valueOf(lineHeight));
            int count = (heightWithScrollY-paddingTop-paddingBottom) / lineHeight;
            Log.d("Count: ",String.valueOf(count));
            mPaint.setColor(noteLineColor);
            mPaint.setTypeface(this.getTypeface());
            Log.d("Descent: ",String.valueOf(mPaint.descent()));

            for(int i=lineHeight;i<=count*lineHeight;i+=lineHeight)
            {
                float baseline = i + paddingTop + mPaint.descent();
                canvas.drawLine(left+paddingLeft,baseline,right-paddingRight,baseline,mPaint);
                Log.d("XYXY:",String.valueOf(left+paddingLeft)+" "+String.valueOf(baseline)+" "+String.valueOf(right-paddingRight));
            }
            super.onDraw(canvas);
        }  

и я использовал представление как

 <com.yocto.wenote.note.LinedEditText
        android:id="@+id/body_edit_text"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:gravity="top"
        android:layout_marginBottom="12dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        android:textSize="18sp"
        android:singleLine="false"
        android:lineSpacingMultiplier="1.4"
        android:inputType="textMultiLine|textCapSentences"/>

И последнее, но не по значению: я использовал эту реализацию в своих зависимостях build.gradle (лично я думаю, что использование этой версии alpha05 помогло решить проблему, но я не проверял иначе)

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}  
0 голосов
/ 20 мая 2019

Вы должны запросить базовую линию для каждой существующей строки, чтобы нарисовать линию в правильном положении. Я упростил ваш код, эта версия исправит вашу проблему, я тестировал на Android P и Android Q:

@Override
protected void onDraw(Canvas canvas) {
    int left = getLeft();
    int right = getRight();
    int paddingLeft = getPaddingLeft();
    int paddingRight = getPaddingRight();

    mPaint.setColor(noteLineColor);
    mPaint.setTypeface(this.getTypeface());

    Layout layout = getLayout();
    // 1. Draw line for existing rows
    for (int i = 0; i < layout.getLineCount(); i++) {
        int baseline = layout.getLineBaseline(i);
        canvas.drawLine(
                left + paddingLeft,
                baseline,
                right - paddingRight,
                baseline,
                mPaint
        );
    }

    // 2. Draw line for non-existing rows
    final int heightWithScrollY = getHeight() + getScrollY();
    int lineHeight = getLineHeight();
    int nextBaseline = layout.getHeight() + lineHeight;
    while(nextBaseline < heightWithScrollY){
        canvas.drawLine(
                left + paddingLeft,
                nextBaseline,
                right - paddingRight,
                nextBaseline,
                mPaint
        );
        nextBaseline += lineHeight;
    }

    super.onDraw(canvas);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...