I have a custom TextView with it's onDraw
method overridden to draw a red border around the text view.
When I set the gravity to something else than left AND singleLine=true : the border is not drawn.
The following screenshot illustrate the problem : the second textView doesn't have a border
TL means TOP|LEFT ; C means CENTER ; SL means single-line ; T means true ; F means false
Here is the code (can be copy/pasted and run - no need for a layout file)
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;
}
}
Thanks to pskink's thorough research (from the comment on my another answer):
If you want to know why custom
onDraw
doesn't draw anything, justLog.d
the value ofcanvas.getMatrix()
.[It] seems that canvas is translated/horizontally scrolled (at least in my case) 8159 pixels to the left, so calling
canvas.translate(8159, 0)
fixes the issue, of course 8159 is not a magic number and can vary.I found it, see
VERY_WIDE
constant inTextView
, it is set to 16384 (2**14), in my caseTextView
has width of 66 and now (16384-66)/2 == 8159, voila!...but VERY_WIDE is private so you cannot access it :-(
From here, I wonder if the offset can be retrieved programmatically, and sure it does, easily by getScrollX()
. This approach translates the canvas instead of "hacking" the single-line by cancelling horizontal scrolled. It displays the single-line more naturally.
Inside custom 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);
}
This code hasn't been tested thoroughly for all cases. I only confirmed the 4 cases provided by the OP
The problem seems to be from setSingleLine()
. The workaround is to call setHorizontallyScrolling(false)
after setSingleLine(true)
, but this will "wrap" the text if it's too long. You can't even truncate it by using setEllipsize()
; it doesn't have any effects.
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);
I still couldn't pinpoint the issue, but my guess is on the single-line.
When calling text2.setSingleLine(true);
, Android will do this:
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);
}
}
}
From here, I concluded that somewhere in applySingleLine()
was causing this issue. Thus, I tried "simulating" the single-line by changing the code to below, and the issue is still there.
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);
But when I put setHorizontallyScrolling(true)
into comment, the issue was gone! Thus, by calling setHorizontallyScrolling(false)
after setSingleLine(true)
, it negates the effect and solves the issue, but this will "wrap" the text if it's too long.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With