1

I would like to ask a simple but misleading question according to my point of view.

I have a dialog, or better, a custom dialog:

public class MyCustomDialog extends Dialog {

public MyCustomDialog(Context context, int layoutResourceId) {
    super(context);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(layoutResourceId);
    LayoutParams params = getWindow().getAttributes();
    params.width = LayoutParams.MATCH_PARENT;
    getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
}
}

This dialog takes a layuoutResourceId (R.layout.some_custom_layout.xml) as argument when it is instantiated. I need to use this dialog to display a custom List of some Master and details data which is dinamically created. In order to do this kind of operation I am using a custom Adapter class , called MasterDetailArrayAdapter:

public class MasterDetailArrayAdapter extends ArrayAdapter<Item> {

private LayoutInflater mdInflater;

public enum EntryType {
    DETAIL, MASTER
}

public MasterDetailArrayAdapter(Context context, List<Item> items) {
    super(context, 0, items);
    mdInflater = LayoutInflater.from(context);
}

@Override
public int getViewTypeCount() {
    return EntryType.values().length;
}

@Override 
public int getItemViewType(int position) {
    return getItem(position).getViewType();
}

@Override 
public View getView(int position, View convertView, ViewGroup parent) {
    return getItem(position).getView(mdInflater, convertView);
}

}

This adapter extends an ArrayAdapter of Item, where Item is an interface I have created:

 public interface Item {

   public int getViewType();
   public View getView(LayoutInflater inflater, View convertView);
 }

So The MasterDetailArrayAdapter takes a List as parameter when it is constructed. The Item interface is implemented by a MasterWithValue class (or better by a Master class which MasterWithValue extends):

public class MasterWithValue extends Master {

private String value;
private List<Detail> detailList;

public MasterWithValue(String masterName, String masterValue) {
    super(masterName);
    this.value = masterValue;
    this.detailList = new ArrayList<Detail>();
}

@Override 
public int getViewType() {
    return super.getViewType();
}

@Override
public View getView(LayoutInflater inflater, View convertView) {
    View view;
    if (convertView == null) {
        view = inflater.inflate(R.layout.statistics_rowlist_master, null);
    }
    else {
        view = convertView;
    }

    TextView MasterEntryName = (TextView) view.findViewById(R.id.statistics_master_name);
    TextView MasterEntryValue = (TextView) view.findViewById(R.id.statistics_master_value);

    MasterEntryName.setText(super.name);
    MasterEntryValue.setText(this.value);

    return view;
}

public String getMasterValue() {
    return value;
}

public List<Detail> getDetailList() {
    return this.detailList;
}

public void addDetailToMaster(Detail detail) {
    this.detailList.add(detail);
}

 }

And Detail class:

public class Detail implements Item {

public final String detailName;
public final String detailValue;

public Detail(String detailName, String detailValue) {
    this.detailName = detailName;
    this.detailValue = detailValue;
}

@Override
public int getViewType() {
    return EntryType.DETAIL.ordinal();
}

@Override
public View getView(LayoutInflater inflater, View convertView) {
    View view;
    if (convertView == null) {
        view = inflater.inflate(R.layout.institutional_rowlist_detail, null);
    }
    else {
        view = convertView;
    }

    TextView detailEntryName = (TextView) view.findViewById(R.id.institutional_detail_name);
    TextView detailEntryValue = (TextView) view.findViewById(R.id.institutional_detail_value);
    detailEntryName.setText(this.detailName);
    detailEntryValue.setText(this.detailValue);

    return view;
}

}

Now the code where it's all built:

                      // instantiating a new CustomDialog class
                dialog = new MyCustomDialog(thisContext,  R.layout.institutional_info_custom_list);
                DetailListView = (ListView) dialog.findViewById(R.id.custom_dialog_list);
                final MasterDetailArrayAdapter adapter = new MasterDetailArrayAdapter(ComeHaInvestito.this, MasterAndDetailstatisticsInfoList);         
                DetailListView.setAdapter(adapter);

thisContext is the context of the activity, MasterAndDetailstatisticsInfoList is a of Item but this is not the point. My result is this:

enter image description here

Everything works perfectly, except if you rotate the screen when the dialog is showed: The dialog closes and then it is NEVER re-showed again. It is so frustrating, sorry for saying that, but it is all day I am trying to figure it out and how can I resolve it.

I understand that when you rotate the screen of the device, the activity is destroyed and recreated, but the dialog is not. I need to keep it on the screen when the device is rotated but I can't use the Manifest solution, thus:

android:configChanges="orientation|keyboardHidden|screenSize

Does not work for me cause I have some other graphical Views that change their position on screen rotate in that way.

So I am looking for another solution. I see that everyone here on SO and in other discussion forums talks about using the Fragments almost everytime you need to retain some data on android device config changes ( this includes screen orientation change ) so I was thinking of doing that, but with the solution I am currentry using I have no idea on how implement a fragment which will retain my custom dialog without messing the code I have already written. So guys I really need some help on this.

