你能解释一下TextView + Gravity + SingleLine和Canvas的行为吗?

时间:2014-08-26 08:23:41

标签: android canvas textview

我有一个自定义的TextView,它覆盖了onDraw方法,在文本视图周围绘制一个红色边框。

当我将引力设置为左边的其他内容 AND singleLine = true 时:未绘制边框。

  • 有什么理由吗?
  • 有解决方法吗?

以下屏幕截图说明了问题:第二个textView没有边框 enter image description here

TL表示TOP | LEFT; C表示CENTER; SL意味着单行; T意味着真实; F表示错误

这是代码(可以复制/粘贴和运行 - 不需要布局文件)

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.RelativeLayout;
import android.widget.TextView;


public class MyActivity extends Activity {

    private final int TEXT_VIEW_WIDTH_PX = 400;
    private final int TEXT_VIEW_HEIGHT_PX = 60;
    private Paint borderPaint = new Paint();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        RelativeLayout rootView = new RelativeLayout(this);
        getWindow().setContentView(rootView);

        borderPaint.setColor(Color.RED);

        MyTextView text1 = makeTextView("Grav=TL ; SL=F",100);
        rootView.addView(text1);

        MyTextView text2 = makeTextView("Grav=C ; SL=T",200);
        //combination of the 2 following attributes cause the issue
        text2.setSingleLine(true);
        text2.setGravity(Gravity.CENTER);
        rootView.addView(text2);

        MyTextView text3 = makeTextView("Grav=C ; SL=F",300);
        text3.setGravity(Gravity.CENTER);
        rootView.addView(text3);

        MyTextView text4 = makeTextView("Grav=TL ; SL=T",400);
        text4.setSingleLine(true);
        rootView.addView(text4);
    }

    /**
     * Custom TextView with red border
     */
    private class MyTextView extends TextView {

        private MyTextView(Context context) {
            super(context);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawLine(1,1,1,TEXT_VIEW_HEIGHT_PX-1,borderPaint);
            canvas.drawLine(1,1,TEXT_VIEW_WIDTH_PX-1,1,borderPaint);
            canvas.drawLine(TEXT_VIEW_WIDTH_PX-1,1,TEXT_VIEW_WIDTH_PX-1,TEXT_VIEW_HEIGHT_PX-1,borderPaint);
            canvas.drawLine(1,TEXT_VIEW_HEIGHT_PX-1,TEXT_VIEW_WIDTH_PX-1,TEXT_VIEW_HEIGHT_PX-1,borderPaint);
        }
    }


    /**
     * create a MyTextView with 'text' context and located at x=50 ; y=marginTop
     * (nothing relevant for the issue here)
     */
    private MyTextView makeTextView(String text, int marginTop){
        MyTextView textView = new MyTextView(this);
        textView.setText(text);
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(TEXT_VIEW_WIDTH_PX,TEXT_VIEW_HEIGHT_PX);
        lp.topMargin = marginTop;
        lp.leftMargin = 50;
        textView.setLayoutParams(lp);
        return textView;
    }

}

2 个答案:

答案 0 :(得分:7)

感谢pskink的彻底研究(来自对我的另一个答案的评论):

  

如果您想知道为什么自定义onDraw没有绘制任何内容,只需Log.d canvas.getMatrix()的值。

     

[它]似乎画布被翻译/水平滚动(至少在我的情况下)左侧8159像素,因此调用canvas.translate(8159, 0)修复了问题,当然8159不是幻数并且可以变化。

     

我找到了,在VERY_WIDE中看TextView常数,设置为16384(2 ** 14),在我的情况下TextView的宽度为66,现在(16384- 66)/ 2 == 8159,瞧!

     

...但是VERY_WIDE是私有的,所以你无法访问它: - (

从这里开始,我想知道是否可以通过getScrollX()轻松地以编程方式检索偏移量,并确保它可以检索到。这种方法通过取消水平滚动来转换画布而不是“黑客”单行。它更自然地显示单行。

内部自定义TextView

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // translate the canvas before drawing onto it, fixing the position
    canvas.translate(getScrollX(), 0);

    canvas.drawLine(1, 1, 1, TEXT_VIEW_HEIGHT_PX - 1, borderPaint);
    canvas.drawLine(1, 1, TEXT_VIEW_WIDTH_PX - 1, 1, borderPaint);
    canvas.drawLine(TEXT_VIEW_WIDTH_PX - 1, 1, TEXT_VIEW_WIDTH_PX - 1,
        TEXT_VIEW_HEIGHT_PX - 1, borderPaint);
    canvas.drawLine(1, TEXT_VIEW_HEIGHT_PX - 1, TEXT_VIEW_WIDTH_PX - 1,
        TEXT_VIEW_HEIGHT_PX - 1, borderPaint);
}

此代码尚未针对所有情况进行彻底测试。我只确认了OP

提供的4个案例

答案 1 :(得分:3)

TL; DR

问题似乎来自setSingleLine()。解决方法是在setHorizontallyScrolling(false)之后调用setSingleLine(true),但如果文本太长,这将“包装”文本。您甚至无法使用setEllipsize()截断它;它没有任何影响。

MyTextView text2 = makeTextView(
    "Grav=C ; SL=T a ab abc abcd abcde abcdef abcdefg abcdefgh abcdefghi",
    200);
text2.setSingleLine(true);
text2.setHorizontallyScrolling(false);
text2.setGravity(Gravity.CENTER);

原帖

我仍然无法确定问题,但我的猜测是在单行上。

调用text2.setSingleLine(true);时,Android会执行此操作:

public void setSingleLine(boolean singleLine) {
    setInputTypeSingleLine(singleLine);
    applySingleLine(singleLine, true, true);
}

// no effect, since there is no editor
/*
private void setInputTypeSingleLine(boolean singleLine) {
    if (mEditor != null &&
            (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
        if (singleLine) {
            mEditor.mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
        } else {
            mEditor.mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
        }
    }
}
*/

private void applySingleLine(boolean singleLine, boolean applyTransformation,
        boolean changeMaxLines) {
    mSingleLine = singleLine;
    if (singleLine) {
        setLines(1);
        setHorizontallyScrolling(true);
        if (applyTransformation) {
            setTransformationMethod(SingleLineTransformationMethod.getInstance());
        }
    } else {
        if (changeMaxLines) {
            setMaxLines(Integer.MAX_VALUE);
        }
        setHorizontallyScrolling(false);
        if (applyTransformation) {
            setTransformationMethod(null);
        }
    }
}

从这里开始,我总结说applySingleLine()中的某个地方导致了这个问题。因此,我尝试通过将代码更改为下面来“模拟”单行,问题仍然存在。

MyTextView text2 = makeTextView("Grav=C ; SL=T",200);
//text2.setSingleLine(true);
text2.setLines(1);
text2.setHorizontallyScrolling(true);
text2.setTransformationMethod(SingleLineTransformationMethod.getInstance());

text2.setGravity(Gravity.CENTER);
rootView.addView(text2);

但是当我对setHorizontallyScrolling(true)发表评论时,问题就消失了!因此,通过在setHorizontallyScrolling(false)之后调用setSingleLine(true),它会否定效果并解决问题,但如果文本太长,这将“包装”文本。