SwitchButton (in custom view) value is not checked after rotation - android

I need to create custom view - TextView and Switch button.
I have custom view:
public class CustomTextWithSwitch extends LinearLayout implements View.OnClickListener {
private CustomTextWithSwitchBinding binding;
private boolean defaultValue;
public CustomTextWithSwitch(Context context) {
this(context, null);
}
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
binding = CustomTextWithSwitchBinding.inflate(LayoutInflater.from(getContext()), this);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextWithSwitch);
defaultValue = a.getBoolean(R.styleable.CustomTextWithSwitch_defaultValue, false);
a.recycle();
}
#Override
protected void onFinishInflate() {
...
binding.sToggle.setChecked(defaultValue);
super.setOnClickListener(this);
super.onFinishInflate();
}
public void toggle() {
binding.sToggle.toggle();
defaultValue = binding.sToggle.isChecked();
}
#Override
public void onClick(View view) {
toggle();
}
public void setDefaultValue(boolean defaultValue) {
this.defaultValue = defaultValue;
binding.sToggle.setChecked(defaultValue);
}
}
I use that in the activity:
public class MyActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
myCustomView.setDefaultValue(true);
}
}
When I open this activity, everything works as expected - switch button is checked. However, when I rotate the screen, it is checked to false. Can somebody advise what am I doing incorrectly? Note: I use ViewModel and the value is restored correctly. However, the Switch button in this custom view is not toggled.
UPDATE: This issue happens only when I have another CustomTextWithSwich in my activity, so it means they have effect on each other(it seems that default value is set according to second View). Is it possible to separate them, so they are standalone?

I suggest you to use a ViewModdel. During rotation it is normal that you will lost the states. ViewModel will not be destroyed during app rotation so the last state of CustomTextWithSwich will be not lost.
On the other hand you can override onConfigurationChanged method every time app is rotated this method will run, so you can use it to set your data.
#Override
public void onConfigurationChanged(#NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}

Please check whether both of your CustomTextWithSwitch and its children on Activity has android:ids. Ids should be different. In this case Android take care about saving instance state for your view.
If it does not save your state, use this code inside your CustomTextWithSwitch
#Override
public Parcelable onSaveInstanceState()
{
Bundle bundle = new Bundle();
bundle.putParcelable("superState", super.onSaveInstanceState());
bundle.putBoolean("isChecked", defaultValue); // ... save check state
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state)
{
if (state instanceof Bundle)
{
Bundle bundle = (Bundle) state;
defaultValue = bundle.getBoolean("isChecked"); // ... load state
binding.sToggle.setChecked(defaultValue);
state = bundle.getParcelable("superState");
}
super.onRestoreInstanceState(state);
}

Have faced the same issue. I fixed it by setting setSaveEnabled(false) on the switch view. Just let the viewModel restore it's state.
public CustomTextWithSwitch(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
binding = CustomTextWithSwitchBinding.inflate(LayoutInflater.from(getContext()), this);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextWithSwitch);
defaultValue = a.getBoolean(R.styleable.CustomTextWithSwitch_defaultValue, false);
a.recycle();
//Add this (assuming your switch view id is 'switch_view')
binding.switchView.setSaveEnabled(false);
}

Related

Where to call bind and unbind method in custom view when using ButterKnife?

I'm new to Android. I wrote a custom view, but I just don't know where to call the bind and unbind method. I have searched for this documentation. And it seemingly suggests to use bind in the onFinishInflate() callback. But I bind the view in its constructor function and there is no crash any way. Is it correct? And how about the unbind? I find this question, it suggests to use unbind in the onDetachedFromWindow() callback. Is it required or necessary?
public class BloodIndicatorView extends FrameLayout {
#Bind(R.id.ll_record_bloodpress)
LinearLayout llRecordBloodpress;
#Bind(R.id.ll_record_bloodsugar)
LinearLayout llRecordBloodsugar;
private Context mContext;
public BloodIndicatorView(Context context) {
this(context, null);
}
public BloodIndicatorView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public BloodIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
LayoutInflater.from(mContext).inflate(R.layout.health_blood_indicator, this);
ButterKnife.bind(this);
}
public void update() {
}
#OnClick(R.id.ll_record_bloodpress)
public void recordBloodpress() {
Intent intent = BloodPressActivity.getIntent2Act(mContext);
mContext.startActivity(intent);
}
#OnClick(R.id.ll_record_bloodsugar)
public void recordBloodsugar() {
Intent intent = BloodSugarActivity.getIntent2Act(mContext);
mContext.startActivity(intent);
}
}
You can annotate the view inside the class which you are doing correctly and since there is no error that means ButterKnife.bind(this); is happening correctly. And its not necessary to unbind this should work completely fine.

