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

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.

Related

Android Material Text Input Layout End Icon Not Visible but working

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

Android. SetText doesn't change the text for Button

I have custom view - custom keyboard.
Each button is MaterialButton.
I need to implement shift button to change letters case, so I've done it this way
private fun onCaseChanged() {
buttons.forEach { button ->
(button as? TextView)?.let {
val text = it.text.toString()
it.text = if (isUpperCase)
text.toUpperCase(Locale.getDefault())
else
text.toLowerCase(Locale.getDefault())
}
}
}
All is working correct except UI - text on buttons doesn't change.
When I change buttons to TextViews - all works fine. But not with any type of Button.
So for now it seems like only way for me.
Bu I'm still curious - if there's another way to fix this problem with leaving MaterialButtons
Try android:textAllCaps="false" in the Button declaration in your XML files.
If that successfully works, I suggest changing the default button style in your theme so that it defaults to false if that's what you need.
Example
<Button
android:id="#+id/button"
android:textAllCaps="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>

How to save textview default background color to apply it later?

I simply need to change(toggle) the background colour of a textview upon each touch.
So when a user first touches the textview, it turns blue. Upon the next touch it turns back to the default theme color for the textview. I have not specified any colours at all in any of the XML files so far.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/list_item_name"
android:onClick="ToggleListItemSelectedState"/>
In the click handler for the textbox:
TextView selectedTextView = (TextView)view;
defaultBackground=//?How to save the default background
From this stackoverflow answer, I tried
defaultBackground=selectedTextView.getBackground();
int colorCode = defaultBackground.getColor();
but the getColor() isn't recognized.
I need to
Either get the default textview background colour from the theme
OR
Save the background colour the first time the textview is touched and
then subsequently reset the background using the saved colour.
Thank you
Try
mBackgroundColor = ((ColorDrawable) view.getBackground()).getColor();
view.setBackgroundColor(mBackgroundColor);
You can retrieve the current background of the view using View#getBackground(). If you don't set any background, it returns null.
The background of the view can be set by View#setBackground(Drawable) or ViewCompat#setBackground(View, Drawable) if you target APIs below 16.
Therefore probably you want to store the original background and the desired background:
mOriginalBackground = textView.getBackground();
mOtherBackground = new ColorDrawable(otherColor);
And now toggle between them:
if (showOriginalBackground) {
ViewCompat.setBackground(textView, mOriginalBackground);
} else {
ViewCompat.setBackground(textView, mOtherBackground);
}
showOriginalBackground = !showOriginalBackground;
One of the answers to this question suggested using View#setBackgroundColor(int). Please note, that this will (on API 15 and above) mutate the original background if it is already a ColorDrawable. Therefore:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/red" />
mOriginalBackground = textView.getBackground(); // red
textView.setBackgroundColor(blue); // mOriginalBackground is now blue!
ViewCompat.setBackground(textView, mOriginalBackground); // user still sees blue!

Android button setColorFilter behaviour [duplicate]

