1

I have a class like this:

public Class Row {
    private int data = 0;
    private View.OnClickListener callback = null;

    public void setData(int a) { data = a; }
    public void setOnClickListener(View.OnClickListener i) { callback = i; }
}

And I like to provide an instance of this class to a Fragment. Considering a member interface, I cannot Serialize or Parcel this class and send it via setArgument().

What do you suggest for providing this class to my Fragment? I thought of 3 methods, but I wonder what is better:

1-Providing class via a setter functions:

Myfrag frag = new Myfrag();
Row r = new Row();
r.setOnClickListener(this);
frag.setRow(r);
getSupportFragmentManager().beginTrasaction().add(R.id.containder_id, frag).commit();

Some people discourage using this method, because it seems that this method may fail on Fragment recreation.

2-Proving class via a callback interface In this case activity provides an interface which returns class for Fragment:

public class MyActivity extend Activity implements Myfrag.OnGetRow{
    private Row mRow = null;
    ...

    public createFragment() {
        Myfrag frag = new Myfrag();
        mRow = new Row();
        mRow.setOnClickListener(this);
        getSupportFragmentManager().beginTrasaction().add(R.id.containder_id, frag).commit();
    }

    public Row getRow() {
        return mRow;
    }
}

And in Fragment:

public class MyFrag extend Fragment {
    ...
    public interface OnGetRow {
        public Row getRow();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        OnGetRow mCallback  = null;

        try {
            mCallback = (OnGetRow) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                + " must implement OnHeadlineSelectedListener");
        }

        mRow = mCallback.getRow();
    }
}

3-Just provide a public function for activity, and call it in onAttach() of Fragment.

What do you suggest for this problem?

Afshin
  • 8,839
  • 1
  • 18
  • 53
  • Hm... since you are passing in object that call some other callback...it sounds strange to me. Maybe you should reconsider architectural change in your code. If not, maybe you can try Dagger2 injection framework. – Bozic Nebojsa Jun 13 '16 at 09:20
  • @BozicNebojsa No,it is not strange. I'm writing a library for settings. Each instance of a class (Similar to this, but much more complex) provide an item for my settings. Those member interfaces are for handling clicks on settings items in main activity. – Afshin Jun 13 '16 at 09:35
  • Possible duplicate of [Best practice for instantiating a new Android Fragment](http://stackoverflow.com/questions/9245408/best-practice-for-instantiating-a-new-android-fragment) – Anirudha Agashe Jun 13 '16 at 09:42
  • @AnirudhaAgashe Not duplicate. Or I did not found anyone who has handled class memebr interfaces. – Afshin Jun 13 '16 at 09:44

3 Answers3

0

Better approach in my knowledge is that you make Row serializeable and set instance of Row in bundle and set bundle as argument of desired Fragment, after recreation you can get that bundle in onResume() method using function getArguments(); so that your data will persist.

Khizar Hayat
  • 3,427
  • 3
  • 18
  • 22
  • This approach in not applicable here because of interface as a class member variable. This is a small class, but in reality, you may have several complex custom interfaces in class. – Afshin Jun 13 '16 at 09:09
  • Why not , even if interface as a class member variable you can do it. – Khizar Hayat Jun 13 '16 at 09:13
0

Best approach in my opinion is bellow, in this example i have used Dart liblary for less code in your fragment;

public class MyFragment extends Fragment {

    private static final String FIRST_PARAM_KEY = "KEY1";
    private static final String SECOND_PARAM_KEY = "KEY2";

    @InjectExtra(FIRST_PARAM_KEY)
    Integer firstParam;
    @InjectExtra(SECOND_PARAM_KEY)
    String SecondParam;

    public static MyFragment newInstance(Integer param1, String param2) {
        Fragment fragment = new MyFragment();
        Bundle args = new Bundle();
        args.putInt(FIRST_PARAM_KEY, param1);
        args.putString(SECOND_PARAM_KEY, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Dart.inject(this, getArguments());
    }
}

Whenever you want to create new fragment, use: Fragment fragment = MyFragment.newInstance(123, "text"). Remember if you want to pass object, that object have to implements Parcelable.

Artur Szymański
  • 1,639
  • 1
  • 19
  • 22
0

The best approach is put your data in bundle and setArgument in fragment. Here is an example - Let say I have a Object of class UserModel to pass to UserFragment.

public class UserModel  implements Parcelable{
    int id;
    String name;
    String profilePic;

    protected UserModel(Parcel in) {
        id = in.readInt();
        name = in.readString();
        profilePic = in.readString();
    }

    public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
        @Override
        public UserModel createFromParcel(Parcel in) {
            return new UserModel(in);
        }

        @Override
        public UserModel[] newArray(int size) {
            return new UserModel[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeString(profilePic);
    }
}

Now in UserFragement do like this :

public class UserFragment extends Fragment {
    private UserModel userModel;
    private String userStr;
    public static Fragment getInstance(UserModel userModel, String userStr) {
        Bundle bundle = new Bundle();
        bundle.putParcelable("USER_DATA", userModel);
        bundle.putString("USER_STR", userStr);
        UserFragment userFragment = new UserFragment();
        userFragment.setArguments(bundle);
        return userFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(getArguments() != null){
            Bundle bundle = new Bundle();
            userModel = bundle.getParcelable("USER_DATA");
            userStr = bundle.getString("USER_STR");
        }
    }
}

Now use newInstance method to create a new object of the fragment.

Hope it will help you :)

Neo
  • 3,546
  • 1
  • 24
  • 31
  • All samples like your and next answer use simple `Parcelable` or `Serializable` member variables(In your case int ans String). I know I can do this. But my problem is those interfaces. How you will do this if 1 of your class members was `View.OnClickListener` interface? – Afshin Jun 13 '16 at 09:32
  • I am sure you want to send data only to the fragment, nothing else. Just not yet confirmed, why you want to pass your clickListener object to the fragment. – Neo Jun 13 '16 at 09:42
  • I described why I have sent this listener in a comment on previous replay. This listener provides handling clicks related to an instance of this class in activity. I need to have different listeners for different instances of class, so I added this as a class member variable and need to provide it for fragment. – Afshin Jun 13 '16 at 09:48
  • In the case the best approach will be, define your own listener inside the fragment, and pass it's object via setter. Nothing is wrong in this approach, every library works like this only. – Neo Jun 13 '16 at 10:00