Android Material Text Input Layout End Icon Not Visible but working - android

I recently found a bug with Material Design TexInputLayout and the way it makes the endIcon visible.
Consider you have setup everything right and have end icon enabled like:
inputLayout.endIconMode == END_ICON_CLEAR_TEXT
The problem is that if you define a focusListener like:
inputLayout.editText?.setOnFocusChangeListener { view, hasFocus -> ...}
and navigate to a screen which has this textInputLayout in it with some already filled text in like following pic
You'll notice that the endIcon is not showing, but if you tap on where it's supposed to be, it will work.
Even making sure it should be displayed by making it visible inputLayout.isEndIconVisible = doesn't help. Through the debugging you can see you are hitting the public void setEndIconVisible(boolean visible) with true value but still it doesn't make the icon visible.
I found a solution which I have posted as the answer.

The issue is that ClearTextEndIconDelegate has a default OnFocusChangeListener which when you click on the editText it runs an animation which through it the alpha value of the private/internal endIconView(which holds the endIconDrawable) changes from 0 to 1. When you override and set your own OnFocusChangeListener you actually discard this process and setting the inputLayout.isEndIconVisible = true enables the view but you can't see it still. I didn't find any way to access to the parent view and change the alpha.
Actually you can by doing something like
(((inputLayout.getChildAt(0) as FrameLayout).getChildAt(2) as LinearLayout).getChildAt(1) as FrameLayout).getChildAt(0).alpha = 1f
Take a look at image below you'll get how I ended up the above:
But this is not a great solution also if the hierarchy of the view changes it won't work anymore.
Ultimate Solution
What I came with is the invoking the original OnFocusChangeListener within my OnFocusChangeListener something like :
val originalListener = inputLayout.editText?.onFocusChangeListener
inputLayout.editText?.setOnFocusChangeListener { view, hasFocus ->
originalListener?.onFocusChange(view, hasFocus)
// do your thing here
}

For this case, it will be better to set custom mode for end icon
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM // may be set in xml
textInputLayout.setEndIconDrawable(R.drawable.your_icon)
textInputLayout.setEndIconOnClickListener {
textInputEditText.text?.clear()
}
And then add your custom OnFocusChangeListener:
inputLayout.editText?.setOnFocusChangeListener { view, hasFocus ->
textInputLayout.isEndIconVisible = hasFocus
// add your code here
}

For me, none of the solution worked and the end icon was invisible because the alpha is set to 0.
So I decided to manually set drawable end in the TextInputEditText.
// First, set the endIconMode to None and set the click listener to clear the text
testInputLayout.endIconMode = TextInputLayout.END_ICON_NONE
testInputLayout.setEndIconOnClickListener {
binding.customTextInput.editText?.text?.clear()
}
// Inside the custom focusChangeListener, show/hide the drawable end
textInputLayout.editText?.setOnFocusChangeListener { _, hasFocus ->
// This is required so that the clear text click action can be handled
binding.customTextInput.isEndIconVisible = hasFocus
if (hasFocus) {
// Display Drawable end
textInputLayout.editText?.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(context, R.drawable.ic_clear), null)
} else {
// Hide Drawable end
textInputLayout.editText?.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
// Do what you want
}

TextInputLayout's textwatcher checks if the view has focus and if there is any text in its EditText and sets the visibility of the end icon. Probably, it sets the end icon visibility to Visibility.GONE since it does not have the focus even if there is any text in its underlying EditText. So the TextInputLayout needs to have the focus as well. So the below code worked for me
inputLayout.requestFocus()
inputLayout.edittext.setText(YOUR_TEXT)
inputLayout.edittext.setSelection(YOUR_TEXT_LENGTH)

Extending to Amir's answer
Running on a Pixel 5 API 32 device and targetSdkVersion to 31.
The children of TextInputEditText are shown as below:
So we might use following code to set this view's alpha by:
id_of_text_input_layout.findViewById<View>(R.id.text_input_end_icon)?.alpha = 1f

After trying the solutions from this thread and finding they don't work in my case (the "clear text icon" from com.google.android.material.textfield.TextInputLayout wasn't always being displayed, not even with alpha 0), I ended up implementing the "clear text icon" from scratch, ie:
add an ImageView with my "clear text icon" to TextInputLayout's parent ViewGroup
attach an onClickListener to that ImageView and place the text-clearing logic there
attach onTextChanged and onFocusChange listeners to my EditText to trigger ImageView's visibility changes.
make the ImageView visible by animating its alpha from 0 to 1 and setting its visibility to View.VISIBLE at the end. To make it invisible, do the reverse, ie. animate its alpha from 1 to 0 and set its visibility to View.GONE at the end. Weirdly enough, when just toggling the visibility between GONE and VISIBLE, that ImageView wasn't always being displayed.
when the ImageView is shown, add endPadding to TextInputLayout, so that text in the EditText doesn't show under the ImageView

Related

How to reset CardView background?

