Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ViewPager's PagerAdapter & Mortar's Screens #162

Open
ksloginov opened this issue May 27, 2015 · 4 comments
Open

ViewPager's PagerAdapter & Mortar's Screens #162

ksloginov opened this issue May 27, 2015 · 4 comments

Comments

@ksloginov
Copy link

Hi,

This topic has been discussed a while ago and even nice snippet has been posted (https://gist.github.com/kboyarshinov/d30c354541f95ecaeaac), but since then, the Flow and Mortar have changed and I'm a bit confused.

It used to be something like this:

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

But now, Layouts is no longer part of Flow and requireChild is no longer part of MortarScope.

Does anyone succeed in adapting it for the latest libs versions?

Thanks a lot(all square libraries are so awesome!) & best wishes!

@ksloginov
Copy link
Author

Ok, 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" + (new Random()).nextInt(999999));
        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

@ksloginov
Copy link
Author

Ok, apparently I did something.

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 idea 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).

It works, though I'm not sure if it's a right way to do it, so would be very happy, if someone can approve this approach or propose a better solution.

@lukaspili
Copy link

In my opinion, your logic of static variable and refreshPresenter() looks wrong.
See here for an up to date example of view pager with Mortar and Flow: https://github.com/lukaspili/Mortar-Flow-Dagger2-demo
In particular, look at the following classes: SlidesScreen, SlidePageScreen, SlidePagerAdapter.

And in the future, you should use stack overflow for such topic.

@vsumtsov-gh
Copy link

At this gist, you can find above solution but with save state functionality.
https://gist.github.com/VladSumtsov/2128e0cc0f7c2131c0f971ee26d36ce5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants