9

I have a custom view extending a TextView. Where should I call my component to inject the view?

component.inject(customTextView);
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
Sibelius Seraphini
  • 5,303
  • 9
  • 34
  • 55

3 Answers3

7

So, I've find out that I need to add the injection in the constructor of my custom view (in all of them, or make one call the other)

Example:

public class CustomTextView extends TextView {
   @Inject
   AnyProvider anyProvider;

   public CustomTextView(Context context) { this(context, null); }
   public CustomTextView(Context AttributeSet attrs) { 
      super(context, attrs);
      Application.getComponent(context).inject(this);
   }
}
Sibelius Seraphini
  • 5,303
  • 9
  • 34
  • 55
  • 3
    Are you using dagger 2.11? Can you show me who to get this: Application.getComponent(context)? Can't make my Application return anything but AndroidInjector. Thanks. – GuilhE Jun 06 '17 at 14:47
  • Your `ApplicationComponent` - returned by `applicationInjector()` - should extend `AndroidInjector` so can be cast to `ApplicationComponent`. I got a compiler warning telling me to override the return type of `ApplicationComponent.Builder.build()` to clarify this. It's probably not good to call `applicationInjector()` multiple times as it will create the component many times so I'm storing a reference to it... not sure if that is best practice though. Maybe follow up in a new question. – NeilS Aug 03 '17 at 11:38
  • @GuilhE ... use it, this returns dagger Component daggerAppComponent = DaggerAppComponent.builder().application(this).build(); – Tushar Pandey Dec 31 '17 at 01:09
3

My general solution for that kind of thing is this

public class WelcomeView
        extends LinearLayout {
    private static final String TAG = "WelcomeView";

    public WelcomeView(Context context) {
        super(context);
        init(context);
    }

    public WelcomeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public WelcomeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(21)
    public WelcomeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        if(!isInEditMode()) {
            Application.getComponent(context).inject(this);
        }
    }
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • you can use this() to make one constructor call another one, so you don't need to add init in all your constructors – Sibelius Seraphini Apr 08 '16 at 13:18
  • True, but I like to make sure it's called from all my constructors, just in case Android does something weird :) – EpicPandaForce Apr 08 '16 at 13:22
  • Isn't getting rid of the static methods one of the main points of dependency injection? But you are still calling a static method here. In activity and fragment I know how to avoid it but what about views? – kingston Nov 16 '16 at 14:20
  • 2
    You can also do `((CustomApplication)(context.getApplicationContext()).getComponent().inject(this);` if you want – EpicPandaForce Nov 16 '16 at 14:24
  • 1
    @kingston I guess the question truly is where you obtain your `Component` from. I've seen solutions like singleton enum, global LinkedHashMap, MortarScope, custom ContextWrapper, etc. – EpicPandaForce Nov 16 '16 at 14:32
  • @EpicPandaForce right that was my real question. Thank you for the clues – kingston Nov 16 '16 at 16:27
2

UPDATE: Since 2.10 version of Dagger this answer is invalid.

Custom view:

public class CustomView extends View {
    @Inject
    AnyObject anyObject;

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        MainActivity mainActivity = (MainActivity) getContext();
        mainActivity.getComponent().inject(CustomView.this);
    }
}

Activity:

ActivityComponent mComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mComponent = DaggerActivityComponent.builder()
                    .appComponent(getApp().getAppComponent())
                    .activityModule(new ActivityModule(MainActivity.this))
                    .build();
    mComponent.inject(MainActivity.this);
    ...
}

public ActivityComponent getComponent() {
    return mComponent;
}

Dagger2 component with Activity scope:

@ActivityScope
@Component(dependencies = AppComponent.class, modules = {ActivityModule.class})
public interface ActivityComponent extends AppComponent {

    void inject(MainActivity mainActivity);
    void inject(CustomView customView);
}

Scope:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}
Mussa
  • 1,463
  • 21
  • 25