EditText缩放选中文本

31

我有一个EditText,我想要对它进行缩放和滚动,使用 setScaleX/setScaleY 时可以正常工作——文字在正确的位置进行编辑。

但是当我尝试选择文本时,它绘制的选择句柄的位置就像文本没有缩放一样。这是已知的bug

这是预期的结果,因为句柄是绘制在与视图大小相关联的弹出窗口上的。

所有针对 android.widget.Editor 的操作都针对其字段 private TextView mTextView; 进行的。如果我们通过反射设置自己的编辑器,我不知道该怎么处理私有方法,因为它们不能被覆盖。

此外,选择句柄是在弹出窗口的 android.widget.Editor.HandleView#HandleView 上绘制的,并且坐标是在 Layout 中计算的,但我们只需要 DynamicLayout,但对于我们的目的来说,它并没有什么区别。

方法 android.text.Layout#getPrimaryHorizontal(int, boolean) 是公共的,它的值可以乘以比例,但为此我们需要扩展和重写私有方法 android.widget.TextView#makeSingleLayout,但这是一个问题。

我们也可以实现自己的 Layout,并覆盖所有必需的方法,但我们可以覆盖的所有方法都带有 @hide 注释,而没有可以通过反射访问的字段。

下面是缩放了 2 倍的屏幕截图:

enter image description here

附:任务的上下文是具有捏合缩放编辑文本的编辑器。重新布局文本并计算大小不是一个解决方案,因为我需要在每个屏幕大小上使用可移植的文档。


1
请考虑对那堵文字墙进行格式化。这样阅读起来会更容易。 - Gerstmann
抱歉,@Avanz。目前我无法接受您的解决方案,因为未经检查的解决方案无法获得批准。对我来说,许多跨度具有度量影响。此外,我有很多自定义跨度,因此在这个解决方案中,我应该覆盖每一个以返回缩放大小。我有22个自定义跨度,包括字符和段落跨度。首先,我会尝试您的解决方案,然后再确定是否接受。 - Yevgen Kulik
好的,请慢慢来,有什么就告诉我。 - Avanz
此功能已移至第二个版本。实际日期为八月。 - Yevgen Kulik
2个回答

13

您可以使用MetricAffectingSpan来做到这一点。以下是一个展示它的类:

package android.text.style;

import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;

public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {

    private final int mSize;
    private boolean mDip;

    /**
     * Set the text size to <code>size physical pixels.
     */
    public AbsoluteSizeSpan(int size) {
        mSize = size;
    }

    /**
     * Set the text size to <code>size physical pixels,
     * or to <code>size device-independent pixels if
     * <code>dip is true.
     */
    public AbsoluteSizeSpan(int size, boolean dip) {
        mSize = size;
        mDip = dip;
    }

    public AbsoluteSizeSpan(Parcel src) {
        mSize = src.readInt();
        mDip = src.readInt() != 0;
    }

    public int getSpanTypeId() {
        return TextUtils.ABSOLUTE_SIZE_SPAN;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mSize);
        dest.writeInt(mDip ? 1 : 0);
    }

    public int getSize() {
        return mSize;
    }

    public boolean getDip() {
        return mDip;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        if (mDip) {
            ds.setTextSize(mSize * ds.density);
        } else {
            ds.setTextSize(mSize);
        }
    }

    @Override
    public void updateMeasureState(TextPaint ds) {
        if (mDip) {
            ds.setTextSize(mSize * ds.density);
        } else {
            ds.setTextSize(mSize);
        }
    }
}

参考资料:Java源代码仓库项目

您需要使用MetricAffectingSpanwrap(CharacterStyle cs)来完成对给定Spanned的单个区域应用CharacterStyle

在子类中,重写onTouch并将其值传递给ScaleGestureDetector。将检测到的比例作为成员变量存储。

覆盖onDraw,在调用super.onDraw之前使用你的比例值调用canvas.scale()。正如您可以在AbsoluteSizeSpan中注意到的那样,使用AbsoluteSizeSpan(Parcel src)将获得您想要调整大小的文本,然后应用updateDrawState


1
你能再解释一下这个修复方案是如何解决句柄未按正确大小渲染的问题的吗? - N_A
1
我真的没有时间尝试解决方案。但是我很快会回来看看。如果有人已经检查过它,请评论一下。Avanz,感谢你给出了最完整的答案。 - Yevgen Kulik
/** @hide */ public static final int ABSOLUTE_SIZE_SPAN = 16; - Yazon2006

2

你尝试过为这些元素应用不同的样式吗?我认为可以通过一些条件来实现。

<item name="android:textSelectHandle">@drawable/text_select_handle_middle</item>
<item name="android:textSelectHandleLeft">@drawable/text_select_handle_left</item>
<item name="android:textSelectHandleRight">@drawable/text_select_handle_right</item>

你能解释一下你认为如何解决不当调整大小问题吗? - N_A
问题在于你正在处理的错误不是我所知道的任何标准方法都可以“解决”的。而且你可能已经尝试过一些非标准的方法。我想提出的建议是设置选择的样式并动态更改可绘制对象,在此过程中进行缩放(编辑:和定位)。请注意,我还没有尝试过这个,也许明天我会试着摆弄一下,因为格林威治标准时间正在折磨我。 - user1487189
啊,你认为如果在调整大小完成后更改样式,手柄可能会适当地调整大小? - N_A
这只是一个想法,我猜值得一试。 - user1487189

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接