I have a preferences screen with on checkbox header. In response to it being checked other preferences in that screen are enabled or disabled.
One of these preferences should also change it's icon and text as well as the disabled state.
I can change the state, but the icon and text won't update.
xml layout for the custom preference:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingRight="?android:attr/scrollbarSize" >
<ImageView
android:id="#+id/icon_wearable"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingRight="5dp"
android:src="#drawable/wear_notifications_enabled" />
<TextView
android:id="#+id/txt_wearable_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="7"
android:text="#string/notifications_enabled_wearable_preferences_title"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="#drawable/actionbar_info" />
</LinearLayout>
There are image and text resources for the disabled state.
The Preference class (comments in the code show the evolution and of my attempts to get it to work):
public class WearablePreference extends Preference {
private TextView txt;
private ImageView img;
public WearablePreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.wearable_notification_layout);
// setWidgetLayoutResource(R.layout.wearable_notification_layout);
}
public WearablePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.wearable_notification_layout);
// setWidgetLayoutResource(R.layout.wearable_notification_layout);
}
public WearablePreference(Context context) {
super(context);
setLayoutResource(R.layout.wearable_notification_layout);
// setWidgetLayoutResource(R.layout.wearable_notification_layout);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
if (txt == null && img == null) {
txt = (TextView) view.findViewById(R.id.txt_wearable_title);
img = (ImageView) view.findViewById(R.id.icon_wearable);
handleStateChange();
}
}
public void handleStateChange() {
final boolean enabled = super.isEnabled();
if (txt != null && img != null) {
txt.post(new Runnable() {//also tried it not as a separate runnable since the click event is caught and handled in the main thread anyway, but still worth the try...
public void run() {
int imgResource = enabled ? R.drawable.wear_notifications_enabled
: R.drawable.wear_notifications_disabled;
int txtResource = enabled ? R.string.notifications_enabled_wearable_preferences_title
: R.string.notifications_disabled_wearable_preferences_title;
txt.setText(txtResource);
img.setImageResource(imgResource);
notifyChanged();
}
});
}
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
// handleStateChange();
}
}
and finally the code that should have made this happen:
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.notification_preferences);
wearableNotificationPreferences = (WearablePreference) findPreference(PREFERENCE_WEARABLE);
private void toggleAllItems(boolean enabled)
{
if (wearableNotificationPreferences!=null) {
wearableNotificationPreferences.setEnabled(enabled);
wearableNotificationPreferences.handleStateChange();
}
}
In the last bit of code, the enabled state change happens, but that's it. The icon and text won't change.
I can't use preferences change listeners, as there is no preference change on my particular item, though I'm considering adding a stub preference just so I can get it to work.
Does anyone know why the text and icon won't update and/or what I can do to get them to change?
Thanks,
Omer.
I managed to solve it.
I removed all the event handling code, added a boolean preference entry with the preference key, and I set the value inside the "onBindView" method.
If anyone is interested, here's the code (important changes enclosed in '**'):
#Override
protected void onBindView(View view) {
super.onBindView(view);
// if (txt == null && img == null) {
txt = (TextView) view.findViewById(R.id.txt_wearable_title);
img = (ImageView) view.findViewById(R.id.icon_wearable);
**boolean enabled=MyApp.getPrefs().getUserPref(getKey(), true);**
int imgResource = enabled ? R.drawable.wear_notifications_enabled
: R.drawable.wear_notifications_disabled;
int txtResource = enabled ? R.string.notifications_enabled_wearable_preferences_title
: R.string.notifications_disabled_wearable_preferences_title;
txt.setText(txtResource);
img.setImageResource(imgResource);
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
**MyApp.getPrefs().setUserPref(enabled, getKey());**
}
Related
My app uses an AutoCompleteTextView to display data from a list as part of a search, like so:
I want the search field to be visible, so I added the following to my Fragment:
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
and sure enough, it works as expected when I touch the search text field:
However, when I begin typing characters into the text field, the drop-down list is obscured by the keyboard UNLESS I set dropDownWidth to 101 dp or less:
For example, when I set it to 102 dp or match_parent, I get the following:
I can scroll the list, but it remains obscured.
I searched for the problem on Stack Overflow, but nothing came up. BTW, I'm running on a Pixel 2 with Android 10 and, interestingly enough, the problem doesn't occur on an emulated Huawei 8 running API 26.
I faced this problem some time ago . Bellow is an example (java code - cause it was some time ago )
I used DelayAutoCompleteTextView and I ve made custom adapter
private void setAutocompleteForPatientName(ArrayList patients) {
contactName = (DelayAutoCompleteTextView) findViewById(R.id.nameET);
contactName.setThreshold(1);
contactName.setAdapter(new YourCustomAdapter(this, contacts));
contactName.setLoadingIndicator(
(android.widget.ProgressBar) findViewById(R.id.pb_loading_indicator));
contactName.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView adapterView, View view, int i, long l) {
DO WHATEVER YOU WANT WITH SELECTION
}
});
}
My adapter was implementing Filterable .
Bellow is the DelayAutocomplete
public class DelayAutoCompleteTextView extends AutoCompleteTextView {
private static final int MESSAGE_TEXT_CHANGED = 100;
private static final int DEFAULT_AUTOCOMPLETE_DELAY = 750;
private int mAutoCompleteDelay = DEFAULT_AUTOCOMPLETE_DELAY;
private ProgressBar mLoadingIndicator;
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
DelayAutoCompleteTextView.super.performFiltering((CharSequence) msg.obj, msg.arg1);
}
};
public DelayAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setLoadingIndicator(ProgressBar progressBar) {
mLoadingIndicator = progressBar;
}
public void setAutoCompleteDelay(int autoCompleteDelay) {
mAutoCompleteDelay = autoCompleteDelay;
}
#Override
protected void performFiltering(CharSequence text, int keyCode) {
if (mLoadingIndicator != null) {
mLoadingIndicator.setVisibility(View.VISIBLE);
}
mHandler.removeMessages(MESSAGE_TEXT_CHANGED);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_TEXT_CHANGED, text), mAutoCompleteDelay);
}
#Override
public void onFilterComplete(int count) {
if (mLoadingIndicator != null) {
mLoadingIndicator.setVisibility(View.GONE);
}
super.onFilterComplete(count);
}
}
and here is the xml
<YOURPACKAGE THAT CONTAINS YOUR CUSTOM VIEW CLASS .DelayAutoCompleteTextView
android:id="#+id/nameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:hint="#string/patientName"
android:imeOptions="flagNoExtractUi|actionSearch"
android:inputType="textCapSentences"
android:paddingRight="20dp"
android:textSize="12sp" />
<ProgressBar
android:id="#+id/pb_loading_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="20dp"
android:visibility="gone" />
</FrameLayout>
In the meantime, I worked around the problem by moving my AutoCompleteTextView to above the list:
Now the list displays just fine:
I think the problem was due to the AutoCompleteTextView being at the bottom of the page.
I'm developing a View that shows a stack of messages making fadeIn,fadeOut and wait time between them. It has a stack of messages and when I need to display some of them I added to it. This view hasn't a specific design so the user can customize like he wants.
I have written well the 3 super constructors.
The XML layout is the next:
[... Others views ...]
<com.example.lectorrss.Views.StatusMessage
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/statusMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:background="#6894ed">
<ProgressBar
android:id="#+id/progressBar"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/message"
android:layout_toRightOf="#id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Hola"
android:textColor="#fff"></TextView>
</RelativeLayout>
</com.example.lectorrss.Views.StatusMessage>
[... Other views ...]
In this case, I want my custom view shows the views that are inside (progress bar + textview) and then apply the behaviour I want.
The code of my CustomView is:
public class StatusMessage extends ViewGroup{
private List<String> messagesStack;
private Worker worker;
public StatusMessage(Context context) {
super(context);
}
public StatusMessage(Context context, AttributeSet attrs){
super(context, attrs);
}
public StatusMessage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
TextView txtView = (TextView) this.findViewById(R.id.message);
if(txtView == null){
Log.d("BrowsePortal", "TextView null");
}else{
// HERE, THE CODE CAN FIND OK THE VIEW I WANT
Log.d("BrowsePortal", "TextView not null");
}
worker = new Worker(this);
worker.execute();
}
[Other logic code about view]
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
But as my view needs time of wait between messages, and uses animation, I need to create an AsyncTask:
public class Worker extends AsyncTask<Void,String,Void>{
private StatusMessage view;
public Worker(StatusMessage view) {
this.view = view;
}
#Override
protected Void doInBackground(Void... arg0) {
[I supressed all logic code here]
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
TextView txtView = (TextView) view.findViewById(R.id.message);
if(txtView == null){
// But here the code return null
Log.d("BrowsePortal", "TextView null");
}else{
Log.d("BrowsePortal", "TextView not null");
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
[I supressed all logic code here]
}
}
My problem is that from asynctask, on the progressUpdate, when I need to access to the textview, findviewbyid returns null. But trying to access the same view from custom view class instead asynctask, it works. Why from view class I can access to textview but from asynctask not?
Also, in post execute, the same code returns me null.
My asynctask receives well in her constructor the view so from here would work well as from customview.
The issue was with this line:
import android.R which I change to import [app_package].R;.
Then, when I put this line:
TextView txtView = (TextView) view.findViewById(R.id.message);
... the method could not find the textview because was using as ID, a resource from android.R instead of package.R.
The most strange is that along activities I made this time ago, sometimes I did the same mistake, but I could fix quickly because Eclipse said me that the resource specified wasnt found underlining the resource line, but for some reason, in this case didn't notify me. Maybe R.id.message is reserved for android.R.
I have a settings activity, HomeActivity, that is loaded on startup. The launch mode is set to singleTask, due to certain requirements, In the layout of the HomeActivity there is an element with a icon, title and a switch. The idea is that you should be able to enable/disable Call, using the switch. And if you click the row (outside the switch) you are brought to a new activity, CallPreferences, where you can set Call specific settings. At the action bar of CallPreferences a switch should also be present where the user again can enable/disable call. Switches on both activites should reflect the "reality". That is, when the switch is changed, the value is stored to shared prefs. Both switches then read from shared prefs onCreate to set their value to on or off.
In the xml of HomeActivity I have a preference screen that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:foo="http://schemas.android.com/apk/lib/com.xxx.yy" >
<com.xxx.yy.preferences.IconSwitchPreference
foo:icon="#drawable/call_icn"
android:title="#string/call"
android:key="callIconSwitchPreference" >
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.xxx.yy.preferences.CallPreferences"
android:targetPackage="om.xxx.yy" />
</com.xxx.yy.preferences.IconSwitchPreference>
</PreferenceScreen>
IconSwitchPreference is a custom preference layout of mine containing a linear layout, a text view for the title, an image view and a switch:
<?xml version="1.0" encoding="utf-8"?>
<!-- Layout for a Preference in a PreferenceActivity. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
<Switch
android:id="#+id/menu_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="false"
android:focusableInTouchMode="false"
android:onClick="onMenuSwitchClicked" />
</LinearLayout>
And the class that runs the code:
public class IconSwitchPreference extends IconPreference {
public IconSwitchPreference(Context context) {
this(context, null);
}
public IconSwitchPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IconSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.preference_icon_switch);
if (attrs != null) {
int iconResId = attrs.getAttributeResourceValue(XMLNS, "icon", 0);
mIcon = context.getResources().getDrawable(iconResId);
mFilter = attrs.getAttributeValue(XMLNS, "filter");
mUrl = attrs.getAttributeValue(XMLNS, "url");
}
}
}
In CallPreferences I programmatically create the switch and add it to the action bar:
private void createActionBarSwitch() {
ActionBar actionBar = getActionBar();
Switch actionBarSwitch = new Switch(this);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
ActionBar.LayoutParams.WRAP_CONTENT,
ActionBar.LayoutParams.WRAP_CONTENT, Gravity.CENTER_VERTICAL
| Gravity.RIGHT));
actionBarSwitch.setChecked(isSwitchOn());
}
This works and I can set the switch to the stored value, and the switch is updated to reflect the value.
In HomeActivity however the switch is not updated to reflect the value.
The following does not work:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.preference_icon_switch, null);
TextView tv = (TextView)view.findViewById(android.R.id.title);
tv.setText("Test");
Switch menuSwitch = (Switch)view.findViewById(R.id.menu_switch);
menuSwitch.setChecked(sharedPrefs.isCallhandlingEnabled());
}
Neither is the text view changed to test, nor is the switch enabled (default value is false).
The following however works:
final IconSwitchPreference ic = (IconSwitchPreference) findPreference("callIconSwitchPreference");
ic.setTitle("Test");
The title is set to "Test". The only problem is that I don't have any reference to the switch, so I can update it's value. Can IconSwitchPreference.java be updated to extract and store a reference to the switch in its used xml?
I've tried a number of solutions and code samples; but all of them have something that is not working. Another solution would be to use a standard SwitchPreference, but it doesn't differ between a click on the switch itself to change its state (without going to the new activity) and a click on the row to enter the activity (without changing the switch value).
Solved the issue by adding the following tag to the HomeActivity xml:
foo:switchPref="#string/call_enabled_pref_key"
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:foo="http://schemas.android.com/apk/lib/com.xxx.yy" >
<com.xxx.yy.preferences.IconSwitchPreference
foo:icon="#drawable/call_icn"
android:title="#string/call"
android:key="callIconSwitchPreference"
foo:switchPref="#string/call_enabled_pref_key" >
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.xxx.yy.preferences.CallPreferences"
android:targetPackage="om.xxx.yy" />
</com.xxx.yy.preferences.IconSwitchPreference>
</PreferenceScreen>
And then in IconSwitchPreference.java I extract the res id:
mSwitchPrefResId = attrs.getAttributeResourceValue(XMLNS, "switchPref", 0);
public IconSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.preference_icon_switch);
if (attrs != null) {
int iconResId = attrs.getAttributeResourceValue(XMLNS, "icon", 0);
mIcon = context.getResources().getDrawable(iconResId);
mFilter = attrs.getAttributeValue(XMLNS, "filter");
mUrl = attrs.getAttributeValue(XMLNS, "url");
mSwitchPrefResId = attrs.getAttributeResourceValue(XMLNS, "switchPref", 0);
}
}
}
onBindView in the same class (IconSwitchPreference.java) I got a reference to the Switch:
#Override
public void onBindView(View view) {
super.onBindView(view);
mSwitch = (Switch) view.findViewById(R.id.menu_switch);
updateSwitch();
}
Then I used mSwitchPrefResId to fetch the shared prefs (to know if the switch is enabled or not) and set mSwitch to the corresponding value.
I search around stackoverflow and find next related topics:
How can i style an Android Switch?
Custom switch widget in Android 4
Set switchStyle - get error resource not found - why?
I also find bugreport on google group: Issue 36636: Unable to override style switchStyle
And at last find new probles with Switch widget:
I tried to make my own Preference.SwitchPreference and define layout with Switch widget
android:id="#+android:id/switchWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:thumb="#drawable/switch_thumb"
android:layout_gravity="center"
android:padding="16dip"
android:focusable="false" />
but I get an compliation error: Error: Resource is not public. (at 'id' with value '#+android:id/switchWidget'). So I can't use this way.
Second way I tried to extend Switch class add set resources from code. But I find that method setThumbResource is availible only from API 16. But I still can't apply #+android:id/switchWidget because it's not public.
So, How can I get custom Switch Preference for SDK API 15 ??? Or how can I customize Switch in Preferences?
Just found an awful way to achieve this.
First, src/com/myapp/views/preference/MySwitchPreference.java
public class MySwitchPreference extends SwitchPreference {
public MySwitchPreference(Context context) {
super(context);
}
public MySwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySwitchPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
if (view instanceof ViewGroup) {
setLayout((ViewGroup) view);
}
}
#SuppressLint("NewApi")
private void setLayout(ViewGroup viewGroup) {
int count = viewGroup.getChildCount();
for(int n = 0; n < count; ++n) {
View childView = viewGroup.getChildAt(n);
if(childView instanceof Switch) {
final Switch switchView = (Switch) childView;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
switchView.setThumbResource(com.myapp.R.drawable.switch_inner);
switchView.setTrackResource(com.myapp.R.drawable.switch_track);
}
return;
}
else if (childView instanceof ViewGroup)
setLayout((ViewGroup) childView);
}
}
}
And now, res/xml/preferences.xml
<com.myapp.views.preference.MySwitchPreference
android:switchTextOff="Off"
android:switchTextOn="On"
android:title="whatever"
android:key="switch" />
A little bit tricky, and only working with Android > 16.
Don't know much about the switch issues but you could use a ToggleButton as follows:
Define the button in your layout:
<ToggleButton
android:id="#+id/your_awesome_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center_vertical|center_horizontal"
android:layout_marginRight="15dp"
android:textOn=""
android:textOff=""
android:background="#drawable/toggle_button"
/>
Create a selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_checked="false"
android:state_focused="false"
android:drawable="#drawable/switch_off_btn" />
<item
android:state_checked="true"
android:state_focused="false"
android:drawable="#drawable/switch_on_btn" />
<item
android:drawable="#drawable/switch_off_btn" />
</selector>
OnClickListener:
toggleOnOff = (ToggleButton) findViewById(R.id.your_awesome_toggle);
toggleOnOff.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateButtons();
if(toggleOnOff.isChecked()){
SharedPreferences emailPrefs = getSharedPreferences(rememberToggleOnOff,MODE_PRIVATE);
SharedPreferences.Editor editor = yourPrefs.edit();
editor.putBoolean("mon", true);
editor.commit();
}
else {
SharedPreferences emailPrefs = getSharedPreferences(rememberToggleOnOff,MODE_PRIVATE);
SharedPreferences.Editor editor = yourPrefs.edit();
editor.putBoolean("mon", false);
editor.commit();
}
}
});
checkToggleState();
checkToggleState method:
/**
* Checks the state of the Toggle button preferences.
* If preferences are true set the toggle to on, if false set the toggle off.
*
*/
private void checkToggleState() {
SharedPreferences yourPrefs = getSharedPreferences(rememberToggleOnOff,MODE_PRIVATE);
boolean mON = yourPrefs.getBoolean("mon", true);
if(mON) {
toggleOnOff.setChecked(true);
}
else {
toggleOnOff.setChecked(false);
}
}
Change:
android:id="#+android:id/switchWidget"
To:
android:id="#+id/switchWidget"
A simple switch example can be found here.
Switch widget supports 14 and above API level only, but if you want to use Switch Preference pre API level 14, check this.
UPDATE: If you want to style your own switch, try this
Inherit SwitchPreference class and use it in preferences.xml with layout pointing to your custom layout. Then in the onBind method of inherited SwitchPreference class you can find corresponding view by id and set listeners. Don't forget to call super in onBind().
Change:
android:id="#+android:id/switchWidget"
To:
android:id="#*android:id/switchWidget"
I'm facing a problem where I know the root cause but don't see a way to fix it. If a custom compound component is used multiple times in an activity, the values saved from views will overwrite each other. To explain it easier I made the following example.
The xml for the new component, only an EditText to make it shorter.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<EditText
android:id="#+id/custom_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number" >
</EditText>
</merge>
The class implementing the new behavior, only inflating the layout.
public class CustomView extends LinearLayout {
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_view, this, true);
}
}
And a layout using 2 of them.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<test.customview.CustomView
android:id="#+id/customView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</test.customview.CustomView>
<test.customview.CustomView
android:id="#+id/customView2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</test.customview.CustomView>
</LinearLayout>
When the screen is rotated, the value from second View is also restored in the first one.
Digging into the framework's code I found out that Parcelable objects returned from onSaveInstanceState defined in View class are put in a SparseArray with the key object's id. Because I'm including CustomView multiple times the EditText with id "custom_text" is also getting added multiple times. Having the same id, values saved will overwrite each other.
I'm looking for any suggestion on how this should be actually implemented. Right now, I don't see any way to change those identifiers.
Seems like I have some solution with this problem. I try to find it for some time.
1.First you must create inner class which extends BaseSavedState, inside your CustomView.
CustomView{
String value; //some text value from edittext
EditText edittext;
...
private static class Save extends BaseSavedState{
String savedValue;
public Save(Parcel incoming) {
super(incoming);
savedValue = incoming.readString();
Log.i("Save", "Parcel");
}
public Save(Parcelable parcelable) {
super(parcelable);
Log.i("Save", "Parcelable");
}
#Override
public void writeToParcel(Parcel outcoming, int flags) {
super.writeToParcel(outcoming, flags);
outcoming.writeString(savedValue );
Log.i("Save", "writeToParcel");
}
public static final Parcelable.Creator<Save> CREATOR =
new Creator<CustomView.Save>() {
#Override
public Save[] newArray(int size) {
Log.i("Parcelable.Creator<Save>", "newArray");
return new Save[size];
}
#Override
public Save createFromParcel(Parcel incoming) {
Log.i("Parcelable.Creator<Save>", "createFromParcel");
return new Save(incoming);
}
};
}
}
2.then override this two methods in CustomView
CustomView{
String value; //some text value from edittext
EditText edittext;
...
#Override
protected Parcelable onSaveInstanceState() {
Log.i("CustomView", "onSaveInstanceState");
Parcelable p = super.onSaveInstanceState();
Save save = new Save(p);
save.savedValue = value; // value is from CustomView class
return save;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Log.i("CustomView", "onRestoreInstanceState");
if(!(state instanceof Save)){
super.onRestoreInstanceState(state);
return;
}
Save save = (Save) state;
value = save.savedValue;
//setting in this place value to edittext will not do anything.
//instead, you have to do this in step 3
super.onRestoreInstanceState(save.getSuperState());
}
...
}
3.override onAttachedToWindow() and set to edittext "value".
CustomView{
String value; //some text value from edittext
EditText edittext;
...
#Override
protected void onAttachedToWindow() {
edittext.setText(value);
super.onAttachedToWindow();
}
...
}
and now you can have multiple instances of your Custom View 's that are resistant to change orientation - they will have the correct values.I have not tested this solution in 100% but it seems to be good.