Расположение элементов в XML разметке
Имеется вот такой макет:
Есть 3 TextView:
- "Мой счет"
- "*1801"
- "Установлен"
Мне нужно чтоб "*1801" и "Установлен" всегда были отображены в полной размере, а вот если мы имеем длинный "Мой счет" и он не влезает в экран то применялся elipsize. При этом очень важно, чтоб "Мой счет" и "*1801" были прям рядом, как будто это один TextView.
Вопрос в том, что я не могу сообразить как правильно задать разметку элементов. Если кто подскажет, буду признателен.
Ответы (2 шт):
Автор решения: Sergei Buvaka
→ Ссылка
Пришлось написать вот такую View:
class SeparateRestrictTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {
private var inited: Boolean = false
private var showSeparatedRestrictionMode: Boolean = false
@get:Px
private val fadeOffset: Int by lazyUnsafe { context.dpToPx(FADE_OFFSET) }
init {
inflate(context, R.layout.view_separate_restrict_text, this)
context.obtainStyledAttributes(attrs, R.styleable.SeparateRestrictTextView).apply {
if (hasValue(R.styleable.SeparateRestrictTextView_srtv_textAppearance)) {
fadedText.setTextAppearance(getResourceId(R.styleable.SeparateRestrictTextView_srtv_textAppearance, View.NO_ID))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
restrictionText.setTextAppearance(getResourceId(R.styleable.SeparateRestrictTextView_srtv_textAppearance, View.NO_ID))
} else {
restrictionText.setTextAppearance(context, getResourceId(R.styleable.SeparateRestrictTextView_srtv_textAppearance, View.NO_ID))
}
}
recycle()
}
inited = true
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
measureChildWithPadding(restrictionText, widthMeasureSpec, heightMeasureSpec)
measureChildWithPadding(fadedText, widthMeasureSpec, heightMeasureSpec)
val desiredWidth = if (fadedText.inEllipsizedMode) {
showSeparatedRestrictionMode = true
measureChildWithPadding(fadedText, widthMeasureSpec, heightMeasureSpec, fadeOffset + restrictionText.measuredWidth)
fadedText.measuredWidth + fadeOffset + restrictionText.measuredWidth
} else {
showSeparatedRestrictionMode = false
fadedText.measuredWidth
}
val desiredHeight = max(fadedText.measuredHeight, restrictionText.measuredHeight)
setMeasuredDimension(
measureDimension(desiredWidth, widthMeasureSpec),
measureDimension(desiredHeight, heightMeasureSpec)
)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
if (showSeparatedRestrictionMode) {
val restrictionTop = (bottom - top - restrictionText.measuredHeight) / 2
restrictionText.layout(
right - left - restrictionText.measuredWidth,
restrictionTop,
right - left,
restrictionTop + restrictionText.measuredHeight
)
} else {
restrictionText.layout(0, 0, 0, 0)
}
val fadedTextTop = (bottom - top - fadedText.measuredHeight) / 2
val fadedTextRightRight = if (showSeparatedRestrictionMode) {
right - left - restrictionText.measuredWidth - fadeOffset
} else {
right - left
}
fadedText.layout(
0,
fadedTextTop,
fadedTextRightRight,
fadedTextTop + fadedText.measuredHeight
)
}
fun setContent(faded: String?, restriction: String?, separator: String) {
if (faded.isNullOrEmpty() && restriction.isNullOrEmpty()) {
isGone = true
return
}
isVisible = true
fadedText.text = listOfNotNull(faded?.takeIf { it.isNotEmpty() }, restriction).joinToString(separator)
restrictionText.text = restriction
}
override fun addView(child: View?) {
if (!inited) {
super.addView(child)
}
}
private fun measureDimension(desiredSize: Int, measureSpec: Int): Int {
val specMode = MeasureSpec.getMode(measureSpec)
val specSize = MeasureSpec.getSize(measureSpec)
return when (specMode) {
MeasureSpec.UNSPECIFIED -> desiredSize
MeasureSpec.AT_MOST -> min(desiredSize, specSize)
else -> specSize
}
}
private fun measureChildWithPadding(child: View, parentWidthMeasureSpec: Int, parentHeightMeasureSpec: Int, rightPadding: Int = 0) {
val lp = child.layoutParams
val childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, rightPadding, lp.width)
val childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 0, lp.height)
child.measure(childWidthMeasureSpec, childHeightMeasureSpec)
}
private companion object {
@Dp
const val FADE_OFFSET = 6f
}
}
XML разметка:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/fadedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
<TextView
android:id="@+id/restrictionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:includeFontPadding="false"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</merge>
А это в attrs:
<declare-styleable name="SeparateRestrictTextView">
<attr name="srtv_textAppearance" format="reference" />
</declare-styleable>
Автор решения: Alexander Chernin
→ Ссылка
А если так (без спейсингов, падингов и мэржингов):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="VISA"
android:textAlignment="center" />
<TextView
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="Мой счет" />
<EditText
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="5"
android:text="*1801"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Установлен">
Установлен
</Button>
</LinearLayout>
