Setting icon for TextInputLayout helper text and centering it to top - android

So I need to set up 2 things:
Icon for helper text in TextInputLayout
Center it to TOP, since helper text might be multiline
It might look something like this:
Is there easy of doing working around this, without making my own TextInputLayout?

Solving this melts down to following 2 possible solutions:
First is to change the Rect of the Drawable itself by setting the bounds (coordinates X and Y), drawback of this solution is whenever layout is redrawn (orientation change etc.), this has to be done again.
findViewById<AppCompatTextView>(R.id.textinput_helper_text)?.run {
compoundDrawables.first()?.run {
setBounds(
bounds.left,
bounds.top - this#with.height.div(2),
bounds.right,
bounds.bottom - this#with.height.div(2)
)
bottom = this#with.height.div(2)
}
}
Second solution is to add new ImageView in front of the helper TextView and add padding to it, so it's pushed up.
It's not a one liner, but it can be formed into easy to use method as such:
fun TextInputLayout.setStartHelperTextIcon(
#DrawableRes drawableRes: Int,
paddingPx: Int
) {
with(findViewById<AppCompatTextView>(R.id.textinput_helper_text)) {
(this?.parent?.parent as? LinearLayout)?.run {
if(findViewById<ImageView>(R.id.text_input_layout_helper_icon) == null) {
addView(
ImageView(context).apply {
id = R.id.text_input_layout_helper_icon
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setPadding(
paddingPx,
0,
paddingPx,
paddingPx + this#with.height.div(2)
)
},
0
)
}
}
}
}

Related

ProgressBar inside MaterialButton

I was looking at material design website when I got to this page about circular progressbar and more precisely this video
I'd like to create a button like the one in the video but I don't see any documentation about this, and I don't know where to start
I've managed to simply use:
val progress = CircularProgressDrawable(context).apply {
setStyle(CircularProgressDrawable.DEFAULT)
// do use setBounds if you need to
start()
}
myMaterialButton.setCompoundDrawablesWithIntrinsicBounds(null, progress, null, null)
// Do your stuff
//replace the image by the one you want
myMaterialButton.setCompoundDrawablesWithIntrinsicBounds(null, myDrawable, null, null)
Reference: setCompoundDrawablesWithIntrinsicBounds
You could also use IndeterminateDrawable from material progress indicator. I see the CircularProgressDrawable animation breaks in API level 25
private val progressDrawableBackward by lazy {
val progressIndicatorSpec = CircularProgressIndicatorSpec(
context,
null,
0,
R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall
)
progressIndicatorSpec.indicatorColors =
intArrayOf(progressColor)
IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply {
val size = (DRAWABLE_CENTER_RADIUS + DRAWABLE_STROKE_WIDTH ).toInt() * 2
setBounds(0, 0, size, size)
setVisible(true, true)
}
}

wrap_content breaks performance of my constraint layout

