When I include the below XML
to layout file, I can see the below image. If you see it, you could realize that the TextView
has top and bot
The answer of TopAlignedTextView code:TopAlignedTextView@GitHub
use it by layout:
<com.github.captain_miao.view.TopAlignedTextView
android:id="@+id/text_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:text="@string/text_demo_a"
/>
Inside a LinearLayout the default padding might be an issue. Try setting it to 0dp. It worked for me.
Modified this answer a little bit to use kotlin class and extend AppCompatTextView
, trimming vertical padding.
It allows setting android:fontFamily
. Method calculateTextParams()
moved from onDraw()
for performance. Not tested for multiple lines of text:
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
class NoPaddingTextView : AppCompatTextView
{
private val boundsRect = Rect()
private val textParams = calculateTextParams()
constructor(context : Context?)
: super(context)
constructor(context : Context?, attrs : AttributeSet?)
: super(context, attrs)
constructor(context : Context?, attrs : AttributeSet?, defStyleAttr : Int)
: super(context, attrs, defStyleAttr)
override fun onDraw(canvas : Canvas)
{
with(boundsRect) {
paint.isAntiAlias = true
paint.color = currentTextColor
canvas.drawText(textParams,
-left.toFloat(),
(-top - bottom).toFloat(),
paint)
}
}
override fun onMeasure(widthMeasureSpec : Int, heightMeasureSpec : Int)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
calculateTextParams()
setMeasuredDimension(boundsRect.width() + 1, -boundsRect.top + 1)
}
private fun calculateTextParams() : String
{
return text.toString()
.also {text ->
text.length.let {textLength ->
paint.textSize = textSize
paint.getTextBounds(text, 0, textLength, boundsRect)
if(textLength == 0) boundsRect.right = boundsRect.left
}
}
}
}
I faced the same problem. Here's a good answer: How to align the text to top of TextView?
But code is little unfinished and don't support all font sizes. Change the line
int additionalPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getContext().getResources().getDisplayMetrics());
to
int additionalPadding = getTextSize() - getLineHeight();
Complete C# code (mono) removes top offset:
public class TextControl : TextView {
public TextControl (Context context) : base (context)
{
SetIncludeFontPadding (false);
Gravity = GravityFlags.Top;
}
protected override void OnDraw (Android.Graphics.Canvas canvas)
{
if (base.Layout == null)
return;
Paint.Color = new Android.Graphics.Color (CurrentTextColor);
Paint.DrawableState = GetDrawableState ();
canvas.Save ();
var offset = TextSize - LineHeight;
canvas.Translate (0, offset);
base.Layout.Draw (canvas);
canvas.Restore ();
}
}
Just wanted to add to DynamicMind's answer that the reason why you see spacing around your TextViews is padding in 9-patch backgrounds they use by default.
9-patch technology allows you to specify a content area which is, effectively, padding. That padding is used unless you set the view's padding explicitly. E.g., when you programmatically set a 9-patch background to a view which had paddings set, they are overridden. And vise-versa, if you set paddings they override what was set by 9-patch background.
Unfortunately, in the XML layout it's not possible to determine the order of these operations. I think just removing the background from your TextViews would help:
android:background="@null"
public class TopAlignedTextView extends TextView {
public TopAlignedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TopAlignedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
setIncludeFontPadding(false); //remove the font padding
setGravity(getGravity() | Gravity.TOP);
}
@Override
protected void onDraw(Canvas canvas) {
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
//remove extra font padding
int yOffset = getHeight() - getBaseline();
canvas.translate(0, - yOffset / 2);
if (getLayout() != null) {
getLayout().draw(canvas);
}
canvas.restore();
}
}