3

I have fragment, where I call custom view-class (Dragger)

public class CanvasFragment extends Fragment {

    private Dragger dragger;

    public CanvasFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_canvas, container, false);

        dragger = (Dragger) rootView.findViewById(R.id.dragger);

        int[] symbolIndex = {};

        dragger.setElementsArray(symbolIndex);
        return rootView;
    }

    public void addIconToCanvas(int id){
        dragger.addIcon(id);
    }
}

and I have another fragment with RecyclerView and adapter, in which I call CanvasFragment's method addIconToCanvas() through the host Activity of CanvasFragment.

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {

    private int[] data;
    private Context context;

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView itemView;
        public ViewHolder(View v) {
            super(v);
            itemView = (ImageView) v.findViewById(R.id.im_item);
        }
    }

    public ItemAdapter(Context context, int[] myDataset) {
        data = myDataset;
        this.context = context;
    }

    @Override
    public ItemAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        holder.itemView.setImageResource(data[position]);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            ((MainActivity)context).addItemToCanvas(data[position]);
            }
        });
    }

    @Override
    public int getItemCount() {
        return data.length;
    }
}

I found that after rotation dragger in CanvasFragment gets new reference, but when I call addIconToCanvas() from adapter - dragger is null.

I really can't understand why field gets reference and then lose it)))

Does anybody knows, what is wrong with that?

Thanks in advance!

P.S.: host activity

public class MainActivity extends AppCompatActivity {

    private CanvasFragment canvasFragment = new CanvasFragment();
    private ItemsFragment itemsFragment = new ItemsFragment();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                .add(R.id.container_canvas, canvasFragment)
                .commit();
            getSupportFragmentManager().beginTransaction()
                .add(R.id.container_items, itemsFragment)
                .commit();
        }

    }

    public void addItemToCanvas(int id){
        canvasFragment.addIconToCanvas(id);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
micsha123
  • 655
  • 2
  • 7
  • 20

3 Answers3

3

With the line

private CanvasFragment canvasFragment = new CanvasFragment();

you assign a new instance of your Fragment to the variable canvasFragment every time the Activity is created. But only if savedInstanceState == null you add this instance of CanvasFragment to the UI.

The Fragment method onCreateView() will only be called if the Fragment needs to be shown, so dragger remains null for the newly created instance after a configuration change.

What can you do?

Only create an instance of CanvasFragment if you want to add it to the UI. In cases where savedInstanceState != null, find the already existing Fragment like this:

canvasFragment = (CanvasFragment)getSupportFragmentManager().findFragmentById(R.id.container_canvas);
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
1

Remove the line setRetainInstance(true); from your fragments and try.

Bob
  • 13,447
  • 7
  • 35
  • 45
  • this line was added after first crash. So it doesn't make any difference – micsha123 Apr 28 '16 at 11:14
  • In the MainActivity, savedInstanceState will not be null after device orientation change. so the statements inside 'if check' will not be executed after device orientation. can you please post the full code of the activity. – Bob Apr 28 '16 at 11:23
  • I've edited question. There's full code of host activity – micsha123 Apr 28 '16 at 11:29
  • Remove the line if(savedInstanceState == null) from the activity and remove the setRetainInstance(true) from both the fragments, and try. – Bob Apr 28 '16 at 11:37
  • It could be good way, but it's closer to workaround than solution. But anyway thank you! – micsha123 Apr 28 '16 at 11:46
  • 1
    I dont think its a workaround. setRetainInstance(true) should not be used to retain the fragment with UI. It could be used to retain stateless fragment to hold large data after orientation change. So during the orientation change, the fragment must be destroyed and a new fragment must be added after the orientation change. So if(savedInstanceState == null) check must be removed, to add a new fragment after orientation change. – Bob Apr 28 '16 at 11:55
  • Well, yes, you're right. IMHO it's better to save state than remove all check's) Vote your answer, man! – micsha123 Apr 28 '16 at 12:02
  • 1
    :) Also, using setRetainInstance(true) might lead to Activity leaks. check this out. http://stackoverflow.com/a/13422819/4586742 – Bob Apr 28 '16 at 12:23
0

put this:

androidConfigChanges: "keyboardHidden | orientation | screenSize"

in your android xml file where the your mainActivity is defined. something like this

<activity
android:name="com.projectname.MainActivity"
android:label = "@string/title_activity_main"
</activity>

then it should luck like this:

<activity
android:name="com.projectname.MainActivity"
androidConfigChanges: "keyboardHidden | orientation | screenSize"
android:label = "@string/title_activity_main"
</activity>
Manuel_Rodrigues
  • 560
  • 2
  • 18