TL;DR: Here's the gist of everything that I can think of that's relevant to the issue I'm facing: [GIST LINK]
And here's a picture of the problem
I'm trying to set up a number of buttons that will all grow to the same size as each other by equal weighting in a vertically oriented LinearLayout
container.
The problem I'm facing surfaces when the text on these buttons cause a different number of lines per button.
Let's say n
is the lowest line count for the buttons and m
is the highest line count; any descenders in the text of buttons with line count m
are cut off. Refer to the words "qshowing my clipping problem"
in the linked screengrab, where all descenders are cut off.
How can I go about fixing this? The clipping gets much worse if I introduce android:lineSpacingExtra
to the button style.
If it's relevant, my minimum API is set to 21
I've fixed this using RxJava to set the height programmatically to the correct maximum so that no clipping occurs. If there is a better solution I'll be glad to see it, but this is what is working for me for now:
class MyActivity {
// ...
private val compositeDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.my_activity)
// ...
val container: LinearLayout = findViewById(R.id.container)
val numBtns = getNumBtnsToAdd()
val btnList: MutableList<Button> = mutableListOf()
val margin10 = dpToPx(10f).toInt()
val countDown = CountDownLatch(numBtns)
val desiredLp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0).apply {
gravity = Gravity.CENTER
setMargins(margin10, margin10, margin10, margin10)
}
// Completable will be run once per subscriber and emit a success or error
val layoutCompletable = Completable.fromAction {
countDown.await()
for (btn in btnList) btn.layoutParams = desiredLp
}.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
compositeDisposable.add(
layoutCompletable.subscribe({
Log.d("MyActivity", "Set LayoutParams on all buttons.")
}, Throwable::printStackTrace)
)
for (i in 0 until numBtns) {
val btn = Button(this, null, 0, R.style.button_style).apply {
layoutParams = LinearLayout.LayoutParams(desiredLp).apply { height = LinearLayout.LayoutParams.WRAP_CONTENTS }
text = if (i == 0) "Button${i+1} with short text"
else "Button${i+1} with text that will span multiple lines showing my clipping problem"
setOnClickListener { doSomething() }
}
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
countDown.countDown()
val height = btn.height
if (height > desiredLp.height) desiredLp.height = height
btn.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
btn.viewTreeObserver.addOnGlobalLayoutListener(listener)
btnList.add(btn)
container.addView(btn)
}
// ...
}
override fun finish() {
compositeDisposable.clear()
super.finish()
}
// ...
}