DialogFragment - setting initial values and retaining state after rotation - android

I have created an DialogFragment which is building and returning AlertDialog from onCreateDialog method. The AlertDialog contains two EditText views.
I'm setting the initial values of these two edit texts in the onCreateDialog method which works great until I rotate the phone and all changes get lost/restored to initial values because the onCreateDialog is recalled.
So my question is where should I place the initial values so they are only set the very first time you open the dialog and if you have done changes and you rotate your phone, the last state i retained and retached?
Below I have pasted simplified version of my code. One solution could be initializing the class attributes at newInstance() method, but then I need to make them static. Other solution could be passing the values through the Bundle, but no put-methods take Calendar as parameter type.
What is best practice?
public class MyDialogFragment extends DialogFragment implements OnClickListener, OnDateSetListener, OnQuantitySetListener
{
private EditText editText1, editText2
private MyObject myObject;
public static MyDialogFragment newInstance()
{
return new MyDialogFragment ();
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater factory = LayoutInflater.from(getActivity());
final View v = factory.inflate(R.layout.my_layout, null);
editText1 = (EditText) v.findViewById(R.id.text1);
editText2 = (EditText) v.findViewById(R.id.text2);
myObject = <get the object from database>;
editText1.setText(myObject.attribute1);
editText2.setText(myObject.attribute2);
bindDataToViews();
return new AlertDialog.Builder(getActivity())
.setIconAttribute(R.drawable.add)
.setTitle("Title of the dialog")
.setView(v)).create();
}
... other methods using getting the values from EditText and putting them back to MyObject
}

Calendar is Serializable so you can put it as that in the Bundle.

You can store your data in onSaveInstanceState(Bundle outState) method, and read them again in onRestoreInstanceState() method. onSaveInstanceState will be called before screen rotation, and onRestoreInstanceState() after change. This is a good place to store data between orientation changes.
Or you can also add in Manifest file
android:configChanges="orientation"
Add this value to activitiy, which contains your alertDialog.

Related

Get Edit text by id from fragment to its hosting Activity

I have one activity which is hosting several tabs(Fragment), now I want to get EditText by id from tab(Fragment) to its hosting activity and set listener on it.
So please help me to get rid of it.
Thanks in advance
To find view by id from fragment, add this:
EditText edittext = getActivity().findViewById(R.id.edittext);
The simple solution is
EditText editText = (EditText)getActivity().findViewById(R.id.viewid);
if( editText != null){
// set listener here
}
In your Activity
public EditText editText;
if(editText != null)
editText.setOnClickListner(this);
In your Fragment
Activity activity = (Activity)context;
//where context is your fragment's context
activity.edtText = (EditText)findViewById(R.id.viewid);
Make sure you set listener when editText is not null otherwise you will get null pointer exeption.
There can be multiple solutions to this. In my opinion the best way is to make an interface named Callback as below
public interface Callback
{
public void onListenerActionPerformed(Some args);
}
Now in whatever fragment you want the listener to be attached, write a function
private Callback callback;
public void setCallback(Callback callback)
{
this.callback=callback;
}
Now register the listener on your EditText in the fragment and inside the body of the corresponding function write
callback.onListenerActionPerformed(Some parameter)
Now from your activity, right below where you have instantiated the fragment write,
fragmentInstanceName.setCallback(new Callback(){
public void onListenerActionPerformed(Some args){
//Your implementation
}
});

EditText Settext not working with Fragment

