2016-10-27 4 views
0

Wie ich vorher gelesen habe, kommt Dolch alle Konstruktoren emittieren und stellen sie selbst, so sollten wir fast keinen Konstruktor in unserem Geschäft haben, jetzt vorstellen, gibt es eine RecyclerView innerhalb einer Fragment und die Activity wird das Fragment Injektion, wie kann ich LayoutManager und Adapter und auch Presenter für Fragment injizieren (ohne sie innerhalb Aktivität Injektion und durch Argumente zu Fragment passieren)Android, Dolch 2 inject recyclerview in Fragment

die Probe arbeite ich an ist here, aber er verwendet Activity selbst als View nach MVP Muster, aber ich versuchezu verwenden 10 stattdessen.

This ist sein Activity (dass Recycler-Ansicht und Presenter hier injiziert werden).

Mein Code:

UserComponent die Subkomponente AppComponent

@UserScope 
@Subcomponent(modules = UserModule.class) 
public interface UserComponent { 
    RepoListComponent plus(RepoListModule repoListModule); 

    UserEntity getUserEntity(); 
} 

RepoListModule ist:

@Module 
public class RepoListModule { 
private RepoListContract.View view; 

public RepoListModule(RepoListContract.View view) { 
    this.view = view; 
} 

@Provides 
RepoListContract.View provideRepoListContractView(){ 
    return view; 
} 

@Provides 
LinearLayoutManager provideLayoutManager(Context context) { 
    return new LinearLayoutManager(context); 
} 

@Provides 
RepoListAdapter provideAdapter(RepoListContract.View view) { 
    return new RepoListAdapter(view); 
    } 
} 

RepoListComponent:

@Subcomponent(modules = RepoListModule.class) 
public interface RepoListComponent { 
    void inject(RepoListContract.View view); 
} 

RepoListActivity:

public class RepoListActivity extends BaseActivity { 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_repo_list); 
    RepoListFragment fragment = (RepoListFragment) getSupportFragmentManager() 
      .findFragmentById(R.id.fragment_container); 
    if (fragment == null) { 
     fragment = new RepoListFragment(); 
     ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), 
       fragment, R.id.fragment_container); 
    } 
    GApplication.get(getApplicationContext()) 
      .getUserComponent().plus(new RepoListModule(fragment)) 
      .inject(fragment); 
    } 
} 

RepoListFragment:

public class RepoListFragment extends Fragment implements RepoListContract.View { 
@BindView(R.id.rv_repo) RecyclerView rvRepo; 

@Inject RepoListContract.Presenter presenter; 
@Inject LinearLayoutManager layoutManager; 
@Inject RepoListAdapter adapter; 

public static RepoListFragment newInstance() { 
    return new RepoListFragment(); 
} 

@Nullable 
@Override 
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 
         @Nullable Bundle savedInstanceState) { 
    View v = inflater.inflate(R.layout.fragment_repo_list, container, false); 
    ButterKnife.bind(this, v); 
    initRvRepo(); 
    return v; 
} 
private void initRvRepo() { 
    rvRepo.setLayoutManager(layoutManager); // null 
    rvRepo.setAdapter(adapter); //null 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    presenter.subscribe(); //NullPointerException 
    } 
} 

Antwort

2

Ich bin mir nicht sicher, ob ich voll und ganz diese Worte verstehen: "... so sollten wir nicht in unserem Geschäft fast jeden Konstruktor ...". Normalerweise gehe ich durch - Als Faustregel, jedes Mal, wenn Sie den Operator new sehen, ist das ein Hinweis darauf, dass Sie eine enge Abhängigkeit von der Klasse haben, die instanziiert wird, und Sie sollten versuchen, dies zu entfernen. Das heißt, hier ist, wie ich Ihre Aufgabe angehen würde (Es gibt definitiv viel mehr Lösungen, das ist nur ein Ansatz).

Beginnen wir mit der einfachsten Sache. Lassen Sie uns Ihr Fragment erstellen:

@Module 
public class RepositoriesListModule { 
    private final RepositoriesListView view; 

    public RepositoriesListModule(RepositoriesListView view) { 
     this.view = view; 
    } 

    @Provides 
    public RepositoriesListView providesView() { 
     return view; 
    } 

    @Provides 
    public RecyclerView.LayoutManager providesLayoutManager(Context context) { 
    return new LinearLayoutManager(context); 
    } 

    @Provides 
    public RecyclerView.Adapter providesAdapter(SomeAdapterImpl adapter) { 
    return adapter; 
    } 

    @Provides 
    public RepositoriesListPresenter providesPresenter(SomePresenterImpl presenter) { 
     return presenter; 
    } 
} 