I have a single TextView which I am updating as a counter every second. If I use wrap_content for its width, I get performance warnings that it takes 50ms to draw the view update in the layout/measure phase. When I programmatically set its width and height, it addresses the alerts, but my views no longer resize (obviously :). What am I missing here? Is there way to minimize view invalidations / resizing to be only when the text changes to a greater width? Do I have to do that programmatically and calculate the new size each time I update the view?
This is not really a solution to why it was happening in the first place, but a workout around (Which feels very anti-android since it doesn't rely on ConstraintLayout to solve what hopefully is a simple system of equations to determine the proper intrinsic TextView width without breaking the whole UI thread):
fun TextView.conditionallyChangeWidthWithNewString(newString: CharSequence) {
val calculatedWidth = getWidthOfNewText(newString.toString())
if (calculatedWidth > layoutParams.width) {
val params = getLayoutParams()
params.width = calculatedWidth
setLayoutParams(params)
}
setTextIfChanged(newString)
}
fun TextView.getWidthOfNewText(s: String): Int {
val p = Paint()
val bounds = Rect()
val plain = typeface
p.setTypeface(plain)
p.textSize = textSize
p.getTextBounds(s, 0, s.length, bounds)
val mt = p.measureText(s)
return mt.toInt()
}

Why won't my LayoutParams change my layout?

I want to display a box with suggested entries next to an EditText.
I figured I'd have to set LayoutParams (like suggested in this question.
However, no matter what I try, the box always ends up in the top left corner.
What I tried:
- get LayoutParams as MarginLayoutParams (which they are), and set them. Set them back in the view for good measure.
- All the answers in the link mentioned above
Change the position with TranslationX and -Y (which works, but runs into other problems like status bar might not always be 24dp and seems a hack)
val layoutParams = box?.layoutParams
//val layoutParams = ConstraintLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply{
// setMargins(left, top, 0,0)
//}
if (layoutParams !is ViewGroup.MarginLayoutParams) return#onTextChanged
//layoutParams.leftMargin = left
layoutParams.marginStart = left
layoutParams.topMargin = top
box?.layoutParams = layoutParams // Don't think this is necessary
Log.d(TAG, "$layoutParams")
// works, but don't like it
// box?.translationX = left.toFloat()
// box?.translationY = top.toFloat()
//doesn't work
//box?.setMargins(left, top, right, 1000)
the function View.Setmargins is as follows:
fun View.setMargins(left: Int, top: Int, right: Int, bottom: Int) {
if (layoutParams is MarginLayoutParams) {
val p = layoutParams as MarginLayoutParams
p.setMargins(left, top, right, bottom)
requestLayout()
}
}
Complete source of the thing I want to do here (snippet above is at line 170, edited it a bit to show things I've tried)
Other things I have tried: add box to different layouts, which in all cases results in the box being drawn in the top-left corner of that layout
For anybody having the same problem as I had:
Mike M. 's suggestion to get the LayoutParams as ConstraintLayout.LayoutParams, and also set its topToTop and startToStart fields to ConstraintLayout.LayoutParams.PARENT_ID did the trick, ie.
(box?.layoutParams as ConstraintLayout.LayoutParams).apply{
topToTop = ConstraintLayout.LayoutParams.PARENT_ID
startToStart = ConstraintLayout.LayoutParams.PARENT_ID
topMargin = top
marginStart = left
}

How to insert a line break before specific word in TextView in Android, if line count is more than one?

For example, I have a string «One two three four five» to be set in TextView.
If the text occupies only one line, there’s no change, and it’s OK
If text occupies two lines, then:
expected result is: «One two \n three four five» (exactly after
"two")
actual result is: «One two three four \n five» (the line break
can be anywhere)
Also I have a blank TextView in ConstraintLayout with zero width, so I don’t know the width of the textView before I set text.
How can I achieve this?
As far as I know, the width of text can be measured before setting text and divided by the textView's width, but unfortunately it doesn't work for me, as I don't know the width of textview.
Here's my code:
override fun setText(textView: TextView, prefix: String, postfix: String) {
var fullText = "$prefix $postfix"
val lines = getLinesCount(textView, fullText)
if (lines > 1) {
fullText = fullText.replace(postfix, "\n$postfix")
}
textView.text = fullText
}
private fun getLinesCount(textView: TextView, text: String): Int {
val paint = Paint()
paint.textSize = textView.textSize
val rect = Rect()
paint.getTextBounds(text, 0, text.length, rect)
// I'm not sure how to calculate textView actual width
return (ceil(rect.width().toFloat() / textView.width)).toInt()
}
TextView Supports getLineCount(); which return line count after textview is drawn on UI.
int lineCount = textView.getLineCount();
if(lineCount > 2) {
//your line break logic goes here.
} else {
//normal logic
}
See this doc.
You need to use Html.fromHtml() to format XML textview in html format.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.setText(Html.fromHtml("One two <br> three four five", Html.FROM_HTML_MODE_COMPACT));
} else {
textView.setText(Html.fromHtml("One two <br> three four five"));
}
You can also refer to this link

How to create simple layer drawable in button

I am trying to better understand how layer drawables work within a buttons drawable(s).
I am trying to draw 2 simple colored boxes, one without insets so that it fills the entire button drawable area. And one with some inset.
ColorDrawable background1 = new ColorDrawable(Color.BLUE);
ColorDrawable background2 = new ColorDrawable(Color.GREEN);
Drawable[] drawables = new Drawable[] {
background1,
background2
};
LayerDrawable ld = new LayerDrawable(drawables);
ld.setLayerInset(0, 0, 0, 0, 0 ); // no inset on white box
ld.setLayerInset(1, 8, 8, 8, 8 ); // 8 inset on all side on green box
// set the left drawable on the button
button.setCompoundDrawablesWithIntrinsicBounds(ld, null, null, null);
However that doesn't work at all. The first problem is that the boxes are not filling any area. Is that because a buttons drawables(s) don't have a predefined size? If that is the case I tried to set the bound manually on the boxes, but didn't have much luck either.
Can anyone help me understand what I am doing wrong?
Create a Specific Design in Drawable and call in background button in xml
The problem right now with ColorDrawable is that getIntrinsicWidth()/getIntrinsicHeight() that determine the bounds of the drawable are by default -1, thus it doesn't render on the screen. What would help in your case is to extend ColorDrawable and override the height and width from the constructor.
Here's a sample:
class CustomDrawable(color: Int, val h: Int, val w: Int): ColorDrawable(color) {
override fun getIntrinsicHeight(): Int {
return h
}
override fun getIntrinsicWidth(): Int {
return w
}
}
and then you can instantiate this instead of ColorDrawable like so
val background1 = CustomDrawable(Color.BLUE, dimensionInPx , dimensionInPx)
val background2 = CustomDrawable(Color.GREEN, dimensionInPx, dimensionInPx)
be sure to convert dp to px before passing these values
Hope this helps.

Categories

Resources