I have a CardView which is getting colored after a button click ("reveal right answer" functionality). The card needs to get back to it's original color for the next question. I am using the default Android color, but I couldn't find the value I need to reset to (R.attr.cardBackgroundColor turns my card blue...), so I used tint like so:
#BindingAdapter("isRight", "showResult")
fun CardView.setCardTint(
isRight: Boolean,
showResult: Boolean,
) {
background.setTintList(null)
if (showResult) {
val colorRes = if (isRight) R.color.result_right
else R.color.result_wrong
background.setTint(ContextCompat.getColor(context, colorRes))
}
}
This works great, but now I need to change the colors to gradients. For that I need to go back to the original solution and change the background itself (to the gradient drawables) - and to my original problem: How to reset the CardView background to it's Android default value?

Android, onClick of Button fires before completing the built in animation

I'm new to android development. Developing a flipflop game.
On click button changes the colour and then it checks if the two turned buttons match or not.
I haven't added any animation below is my on click code.
fun onButtonClick(view: View) {
val id = getResources().getResourceEntryName(view.id)
val btnItem = getButtonItem(id)
view.setBackgroundColor(Color.parseColor(btnItem.color))
btnItem.isTurned = true
btnItem.resId = view.id
checkMatch()
}
and here is what each button looks like
<Button
android:onClick="onButtonClick"
android:id="#+id/btn02"
android:layout_margin="5dp"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight=".50"/>
The problem is, when I click the button, the colour is changed after finishing the onclick execution, not sure if it's setBackgroundColor which is taking time or the onClick itself delays this. Even If i put a Thread.sleep right after setBackgroundColor it will sleep and don't even change the colour.
In checkMatch i want to match the turned cards and if they don't match i set the background colour back. but this all completes before even showing the hidden colour.
What should I do to complete animation and set background colour and then proceed further ?
I hope this makes sense.

Making UI with inward shadow as shown in the photo for Android and iOS (SwiftUI)

I am trying to build an application and want to build a view that has inward shadow effect as shown in the picture. I wanted to do it in both Android and iOS (SwiftUI).
For now in SwiftUI it is possible to do only using different images for selected and non-selected states.
Here is a demo of approach to do this
struct TestSelectedTabs: View {
#State var selectedTab = 1
var body: some View {
TabView(selection: $selectedTab){
Text("One")
.tabItem {
Image(systemName: selectedTab == 0 ? "printer.fill" : "printer")
Text("Print")
}.tag(0)
Text("Two")
.tabItem {
Image(systemName: selectedTab == 1 ? "tv.fill" : "tv")
Text("Show")
}.tag(1)
}
}
}
so having two set of icons (flat & pressed in) you can achieve required effect. Or, of course, you can generate image with desired "press in" effect in code, say using CoreGraphics, Layers, etc. but finally it needs to be just image.
Well you could just create a custom background with that particular shadow in mind and then just whenever the user presses the button change the button's background to reflect the shadow background, and since its custom you could do the shape of your liking and the depth of your liking too.
You would normally keep the icons/buttons flat right? and then when pressed/selected/active just change the background to this,so as to show emphasis on it being selected.
This is an almost similar approach to the method in the above answer.

Verify if a background is equal to another for Edit Text in Android

In my project I have to verify if a backround is equal to another for an edit text (the edit text has a custom drawable). I have tried this code but it's not working:
if (editText.getBackground().equals(getResources().getDrawable(R.drawable.edit_text_box_red)))
{
editText.setBackgroundResource(R.drawable.edit_text_box_white);
}
I need this, because when I press a button and my editText background is red I have to make it white, to it's previous state. How can I do this?
I think it would make sense to store the background information when you change the background in the first place. One way to do this would be setTag(). For example, when you change the view's background to red, you also perform editText.setTag("red"). Then later, you can do
if (editText.getTag().equals("red")) {
editText.setBackgroundResource(R.drawable.edit_text_box_white);
editText.setTag("white");
}
How about "Flagging"?
Its like you create a static bool red = false
Then when you change the color, change the red value to true
In the selection :
if(red == true){
editText.setBackgroundResource(R.drawable.edit_text_box_white);
red = false;}
Hope this help :D

what's the difference of setVisibility(View.INVISIBLE); setVisibility(0);

I met a strange issue, when i set a textview visibility as
text.setVisibility(0); I cannot hide this textview. But after i update the code to text.setVisibility(View.INVISIBLE), the textview is hidden...
I have no idea, why this happened....
You got misconcept I think,
0 stands for VISIBLE..You can check here Developer Doc
0 is for VISIBLE
4 is for INVISIBLE
8 is for GONE
So nothing going wrong in your case,Its working properly as per you passed the parameter.
That's because 0 means VISIBLE. INVISIBLE is 4. These are constant values defined in View:
View.VISIBLE
View.INVISIBLE
View.GONE
Simple because INVISIBLE has the value 4.
So,Try this code text.setVisibility(4);
Will work.
The int is the problem
DOC OFFICIAL
android:visibility
Controls the initial visibility of the view.
Must be one of the following constant values.
Constant Value Description
visible 0 Visible on screen; the default value.
invisible 1 Not displayed, but taken into account during layout
gone 2 Completely hidden, as if the view had not been added.
This corresponds to the global attribute resource symbol visibility.
Related Methods
setVisibility(int)

Categories

Resources