I have fragments for 3 states of a screen; Add, Edit and View.
In Add, I create an entity and save it.
Next time I open it in View mode and set the entity name using
EditText entityName = (EditText) view.findViewById(R.id.entityName);
entityName.setText(entity.getEntityname());
I click on the edit button from View mode to open the Edit mode. I change the entity name here and save it. This brings me back to the view screen. But I find the entity name is not updated.
I debug and found that entity.getEntityname() is having correct value. I am not sure why the edit text does not take new value.
Any ideas?
Note: I am using android version 2.2
The EditText appears to have an issue with resetting text in onCreateView. So the solution here is to reset the text in onResume. This works.
Also there's an issue in onActivityCreated. I reset edittext's content in onStart and it works. [credits to #savepopulation]
There are some classes of View in Android should save their status when their container is detached.
Fragment.onViewCreated() should be called before View.onSaveInstanceState(). So if you set a value in the method Fragment.onViewCreated(). The value should be cleared in the method View.onRestoreInstanceState(Parcelable state).
For example,class TextView,RecyclerView and so on.You can read the code of TextView.java:
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
final boolean freezesText = getFreezesText();
boolean hasSelection = false;
int start = -1;
int end = -1;
....
if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
....
}
....
}
There are to params to control whether to save the state: "freezesText" and "hasSelection".
TextView can't be selected,so hasSelection is false. the function ,getFreezesText(),return false in class TextView too.
So,TextView would not save the state.
the code of EditText.java:
#Override
public boolean getFreezesText() {
return true;
}
EditText return true,so EditText should save the state.
There some method to fix this bug:
1.implement EditText.getFreezesText() and return false,and clear the state of select in EditText
2.implement onSaveInstanceState of EditText, return null.like this:
public Parcelable onSaveInstanceState() {
super.onSaveInstanceState();
return null;
}
3.use EditText.setSaveEnable(false);
4.add param in xml " saveEnable='false'"
As mentioned earlier, the EditText appears to have an issue with resetting text in onCreateView.
This is because once a fragment is created , till the time we remove it from the backstack, its method onResume would be called as the view is not created again.
So the solution here is to reset the text in onResume. This will work on all times even if u lock and unlock ur screen while that fragment is open or you are coming back from another fragment
However if you are setting this data from a bundle it is better tonsave that value in an instance variable cause the bundle might come null amd u can gett null pointer issues then
According to the #TusharVengrulekar , this is how you must implement your Fragment
public class ActionBar extends Fragment {
private TextView lbl_title;
private String title;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_action_bar, container, false);
title = "Contacts";
lbl_title = (TextView) view.findViewById(R.id.lbl_title);
return view;
}
#Override
public void onStart(){
super.onStart();
lbl_title.setText(title);
}
#Override
public void onResume(){
super.onResume();
}
}<!---->
Also there's an issue in onActivityCreated. I reset edittext's content in onStart and it works.
onResume() or onStart() is fine for resetting the text on the EditText on popBackStack() but the issue is when the app goes into the background either of them will be triggered that would not be the expected behavior from the application. We can do something like this too, to reset the text on EditText-
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
binding.coolEt.setText("xyz")
}
This will work on fragment 100%
override fun onResume() {
super.onResume()
Handler(Looper.getMainLooper()).postDelayed({
editText.setText("Abc")
}, 500)
}

shifting the onCreate() content into the application class

My app contains 25 edittexts. I am getting this 25 edittexts with the help of adapter class by giving count=25 and fitting in gridView by gridView.setAdapter(new TextAdapter(this)); in the activity class. So, the edittexts are dynamically generated. But the thing is I am unable to set the initial values in the edittexts. This is because the edittext objects are unavailable to set the values.
Suppose if I don't set any initial values in the edittexts and continue with my app. The same problem repeats while setting the values back in the edittexts which are entered in previous mode after changing the orientation. Because change in orientation creates new activity. Even I tried android:configChanges="orientation|keyboardHidden", but no use while I am setting the values back in the **onConfigurationChanged()**. Because I am setting the setContentView(); in the onConfigurationChanged() as I need the respective view, but still the edittext objects are unavailable to set their values.
Is there any solution to set back the values? If not, I am thinking(Might be completely wrong way, but as a newbie please go easy) to move the onCreate() method content to Application class. So the initial part goes to Application class including the creation of edittexts. and getting that edittext objects in the onCreate() method to set the values. Is it possible? Please suggest. Code snippet would be appreciated.
You will need to modify TextAdapter. Store the initial values in a String array, with the position of the String array element aligned to the position of the EditText in your GridView.
Pseudo-code (untested):
public class TextAdapter extends BaseAdapter {
String [] initial_value = {"Initial Value 1", "Initial Value 2", "Initial Value 3", ..., };
public View getView(int pos, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.edit_text_container, null);
}
(EditText) edtTemp = (EditText) view.findViewById(R.id.edit_text_id);
edtTemp.setText(initial_value[pos]);
}
}

Retaining list in list fragment on orientation change