Is there a way to integrate a custom dialog class (MyCustomDialog in my case) using Fragments and make it retained on screen orientation change?

Thanks for the attention! Hope for some help!

EDIT: I have made an interface called ActivityToFragmentInteraction. Now with this implementation I can see the Dialog on screen orientation change but the ListView inside of it still disappears. How can I fix it?

public class MyCustomDialogFragmentWithListItem extends DialogFragment {

private List<Item> dialogFragmentItemList;
private Context context;

private ActivityToFragmentInteraction activityToFragmentInteraction;

public interface ActivityToFragmentInteraction {
    public List<Item> getListItem();
}

public static MyCustomDialogFragmentWithListItem newInstance(int dialogLayoutResourceId, int dialogListViewResourceId) {
    MyCustomDialogFragmentWithListItem dialogFragment = new MyCustomDialogFragmentWithListItem();
    Bundle args = new Bundle();
    args.putInt("dialog_layout", dialogLayoutResourceId);
    args.putInt("dialog_list_view_id", dialogListViewResourceId);
    dialogFragment.setArguments(args);
    return dialogFragment;
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    this.context = getActivity();

    try {
        activityToFragmentInteraction = (ActivityToFragmentInteraction) activity; 
    } 
    catch (ClassCastException e) {
        e.printStackTrace();
    }
}

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) {
    int dialogLayoutResourceId = getArguments().getInt("dialog_layout");
    int dialogListViewResourceId = getArguments().getInt("dialog_list_view_id");
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    // getting the layout inflater and inflating the view recovered using the dialogLayoutResourceId
    // passed before as argument to the factory method.
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(dialogLayoutResourceId, null);
    builder.setView(view);

    // getting the List<Item> from the activityToFragmentInteraction interface
    dialogFragmentItemList = activityToFragmentInteraction.getListItem();

    // recovering the ListView of the DialogFragment
    ListView listView = (ListView) view.findViewById(dialogListViewResourceId);
    // setting the adapter for the ListView of the DialogFragment.
    final MasterDetailArrayAdapter adapter = new MasterDetailArrayAdapter(context, dialogFragmentItemList);
    listView.setAdapter(adapter);

    return builder.create();
}

}
tonix
  • 6,671
  • 13
  • 75
  • 136

1 Answers1

1

You can use the DialogFragment. What you need to change is your MyCustomDialog.class to extend DialogFragment and the responsability of create the listview and the adapter will be of your dialog too.

Something like this (my example is more simple, but I think will help to understand).

protected void showMyCustomDialog(){
    MyCustomDialog newFragment = new MyCustomDialog();
    newFragment.show(getFragmentManager());
}

protected class MyCustomDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // Get the layout inflater
        LayoutInflater inflater = getActivity().getLayoutInflater();

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        View view = inflater.inflate(R.layout.my_layout, null);
        // now you customize your view!
        //...
        builder.setView(view);
        return builder.create();
    }
}
Scoup
  • 1,323
  • 8
  • 11
  • is there a way to pass the R.layout.my_layout where you set the dialog view dinamically, I mean if I need to reuse this code and set another layout to the same dialog, what should I do? Create a property in my class or something like that? – tonix Mar 21 '14 at 09:12
  • Sure you can. Look this: http://stackoverflow.com/questions/12739909/send-data-from-activity-to-fragment-in-android Use bundle.putInt. http://developer.android.com/reference/android/os/Bundle.html#putInt(java.lang.String, int) – Scoup Mar 21 '14 at 16:04
  • Thanks! That's what I need, however I also have a custom ListView that gets filled using a custom adapter class which extends ArrayAdapter. The problem is that the adapter takes a List as a parameter when it is constructed, so I guess I need to pass the List to the DialogFragment, is there a simple and efficient way to do so? – tonix Mar 21 '14 at 17:53
  • 1
    Well, you can let your fragment get the data instead of your activity, or implement some interface to do that. Just don't pass the data directly to the fragment object because this data will gona be lost on rotation (that is the problem you are trying to solve) – Scoup Mar 21 '14 at 18:54
  • Imagine you have some ModelClass to return your list by some ID. Like ModelClass.getListById(int id). You can use initiate this model on your fragment, and pass only the id to the fragment or you can also use gson and serialize data to string, etc. There is a lot of ways to do that, but aways use the bundle to pass values. – Scoup Mar 21 '14 at 19:11
  • I have tried the interface option and I immediately appreciated the result. If I rotate the screen the dialog doesn't disappear. However the listView inside of it does. Is it because how you said I am passing data directly to the fragment object? But I am using an interface... I have posted the code in the EDIT label of my initial post. Sorry for re-asking that, but now that I have used an interface to communicate between the Activity and the DialogFragment and now that the dialog doesn't disappear on screen rotation, how can I make event the ListView in the dialog to not disappear? – tonix Mar 21 '14 at 20:46
  • I have made it! Thanks! I have implemented the Parcelable interface of the object of my List and then used an ArrayList to save it in onSaveInstanceState(Bundle saveInstance) with the .putParcelableArrayList() method – tonix Mar 21 '14 at 23:47