android - 处理非英文 Unicode(如中文、日文)时,目标 API 28 中不正确的 EditText 行间距行为

标签 android unicode android-edittext

我们注意到,在 targetSdkVersion 28 期间,EditText 将在输入非英语 unicode(如中文、日语等)后倾向于“稍微下推”该行.

当代码为 targetSdkVersion 27 或以下时,不会发生这种行为。


使用targetSdkVersion 27,在模拟器API 28上运行

(输入非英文unicode前)

enter image description here

(输入非英文unicode后)

enter image description here

(确认间距OK)

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 提交了一个问题

最佳答案

好吧,我设法通过以下方式做到了。我的 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);
        }  

我用 View 作为

 <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'
}  

关于android - 处理非英文 Unicode(如中文、日文)时,目标 API 28 中不正确的 EditText 行间距行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55845977/

相关文章:

android - kotlin 编译器返回此类警告

java - Google Docs 的 Google API,请求文档列表 -- 400 Bad Request

python - django 上奇怪的 UnicodeDecodeError

Android EditText 右对齐无法正常工作

java - Android - 糖库 NullPointerException

android - 从 SurfaceView 使用 ByteBuffer 时如何在 android ML-kit 中暂停条形码扫描

Oracle 神秘的 Unicode 代码点

javascript - 无法显示来自 unicode JSON 响应的表情符号

android - 在沉浸模式下,软键盘出现时状态栏出现,但软键盘消失时状态栏不隐藏

android - 如何在用户按下回车键时触发操作?