Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mortar and Flow libraries vs. ViewPager

I'm trying to adapt Mortar&Flow in my app and faced with an issue, that I can't make PageAdapter works with Screens, instead of Fragments.

Anyone managed to get it right?

I didn't succeed but, probably someone can guide me from this point:

The initial Dagger Registration:

@Module(
    injects = {
            MainActivity.class,
    },
    library = true,
    complete = false
)
public class DaggerConfig {
    @SuppressWarnings("unused")
    @Provides @Singleton Gson provideGson() {
        return new GsonBuilder().create();
    }
}

MainScreen, whose View is hosting ViewPager:

@Layout(R.layout.screen_main) @WithModule(MainScreen.Module.class)
public class MainScreen extends Path {
    @dagger.Module(injects = MainView.class, addsTo = DaggerConfig.class)
    public static class Module {}

    @Singleton
    public static class Presenter extends ViewPresenter<MainView> {
        @Inject
        public Presenter() {}
    }
}

MainView:

...........
@Inject
MainScreen.Presenter presenter;
...........
@Override protected void onFinishInflate() {
    super.onFinishInflate();
    ButterKnife.inject(this);

    final Path[] screens = {
            new SubScreen("1"),
            new SubScreen("2"),
            new SubScreen("3"),
    };

    CustomPagerAdapter customPagerAdapter = new CustomPagerAdapter(getContext(), screens );
    customPagerAdapter .setAdapter(firstRunPagerAdapter);
}
.....

Now, the main part, SubScreen (3 similar screens, that differs only by the parameters we are passing into them => they should adjust views according these parameters)

@Layout(R.layout.screen_subscreen) @WithModule(SubScreen.Module.class)
public class SubScreen extends Path {
    private final String title;
    public SubScreen(String titleParam) {
        title = titleParam;
    }

    @dagger.Module(injects = SubView.class, addsTo = DaggerConfig.class)
    public class Module {
        @Provides
        SubViewMetadata provideSubViewMetadata() {
            return new SubViewMetadata(backgroundColor, title);
        }
    }

    @Singleton
    public static class Presenter extends ViewPresenter<SubView> {

        private String title;

        @Inject
        public Presenter(String title) {
            this.title= title;
        }

        @Override
        protected void onLoad(Bundle savedInstanceState) {
            super.onLoad(savedInstanceState);
            if (!hasView()) {
                return;
            }

            getView().setTitle(subViewMetadata.title);
        }
    }
}

and it's view public class SubView extends FrameLayout {

    @InjectView(R.id.subViewTitleTextView)
    TextView subViewTitleTextView;

    @Inject
    SubScreen.Presenter presenter;

    public SubView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ObjectGraphService.inject(context, this);
    }

    public void setTitle(String title) {
        subViewTitleTextView.setText(title);
    }

    @Override protected void onAttachedToWindow() {....}

    @Override protected void onDetachedFromWindow() {....}
......
}

Custom Pager adapter:

public class CustomPagerAdapter extends PagerAdapter {
    private final Context context;
    private final Path[] screens;

    public CustomPagerAdapter(Context context, Path[] screens) {
        this.context = context;
        this.screens = screens;
    }

    @Override
    public int getCount() {
        return (screens == null)? 0 : screens.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object o) {
        return view.equals(o);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Path screen = screens[position];
        MortarScope originalScope = MortarScope.getScope(context);
        MortarScope newChildScope =  originalScope.buildChild().build("tutorialpage" + position);
        Context childContext = newChildScope.createContext(context);
        View newChild = Layouts.createView(childContext, screen);
        container.addView(newChild);
        return newChild;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        View view = ((View) object);
        container.removeView(view);
        MortarScope.getScope(view.getContext()).destroy();
    }
}

The problem statement: it's crashing, as SubView class hasn't been added into list of Injections at the "Layouts.createView(childContext, screen);" moment in the Adapter, and I can't add it by default, because I want to have a @provider of data from SubScreen to SubScreen.Presenter. (I'm using local variable.

If I add SubView.class into list of injections and convert local Screen's variables into static, then I'll have 3 identical pages inside the ViewPager (which is logical, as every next call of the constructor - overrides old static variables).

Any help/ideas? Thanks for your help, Konstantin

like image 767
Konstantin Loginov Avatar asked May 26 '15 14:05

Konstantin Loginov


1 Answers

Ok, I figured out.

First of all, adding SubView into list of globally injected classes Then modifying SubScreen class:

@Layout(R.layout.screen_subscreen)
public class SubScreen extends Path {
    private static String titleStatic; // Introducing static variable
    private final String title;
    public SubScreen(String titleParam) {
        title = titleParam;
    }

    public void refreshPresenter() {
        titleStatic = title;
    }

    @Singleton
    public static class Presenter extends ViewPresenter<SubView> {

        private String title;

        @Inject
        public Presenter() {
        }

        @Override
        protected void onLoad(Bundle savedInstanceState) {
            super.onLoad(savedInstanceState);
            if (!hasView()) {
                return;
            }

            getView().setTitle(titleStatic);
        }
    }
}

and then in Custom adapter do this changes:

public class CustomPagerAdapter extends PagerAdapter {
    private final Context context;
    private final SubScreen[] screens;

    public CustomPagerAdapter(Context context, SubScreen[] screens) {
        this.context = context;
        this.screens = screens;
    }
    ......
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        SubScreen screen = screens[position];
        MortarScope originalScope = MortarScope.getScope(context);
        MortarScope newChildScope =  originalScope.buildChild().build("tutorialpage" + position);
        Context childContext = newChildScope.createContext(context);

        screen.refreshPresenter(); // updating the static var with local one!

        View newChild = Layouts.createView(childContext, screen);
        container.addView(newChild);
        return newChild;
    }
    ....
}

I.e. the solution is to keep the local AND static variables in the Screen, if the same screen is going to be reused. And when we inflate the view it - just setting the right value to the static one (that would be used in the Presenter).

I am not sure, that it is the best possible solution, but it works. It would be nice to hear, if it can be improved.

like image 168
Konstantin Loginov Avatar answered Sep 23 '22 00:09

Konstantin Loginov