Das erste, was ist zu bemerken, dass es die Sicht in den Konstruktor erwartet:

public RepositoriesListFragment extends Fragment implements RepositoriesListView { 
    @Inject RecyclerView.LayoutManager layoutManager; 
    @Inject RecyclerView.Adapter adapter; 
    @Inject RepositoriesListPresenter presenter; 

    public static RepositoriesListFragment newInstance() { 
     return new RepositoriesListFragment(); 
    } 
    // ... 
} 

Jetzt ist dieses das Modul für die Abhängigkeiten sein könnte. Dies ist der Fall, da der Präsentator normalerweise die Ansicht benötigt. Daher würde SomePresenterImpl diese Ansicht im Konstruktor erwarten.

Zweitens nimmt dieses Modul an, dass das Context auch irgendwo zur Verfügung gestellt wird. Höchstwahrscheinlich durch ein anderes Modul, von dem die Komponente abhängen würde.

Hier ist die Komponente:

@Component(modules = { RepositoriesListModule.class, ... }) 
public interface RepositoriesListComponent { 
    void inject(RepositoriesListFragment fragment); 
} 

(Wie gesagt, bevor diese Komponente möglicherweise andere Module benötigen oder sogar auf anderen Komponenten abhängen).

Das letzte, was Sie tun müssen, ist das Fragment zu injizieren. Wie Sie gesagt haben, gibt es eine Aktivität, die das Fragment erzeugt und injiziert. So könnte das aussehen:

public class MainActivity extends Activity { 
    public void onCreate(Bundle savedInstanceState) { 
     // ... 
     RepositoriesListFragment fragment = RepositoriesListFragment.newInstance(); 

     DaggerRepositoriesListComponent.builder() 
     .repositoriesListModule(new RepositoriesListModule(fragment)) 
     .inject(fragment); 
     // ... 
    } 

Wie ich schon sagte, das ist nicht die einzige Möglichkeit, es zu tun. Dies hat ein Problem, wo Sie die Komponente mehrmals erstellen. Wenn Sie die Komponente für die Erstellung verwenden, müssen Sie sie korrekt verarbeiten. Sie können es nicht jedes Mal erstellen, sonst rendern Sie den Bereich unbrauchbar. Der schwierige Teil besteht darin, dass das Modul jedes Mal, wenn Sie die Ansicht injizieren, die korrekte Ansicht benötigt. Sie müssen sicherstellen, dass das Modul auf diese Ansicht verweist.

Hoffe, das hilft.

EDIT:

Nach einem Blick auf den Code, die ich denke, das Problem mit der Tatsache zusammenhängt, dass Sie das Fragment vor der Injektion sind das Hinzufügen, im Wesentlichen so damit das Problem lösen könnte:

public class RepoListActivity extends BaseActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_repo_list); 
    RepoListFragment fragment = (RepoListFragment) getSupportFragmentManager() 
     .findFragmentById(R.id.fragment_container); 
    if (fragment == null) { 
     fragment = new RepoListFragment(); 
     GApplication.get(getApplicationContext()) 
     .getUserComponent().plus(new RepoListModule(fragment)) 
     .inject(fragment); 
     ActivityUtils.addFragmentToActivity(
      getSupportFragmentManager(), 
      fragment, R.id.fragment_container); 
    } 
    } 
} 

Warum passiert es? Mit Blick auf Ihre RepoListFragment greifen Sie auf die injizierten Variablen in beiden onResume und onCreateView. Diese sind Teil des Lebenszyklus des Fragments, der ausgeführt wird, sobald er der Aktivität hinzugefügt wurde (angenommen, ActivityUtils.addFragmentToActivity fügt das Fragment tatsächlich im Layout hinzu oder ersetzt es).

Wie Sie sehen, bedeutet dies, dass Sie bereits auf die Mitglieder zugreifen können, bevor Sie sie injizieren können. Sie sollten also das Fragment injizieren, bevor Sie es hinzufügen.

+0

Willst du sagen, dass ich zum Beispiel Adapter haben kann? Ich habe es aber nicht funktioniert – AlirezaXX

+0

Was meinen Sie können Sie auf den Adapter zugreifen? Du erstellst den Adapter selbst ... – Fred

+0

Danke für deine vollständige Antwort, aber da 'RepositoriesListComponent' Unterkomponente von UserComponent ist, die die Subkomponente von AppComponent ist, kann es passieren, jetzt sind mein Adapter und mein Layoutmanager null! -> Aktivität: => 'GApplication.get (getApplicationContext()) .getUserComponent(). Plus (neues RepoListModule (Fragment)) .inject (fragment);' – AlirezaXX