Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injectview (Roboguice) not working with multiple layouts

So I have different layouts for this one Activity.

And I have different classes that each open and do their thing with a layout.

I inject these classes in the Activity via @Inject. All this is without problem.

But when I try to use @InjectView on one of the controls that are in the not active layout I get an error.

11-02 19:17:31.086: ERROR/AndroidRuntime(1326): Caused by: java.lang.NullPointerException: Can't inject null value into class be.baes.notes.View.EditNoteImpl.saveButton when field is not @Nullable

This would then be the code.

public class EditNoteImpl implements EditNote {
    @Inject CancelEditNoteClickListener cancelEditNoteClickListener;
    @Inject SaveNoteClickListener saveNoteClickListener;
    @Inject Provider<Activity> activity;
    @InjectView(R.id.saveButton) Button saveButton;

    /* (non-Javadoc)
     * @see be.baes.notes.EditNote#activateEditNote()
     */
    @Override
    public void activateEditNote()
    {
        activity.get().setContentView(R.layout.editnote);

        this.saveButton.setOnClickListener(saveNoteClickListener);
    }
}

I can however do this.

public class EditNoteImpl implements EditNote {
    @Inject CancelEditNoteClickListener cancelEditNoteClickListener;
    @Inject SaveNoteClickListener saveNoteClickListener;
    @Inject Provider<Activity> activity;
    private Button saveButton;

    /* (non-Javadoc)
     * @see be.baes.notes.EditNote#activateEditNote()
     */
    @Override
    public void activateEditNote()
    {
        activity.get().setContentView(R.layout.editnote);
        saveButton = (Button)activity.get().findViewById(R.id.saveButton);

        this.saveButton.setOnClickListener(saveNoteClickListener);
    }
}

Is there a better way of doing this?

like image 597
chrissie1 Avatar asked Nov 02 '11 18:11

chrissie1


1 Answers

I have just started to use roboguice so someone else with more experience might be able to give a better answer, but this is what I found so far:

  • In roboguice-1.1.2 (the current stable release), @InjectView items are injected only when setContentView() is called the first time in the Activity. Since your code calls setContentView() dynamically from the auxiliary classes, the injected items there won't be injected correctly.

  • In roboguice-2.0b2 (the current beta), there is support for multiple setContentView() calls and your code should work. However, injected views are still tied to the context Activity (instead of the declaring class) so every @InjectView will potentially also need to be @Nullable across all auxiliary classes sharing the same Activity.

Since the issue here seems to be rooted in the multiple layout (and hence multiple setContentView() calls) in a single Activity, one alternative way to do what you want is to avoid it as follow:

  • Instead of having several layouts, use a single layout using the <include/> tag to load all the layouts into a parent FrameLayout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <include layout="@layout/layout1" />

    <include layout="@layout/layout2" />

    <!-- other layouts... -->

</FrameLayout>
  • then, instead of calling setContentView(), use a method that will toggle the visible layout on the Activity, something like this:
// instead of: activity.setContentView(R.layout.layout1);
// use: activity.showLayout(R.id.layoutview1);
public void showLayout(int layoutViewId) {
    final View view = findViewById(layoutViewId);
    final ViewGroup root = (ViewGroup) view.getParent();
    for (int i = 0; i < root.getChildCount(); i++) {
        final View v = root.getChildAt(i);
        v.setVisibility(v == view ? View.VISIBLE : View.GONE);
    }
}

The above alternative should work for both the stable and beta roboguice releases. The trade-off here is we are loading several layouts at the same time instead of loading each one at several different times. It seems to work well enough for me (though it might be different for your needs).

One thing I should note is that on the current "Upgrading from RoboGuice 1.1 to 2.0" page, the following is mentioned:

The ability to use @InjectView in Views (although you'll need to call RoboGuice.injectMembers() yourself, since there's no RoboView base class and probably never will be).

It seems that this should allow you to implement your auxiliary classes to be derived from View and make @InjectView in them work better (hopefully no need for them to be @Nullable since they are less tied to the Activity). However, looking at the current code, this feature does not seem to be implemented yet (although I could have been looking at the wrong place).

like image 114
Joe Avatar answered Nov 14 '22 22:11

Joe