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

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

当我将重力设置为其他值时 ANDLineLine = true :不绘制边框。

  • 这有什么理由吗?
  • 有没有解决办法?

下面的截图说明了这个问题:第二个textView没有边框 在这里输入图像说明

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

Solutions Collecting From Web of "你能解释TextView + Gravity + SingleLine和Canvas的行为吗?"

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

如果你想知道为什么自定义onDraw不绘制任何东西,只需要Log.d的值为canvas.getMatrix()

[它]似乎帆布翻译/水平滚动(至less在我的情况下)8159像素的左侧,所以调用canvas.translate(8159, 0)修复问题,当然8159不是一个幻数,可以改变。

我发现它,在TextView看到VERY_WIDE常量,它被设置为16384(2 ** 14),在我的情况下, TextView宽度为66,现在(16384-66)/ 2 == 8159,瞧!

…但VERY_WIDE是私人的,所以你不能访问它:-(

从这里,我不知道是否可以以编程方式检索偏移量,并确定它,很容易通过getScrollX() 。 这种方法翻译canvas,而不是通过取消水平滚动来“黑客”单行。 它更自然地显示单行。

在自定义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); } 

此代码尚未经过全面testing。 我只确认了OP提供的4个案例

TL; DR

这个问题似乎来自setSingleLine() 。 解决方法是在setHorizontallyScrolling(false)之后调用setHorizontallyScrolling(false) ,但是如果太长,这将“包装”文本。 你甚至setEllipsize()使用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); 

原始post

我仍然无法指出这个问题,但我的猜测是在单线上。

当调用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)置入注释,问题就没有了! 因此,通过在setSingleLine(true) setHorizontallyScrolling(false)之后调用setHorizontallyScrolling(false) ,它将消除效果并解决问题,但如果太长,则会“包装”文本。