(I changed the question a bit, because the problem is a bit clearer now)
I have 4 buttons on my application, and when a user clickes certain button
I change that button color.
when button 3 is clicked I want to change his color to green, otherwise I want remove his green filter (when button1/2/4 are clicked).
If I click on button 3 It does get the green filter. If then I click button 4 it removes the green filter, but if I click button 1 or 2, nothing happens.
When I switched the position of the buttons in the XML, and put button3 first, It doesnt happen, ideas?
The relevant part of the layout xml is:
<Button
android:id="#+id/ans1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/ans2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans3"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans4"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
The code is:
if (answer.equals("3"))
{
question.setText("In if");
d.setColorFilter(filter);
}
else
{
question.setText("else");
d.setColorFilter(null);
}
I seem to remember having issues when creating too many ColorFilters before. It doesn't sound for certain like that's what's at fault here, since it's happening right away. Still, what you might try is having the filter as a class variable, and then using it within the if/else block. Also, as Trev mentioned, since you're just wanting to remove the green filter, you can just pass null to setColorFilter and avoid making the transparent filter, so you'd end up with something like this:
//in main class
PorterDuffColorFilter greenFilter =
new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP);
//in CheckAnswer()
Drawable d = findViewById(R.id.ans2).getBackground();
if(answer.equals("1") d.setColorFilter(greenFilter)
else d.setColorFilter(null);
The default behavior when calling setColorFilter(ColorFilter) on a Drawable does not automatically invalidate the Drawable, meaning it will not redraw itself solely as a result of the method call.
Try calling d.invalidateSelf() after setting the ColorFilter.
Yesterday I posted a suggestion to a very similar problem that you asked here:
Android button setColorFilter behaviour
It appears that you have edited the code you originally posted there in order to incorporate the suggestions you were given (without acknowledging the answers) and then posted exactly the same code in this question.
The Drawable documentation regarding setColorFilter(ColorFilter cf) states that 'null' may be passed to remove any existing filters. So, perhaps it could be that once the TRANSPARENT filter has been applied, then your subsequent GREEN filter can't be seen? I don't know enough about .setColorFilter and PorterDuff to know for sure, but it's worth a shot. Perhaps try:
d.setColorFilter(null);
d.setColorFilter(filter);
Also you could instead use this method:
setColorFilter(int color, PorterDuff.Mode mode)
You just need to mutate each drawable before setColorFilter.
Drawable d = findViewById(R.id.ans2).getBackground();
d = d.mutate();
d.setColorFilter

Android button setColorFilter behaviour

(I changed the question a bit, because the problem is a bit clearer now)
I have 4 buttons on my application, and when a user clickes certain button
I change that button color.
when button 3 is clicked I want to change his color to green, otherwise I want remove his green filter (when button1/2/4 are clicked).
If I click on button 3 It does get the green filter. If then I click button 4 it removes the green filter, but if I click button 1 or 2, nothing happens.
When I switched the position of the buttons in the XML, and put button3 first, It doesnt happen, ideas?
The relevant part of the layout xml is:
<Button
android:id="#+id/ans1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/ans2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans3"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans4"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
The code is:
if (answer.equals("3"))
{
question.setText("In if");
d.setColorFilter(filter);
}
else
{
question.setText("else");
d.setColorFilter(null);
}
I seem to remember having issues when creating too many ColorFilters before. It doesn't sound for certain like that's what's at fault here, since it's happening right away. Still, what you might try is having the filter as a class variable, and then using it within the if/else block. Also, as Trev mentioned, since you're just wanting to remove the green filter, you can just pass null to setColorFilter and avoid making the transparent filter, so you'd end up with something like this:
//in main class
PorterDuffColorFilter greenFilter =
new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP);
//in CheckAnswer()
Drawable d = findViewById(R.id.ans2).getBackground();
if(answer.equals("1") d.setColorFilter(greenFilter)
else d.setColorFilter(null);
The default behavior when calling setColorFilter(ColorFilter) on a Drawable does not automatically invalidate the Drawable, meaning it will not redraw itself solely as a result of the method call.
Try calling d.invalidateSelf() after setting the ColorFilter.
Yesterday I posted a suggestion to a very similar problem that you asked here:
Android button setColorFilter behaviour
It appears that you have edited the code you originally posted there in order to incorporate the suggestions you were given (without acknowledging the answers) and then posted exactly the same code in this question.
The Drawable documentation regarding setColorFilter(ColorFilter cf) states that 'null' may be passed to remove any existing filters. So, perhaps it could be that once the TRANSPARENT filter has been applied, then your subsequent GREEN filter can't be seen? I don't know enough about .setColorFilter and PorterDuff to know for sure, but it's worth a shot. Perhaps try:
d.setColorFilter(null);
d.setColorFilter(filter);
Also you could instead use this method:
setColorFilter(int color, PorterDuff.Mode mode)
You just need to mutate each drawable before setColorFilter.
Drawable d = findViewById(R.id.ans2).getBackground();
d = d.mutate();
d.setColorFilter

Categories

Resources