PersistInt slowing down button response in custom preference

I've created a custom preference that embeds two buttons (here I have subclassed Button as FastButton). The problem is executing the persistInt to store the preference drastically slows down the response of the button.
I had the notion of only executing the persistInt when the preference's lifecycle is ended, but could not find an appropriate method to override (i.e. there is nothing like onPause() for the Preference class).
I was also unsuccessful at trying to use AsyncTask to move the persistInt off of the UI thread.
Any suggestions about how I should go about mitigating the effect of persistInt on my UI response?
public final class StepperPreference extends Preference {
public int mCurrentValue = 1;
public int maxValue = Integer.MAX_VALUE;
public int minValue = Integer.MIN_VALUE;
private TextView mText;
private FastButton plusButton;
private FastButton minusButton;
public StepperPreference(Context context) {
super(context);
}
public StepperPreference(Context context, AttributeSet attrs) {
super(context, attrs);
parseCustomAttributes(attrs);
}
public StepperPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
parseCustomAttributes(attrs);
}
public void setmCurrentValue(int value) {
if (mCurrentValue != value) {
mCurrentValue = value;
persistInt(mCurrentValue);
}
}
private void parseCustomAttributes(AttributeSet attrs) {
int maxValueAttrInt=Integer.MAX_VALUE;
int minValueAttrInt=Integer.MIN_VALUE;
if (attrs!=null) {
TypedArray a=getContext()
.obtainStyledAttributes(attrs,
R.styleable.StepperPreference,
0, 0);
maxValueAttrInt = a.getInt(R.styleable.StepperPreference_maxValue, Integer.MAX_VALUE);
minValueAttrInt = a.getInt(R.styleable.StepperPreference_minValue, Integer.MIN_VALUE);
a.recycle();
}
if (maxValueAttrInt > minValueAttrInt) {
maxValue = maxValueAttrInt;
minValue = minValueAttrInt;
}
}
#Override
protected View onCreateView(ViewGroup parent) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = (View) li.inflate(R.layout.stepper_preference, parent, false);
mText = (TextView) view.findViewById(R.id.text_view);
Context context = getContext();
int localDefaultValue = 0;
mCurrentValue = getPersistedInt(localDefaultValue);
mText.setText(Integer.toString(mCurrentValue));
plusButton = (FastButton) view.findViewById(R.id.plus_button);
plusButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mCurrentValue < maxValue) {
mCurrentValue++;
mText.setText(Integer.toString(mCurrentValue));
persistInt(mCurrentValue);
}
}
});
minusButton = (FastButton) view.findViewById(R.id.minus_button);
minusButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mCurrentValue > minValue) {
mCurrentValue--;
mText.setText(Integer.toString(mCurrentValue));
persistInt(mCurrentValue);
}
}
});
return view;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
int localDefaultValue = 0;
Object result = a.getInt(index, localDefaultValue);
return result;
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
int localDefaultValue = 0;
setmCurrentValue(restoreValue ? this.getPersistedInt(localDefaultValue) : (int) defaultValue);
}
}
CheckBoxPreference, via its TwoStatePreference superclass, uses persistBoolean() for saving the preference value, much as you are using persistInt(). I do not perceive significant latency in the processing of the CheckBox. This means one of two things:
I'm a troglodyte and am incapable of seeing obvious delays in animations and such
CheckBoxPreference does not exhibit the problems that you are seeing in your StepperPreference
note: these two possibilities are not mutually exclusive
If we assume #2 to be correct, then there's something else afoot. Method tracing, to see where you are spending time "downstream" from persistInt(), may prove useful for determining what is different about StepperPreference.
From your comment, you had a listener responding to preference changes, and that was what was causing the sluggish response. "Inline" preferences, like CheckBoxPreference and StepperPreference, will be somewhat more "twitchy" than DialogPreference subclasses like ListPreference, simply because it takes less work to change the preference state (e.g., one screen tap versus 2+). As a result, listeners need to be cheaper. For example, you might hold off on doing significant work until the user has left the PreferenceFragment and so you know that the preference values are likely to be stable for at least a second or so.

Callback with extended EditText

