Here is a solution that works with Spannable, with the cavate that if the right and left are too wide, they will overlap on the same line. Since to do the Left/Right trick with a spannable requires a line feed between Left and Right, my fix is to add a spannable that reduces the line height to zero (i.e. overlapped lines) for the one linefeed and then restore normal line height after that.
String fullText = leftText + "\n " + rightText; // only works if linefeed between them! "\n ";
int fullTextLength = fullText.length();
int leftEnd = leftText.length();
int rightTextLength = rightText.length();
final SpannableString s = new SpannableString(fullText);
AlignmentSpan alignmentSpan = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE);
s.setSpan(alignmentSpan, leftEnd, fullTextLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new SetLineOverlap(true), 1, fullTextLength-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new SetLineOverlap(false), fullTextLength-1, fullTextLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
And we have the small routine to handle the overlapping lines:
private static class SetLineOverlap implements LineHeightSpan {
private int originalBottom = 15; // init value ignored
private int originalDescent = 13; // init value ignored
private Boolean overlap; // saved state
private Boolean overlapSaved = false; // ensure saved values only happen once
SetLineOverlap(Boolean overlap) {
this.overlap = overlap;
}
@Override
public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v,
Paint.FontMetricsInt fm) {
if (overlap) {
if (!overlapSaved) {
originalBottom = fm.bottom;
originalDescent = fm.descent;
overlapSaved = true;
}
fm.bottom += fm.top;
fm.descent += fm.top;
} else {
// restore saved values
fm.bottom = originalBottom;
fm.descent = originalDescent;
overlapSaved = false;
}
}
}