I have an app using fragments, all of which are contained in a single activity. The activity starts with a fragment containing a menu of buttons, all of which cause various listfragments to replace the original button/menu fragment.
My problem is that upon an orientation change, if the activity is displaying one of the listviews, it goes away and the button menu returns. I understand why this is happening... the activity is destroyed and re-created, but not how to work around it and maintain the list view/current fragment through the orientation change.
I've found setRetainInstance and the example of use here, but I can't figure out how to apply it to my situation with the button menu or the possibility that the fragment I want to retain could be one of several different ones.
Below is code simplified to show the main activity and one of the listfragments.
Any pointers in what to add where to make it so that the list fragment will be retained would be greatly appreciated.
Activity
public class Main extends FragmentActivity {
private MainMenuFragment menu;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
menu = new MainMenuFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.pane, menu).commit();
}
}
ListFragment
public class ItemListFragment extends ListFragment {
private TextView header;
private TextView empty;
private Button add;
public static Cursor itemCursor;
private GroceryDB mDbHelper;
public static long mRowId;
public static CheckCursorAdapter lists;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.common_list, container, false);
header = (TextView) v.findViewById(R.id.header);
empty = (TextView) v.findViewById(android.R.id.empty);
header.setText(R.string.header_item);
empty.setText(R.string.empty_items);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mRowId=0;
mDbHelper = new GroceryDB(getActivity());
mDbHelper.open();
itemCursor = mDbHelper.fetchAllItems();
getActivity().startManagingCursor(itemCursor);
String[] from = new String[] { GroceryDB.ITEM_NAME };
int[] to = new int[] { R.id.ListItem };
lists = new CheckCursorAdapter(getActivity(),
R.layout.listlayout_itemlist, itemCursor, from, to);
setListAdapter(lists);
}
}
how to work around it and maintain the list view/current fragment through the orientation change
You are blindly replacing the fragment every time onCreate() is called. Instead, only add/replace the fragment if savedInstanceState() is null. If it is not null, you are coming back from a configuration change, and your existing fragments will be recreated (or, if they were retained, they are already there).
setRetainInstance(true) means that the fragment itself will be retained across configuration changes, instead of being destroyed/recreated like the activity is. However, it will still be called with onCreateView(). In your code, that means that your data members of ItemListFragment would stick around, but you would still need to call setListAdapter() even if you do not requery the database.
I know that this has been resolved a long time ago, but for the sake of people searching for a solution who have as much issues as I've (repeatedly) had with retaining lists during an orientation change I would like to add that you could also use a custom class which holds the list of data for your listadapter.
This way it keeps the data when recreating the activity (and listfragment) and you can just test to see if it has any data in your oncreate. If the list == null or the list.size < 0 you proceed as usual and get the data whatever way you normally get it. Otherwise you just set your listadapter with the data it already has.
To me this is a lot easier, and seeing as Eclipse automatically creates a similar DummyContent class for your data when creating an android master/detail flow project it basically only requires a change of the oncreate of your listfragment.

How to restore state of an Android View (UI)?

I'm trying to save state of my view in one activity and pass it to another activity using a Bundle. In the second activity I try to restore the view state using the bundle.
First Activity
private View CreateView() {
ScrollView scrollView = new ScrollView(this);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(android.widget.LinearLayout.VERTICAL);
scrollView.addView(layout);
Button btn = new Button(this);
btn.setId(100);
btn.setText("Button Text");
btn.setOnClickListener(new ClickListener());
layout.addView(btn);
return scrollView;
}
onCreate
super.onCreate(savedInstanceState);
View v = CreateView();
setContentView(v);
Saving state
SparseArray<Parcelable> array = new SparseArray<Parcelable>();
view.saveHierarchyState(array);
Bundle bundle = new Bundle();
bundle.putSparseParcelableArray("state", array);
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtras(bundle);
startActivity(intent);
Second Activity onCreate
bundle = this.getIntent().getExtras();
View view = new View(this);
view.restoreHierarchyState(bundle.getSparseParcelableArray("state"));
setContentView(view.getRootView());
Button btn = (Button)findViewById(100);
Everything works without an exception. However, I face two issues:
1. The view in second activity is blank. Though I've restored the saved state I can't see anything
2. Instance for the button (with id 100) in second activity is always null
While debugging I can see one of the values in the bundle having an id 100
Any help on what I seem to be doing wrong will be appreciated. Thanks
I figured out that it is not possible to restore a view in a different activity from which it was initially created (or rendered). Since View is not a serializable type, it can't be send in its entirety as well. At this point there doesn't seem to be any solution (I haven't explored option of modifying Android source code)
Why are you trying to create a View programatically and sending it to another activity? You could simply use the same layout in both activities and then only pass the data that backs the view. That too there would be more convenient ways than using a parcelable?
Could you elaborate on what you are trying to achieve here? Maybe we can give you a better response then....
Have you tried doing this in onSavedInstanceState(...)?
From the Android documentation:
onSaveInstanceState(Bundle) is called before placing the activity in
such a background state, allowing you to save away any dynamic
instance state in your activity into the given Bundle, to be later
received in onCreate(Bundle)
The only approach I've taken with this is using the onSaveInstanceState and onRestoreInstanceState.
public void onSaveInstanceState(Bundle outState){
//save the state of the sign up views!
Serializable page0fields = sign_in_page.getFields();
outState.putSerializable("page0fields", page0fields);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
//restore the state of the sign up views!
sign_in_page.fillFields(
savedInstanceState.getSerializable("page0fields")
);
super.onRestoreInstanceState(savedInstanceState);
}
On the other hand, you seem to desire a much more automated approach to this. I don't believe you can pass around views arbitrarily, as they aren't serializable, though I may be mistaken.

Categories

Resources