I'm trying to create a custom EditText that provides an onLostFocus event. However, I can't get my head around how I tell the custom class what method to run when the focus is lost.
This is my extended EditText:
public class smtyEditText extends EditText {
public smtyEditText(Context context) {
super(context);
}
public smtyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public smtyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setFocusChangeListener() {
this.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
// notify the relevant activity, probably passing it some parameters like what instance of smtyEditText triggered the event.
}
}
});
}
}
The intention of the setFocusChangeListener function was that from any given activity I could do something like:
public class AddMeal extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_meal);
EditText etMealName = (EditText) findViewById(R.id.txtmealName);
etMealName.setFocusChangeListener(this.fieldLostFocus)
}
// then
public void fieldLostFocus(eventSource) {
// run some kind of validation on the field text.
}
}
Clearly I'm "code paraphrasing" here. I also get that Interfaces, and some other "EventNotifier" class might be needed. These are the resources I've tried to decipher so far:
http://www.javaworld.com/article/2077462/learn-java/java-tip-10--implement-callback-routines-in-java.html
How to Define Callbacks in Android?
http://www.justinmccandless.com/blog/Setting+Up+a+Callback+Function+in+Android
But for whatever reason I can't crystallize what is needed. Do you have any suggestions on how I can achieve this?
You don't need the inheritance... it only adds an unnecessary layer of indirection. Just add the focus change handler in your activity.
public class AddMeal extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_meal);
EditText etMealName = (EditText) findViewById(R.id.txtmealName);
etMealName.setFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
// call method to handle loss of focus
}
}
});
}
// then
public void fieldLostFocus(eventSource) {
// run some kind of validation on the field text.
}
}

android: calling a method each time a button is clicked

i would like to create a class, which extends Button and implement a method, which is alwasy called, when the Button is clicked. But i still want it's OnClickListener to be called.
My Idea is to save the OnClickListener into a private member when the constructor or setOnClickListener is called and then set the OnClickListener to my own OnClickListener. This one would then call my method and the saved OnClickListener.
But i don't see how i can get the OnClickListenr, i only see, how to set it.
Is there a way to acces it?
Or do you have a better idea? (it doesn't matter wheter my method is called before or after the OnClickListener)
I guess you could do this:
public class OnceClickedTwiceRunButton extends Button{
public OnceClickedTwiceRunButton(Context context) {
super(context);
}
public OnceClickedTwiceRunButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OnceClickedTwiceRunButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private OnClickListener extraClickMethod;
#Override
public void setOnClickListener(OnClickListener newListener)
{
super.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
DefaultClickMethod(v);
if(extraClickMethod != null)
{
extraClickMethod.onClick(v);
}
}
});
extraClickMethod = newListener;
}
private void DefaultClickMethod(View v)
{
//TODO
}
}

Can't register click event with a custom Preference

I've built a custom Preference that includes a Button for the android:widgetLayout field. I want to trigger a specific action when this button is pressed, but I'm having trouble getting a listener to pick up click events. Here is what I've tried:
#Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
addPreferencesFromResource( R.xml.preference_account );
Preference signOutPreference = findPreference( "sign_out" );
signOutPreference.setOnPreferenceClickListener( new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick( Preference preference ) {
Log.i( TAG, "Click" );
return false;
}
} );
}
#Override
public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
View root = super.onCreateView( inflater, container, savedInstanceState );
root.findViewById( R.id.preference_signOut_Button ).setOnClickListener( new OnSignOutClickListener() );
return root;
}
private class OnSignOutClickListener implements OnClickListener {
#Override
public void onClick( View v ) {
Log.i( TAG, "Click" );
}
}
The OnPreferenceClickListener is never triggered when I click the button or the preference row, and the onCreateView implementation throws a NullPointerException when trying to find the button view.
How do I listen for when this button is clicked? For reference, here are the relevant XML files:
preference_account.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<Preference
android:key="sign_out"
android:persistent="false"
android:summary="account#email.com"
android:title="Signed in as:"
android:widgetLayout="#layout/sign_out_button" />
</PreferenceScreen>
sign_out_button.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/preference_signOut_Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sign Out" />
Edit: I've found a solution using the android:onClick field of the button, but I still would like a solution using Preference if possible.
Maybe you could try the below example:
class MyOnPreferenceClickListener implements OnPreferenceClickListener {
private Activity activity;
private static final String TAG = "ABCDEFGH...";
public MyOnPreferenceClickListener(Activity activity) {
this.activity = activity;
}
#Override
public boolean onPreferenceClick(Preference preference) {
Log.i(TAG, "Click on Item " + preference.getKey());
return true;
}
}
Updating #Sushanth Raghuthaman answer a little bit. This worked for me:
public class CustomPreference extends Preference implements OnPreferenceClickListener {
public CustomPreference(Context context) {
super(context);
setOnPreferenceClickListener(this);
}
public CustomPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setOnPreferenceClickListener(this);
}
public CustomPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setOnPreferenceClickListener(this);
}
public boolean onPreferenceClick(Preference preference) {
//Do something here...
return true;
}
}
The important line above is to register the preference click with itself setOnPreferenceClickListener(this);.

Categories

Resources