I have spent more than 10 hours trying to get the animation of the switch to work.
I have tried so many things that I don't remember several of the attempts, but I'll explain those that I remember.
Code that should work (I think):
I use the widgetLayout parameter:
<CheckBoxPreference
android:defaultValue="false"
android:key="asd"
android:title="#string/asd"
android:widgetLayout="#layout/switchcompat_for_settings">
switchcompat_for_settings:
<android.support.v7.widget.SwitchCompat
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null"
android:clickable="false"
android:focusable="false"
android:gravity="center"
/>
No animation.
Extending SwitchCompat with this code (best attempt??):
https://gist.github.com/antifriz/ad4412b72d50f63ec977
Going from false to true there is an animation, going from true to false there is no animation.
Non ideal behaviour (but acceptable, if it can't be prevented): if the value is true it plays animation when the activity is loaded.
Extending with following code:
Extending Preference classes in Android Lollipop = losing animation
No animation at all.
#Override
public boolean isShown() {
return getVisibility() == VISIBLE;
}
Using SwitchPreference:
Before Lollipop it has the old view, so not ideal because I would need different xml for before Lollipop and after.
If I have only one SwitchPreference I still have no animation. With several consecutive SwitchPreferences I get an animation in all but one (I've also checked with a new project with the Settings template and same problem).
Also, I prefer no animation in any Preference than animation in all but one.
Adding a second Switch:
In switchcompat_for_settings I add a second SwitchCompat and set the one with id=#android:id/checkbox to invisible. I extend the CheckboxPreference to override onBindView, and after calling super.onBindView I find the extra SwitchCompat and call setChecked to the appropriate value.
In the onPreferenceClick I call the setChecked of my extra switch.
It toggles without animation. My understanding is that the problem is that as onBindView is called A LOT of times (when creating the preference, and also each time I toggle it), it cancels the animation.
I tried to add a boolean to call setChecked in onBindView only after being called from onClick but did not succeed. Maybe it is the way to go and I did something wrong.
Only remaining idea (not willing to do this):
Add the switch manually to the activity root view, place in the correct position and handle from each activity that has this preference (several).
Related
I recently refactored my App to use the jetpack navigation. On my MainActivity, I had a ViewPager containing 3 fragments with 1 recyclerview each. I therefore moved the ViewPager from the Activity to a new Fragment. Everything works when it comes to the functionality itself, but a new issue arose which really bugs me: When I select an item on the recyclerview with a long press, the ripple effect gets stuck and stays there, as if I'm still pressing the item, which I don't. Here is a picture of a selected item. As it can be seen, the ripple effect stays active on the TextView.
When I unselect the Item, the ripple effect also stays active:
The selection itself is handled with the android SelectionTracker. The TextView has the background set to background="?android:attr/selectableItemBackground". The whole ViewItem has the background set to android:background="#drawable/bg_multi_selection, whereas the bg_multi_selection file looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/secondaryLightColor" android:state_activated="true" />
<item android:drawable="#android:color/transparent" />
</selector>
What could be the issue of that? This is especially weird, as the problem did not exist, when the ViewPager with its fragments was contained inside the Activity, and not a Fragment. Any suggestions on how to fix this?
I found a solution which works for me. So for anyone interested, I did the following:
In the recyclerviewadapter in the onBindViewHolder() method I check if the element is selected and additionally set the background color and background resource of the TextView programmatically.
if (elementIsSelected) {
viewHolder.viewBinding.itemTextView.setBackgroundColor(context.resources.getColor(R.color.transparent, context.theme))
} else {
val outValue = TypedValue()
context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
viewHolder.viewBinding.itemTextView.setBackgroundResource(outValue.resourceId)
}
whereas I definied the color to be transparent, i.e. #00000000
It's odd that this problem occurs only on some devices, so I'm not sure if the "real" problem lies somewhere else, but at least that's how I fixed it. Hope that might be helpful for someone else.
I have a series of dynamically generated EditTexts in an Android Activity. They are generated based on the below XML:
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/customEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:textCursorDrawable="#null"
/>
In some circumstances I want to disable them but let them remain visible. I am doing this by setting their focusable property to false (I have reasons for using focusable instead of enabled):
if (view2 is EditText)
{
((EditText)view2).Focusable = false;
}
When I want to reenable the EditText I set the focusable property to true:
foreach (EditText disEditText in m_disabledEditTexts)
{
disEditText.Focusable= true;
}
However, this does not make the EditText respond to the user touching it. I have spent all morning fiddling with this and I haven't been able to solve it! How can I make the EditText responsive again after setting focusable to false.
If the EditText does not have focusable set to false it works fine.
This is written in C# / Xamarin.
You need to also add the FocusableInTouchMode property so it can be focused again in touch mode. Thus said, you should probably set FocusableInTouchMode to true after you've set Focusable to false.
Focus Handling
The framework will handle routine focus movement in response to user input. This includes changing the focus as views are removed or hidden, or as new views become available. Views indicate their willingness to take focus through the isFocusable() method. To change whether a view can take focus, call setFocusable(boolean). When in touch mode (see notes below) views indicate whether they still would like focus via isFocusableInTouchMode() and can change this via setFocusableInTouchMode(boolean).
Focus movement is based on an algorithm which finds the nearest neighbor in a given direction. In rare cases, the default algorithm may not match the intended behavior of the developer. In these situations, you can provide explicit overrides by using these XML attributes in the layout file:
nextFocusDown
nextFocusLeft
nextFocusRight
nextFocusUp
To get a particular view to take focus, call requestFocus().
Reference: http://developer.android.com/reference/android/view/View.htm
(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
(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
I have a Preference that enables a sync adapter, and takes a while to actually do its work when toggled. Consequently, when the user clicks the Preference, I spin off an AsyncTask to do the work. In the meantime, I disable the Preference and replace the check box with an indeterminate ProgressBar. I have all of this working via a hack involving a subclass of CheckBoxPreference that overlays the ProgressBar on top of the CheckBox. Yuck.
The android:widgetLayout attribute seems like it's designed exactly for this. I should be able to use android:widgetLayout to specify a replacement for the default CheckBox. Said replacement would implement Checkable and use a ViewSwitcher to switch appropriately between a CheckBox and a ProgressBar.
The only problem is that CheckBoxPreference, in its onBindView() method, seems to ignore the possibility that android:widgetLayout may be used. It explicitly does this:
View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
This effectively makes it impossible to swap in a custom Checkable via android:widgetLayout and have it actually work.
Is this an oversight/bug in CheckBoxPreference, or have I misunderstood android:widgetLayout? Is there a cleaner intended way to do what I'm trying to do?
First, I agree with you that Android should refactor the piece of code view.findViewById(com.android.internal.R.id.checkbox); to "calling a protected method" which can be overrided by subclass.
Luckily, we are still able to override work around as follows:
The idea is simple: declare a checkbox which has id is android default id #android:id/checkbox
<CheckBoxPreference
android:key="autostart"
android:widgetLayout="#layout/customlayout" />
and in customlayout.xml:
<SwitchView>
...
<CheckBox>
android:id="#android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mycheck"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
</CheckBox>
It's really important to note that focusable attributes of checkbox must be set to false (I guess the default layout of CheckBoxPreference does the same thing) so that list apdater receives event rather than the checkbox itself.
I guess you didn't success in your try just because you didn't set the focusable state.
greensuisse
(https://sites.google.com/site/greensuisse/)
android:widgetLayout is the right part of the preference. In the CheckBoxPreference, the widgetLayout is the checkbox.
If you take a base Preference and put a ViewSwitcher in the widgetLayout, it should work