0

I have two fragments FGames and FGamesDetail. which display the list of Games and when clicked should populate the FGamesDetail fragment. I am using MVP pattern.

I am trying to implement MultiPane layout for tablet to have list and detail view next to each other.

I am getting a null pointer exception at 'mListener.onGameSelected(gameEntity);' in FGames. I know I have not initialised it at this place but should I be initialising it every method I go through in MVP pattern.

GamesAdapter - RecyclerView Adapter.

      @OnClick(R.id.row_container)
            void rowClick(){
                GamesPresenter gamesPresenter = new GamesPresenterImpl();
                gamesPresenter.showGameDetail(data.get(getLayoutPosition()));
                Toast.makeText(context, "itemClicked " + data.get(getLayoutPosition()), Toast.LENGTH_SHORT).show();
            }

GamesPresenter - Interface

    public interface GamesPresenter {
        void initUi();
        void showGameDetail(GameEntity gameEntity);

    }

GamesPresenterImpl -

    public class GamesPresenterImpl implements GamesPresenter {

    GamesView gamesView;
    private ApiInterface apiInterface;
    /**
     * Collects all subscriptions to unsubscribe later
     */
    @NonNull
    private CompositeDisposable mCompositeDisposable = new CompositeDisposable();

    public GamesPresenterImpl() {}

    public GamesPresenterImpl(GamesView gamesView) {
        this.gamesView = gamesView;
    }

    @Override
    public void initUi() {
        getGamesData();
    }

    @Override
    public void showGameDetail(GameEntity gameEntity) {
       //gamesView was null so initialised here
        GamesView gamesView = new FGames();
        gamesView.onListItemClick(gameEntity);

      }    
    }

GamesView - interface

    public interface GamesView {

        /**
         * Initialise the recycler view to list Games data
         * @param gameEntities
         */
        void initRecyclerView(List<GameEntity> gameEntities);

        void showToast(String message);

        void onListItemClick(GameEntity gameEntity);
    }
#

FGames - has all the implementation for the Fragment

    public class FGames extends Fragment implements GamesView {

        @BindView(R.id.rv_games)
        RecyclerView rvGames;
        private GamesAdapter gamesAdapter;
        private GamesPresenterImpl presenter;
        OnGameSelectedListener mListener;
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.games_layout, container, false);
            ButterKnife.bind(this, view);
            presenter = new GamesPresenterImpl(this);
            return view;
        }

        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            try {
                mListener = (OnGameSelectedListener) context;
            } catch (ClassCastException e) {
                throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
            }
        }

        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            presenter.initUi();
        }

        @Override
        public void initRecyclerView(List<GameEntity> gameEntities) {
            gamesAdapter = new GamesAdapter(getActivity(), gameEntities);
            rvGames.setAdapter(gamesAdapter);
            rvGames.setLayoutManager(new LinearLayoutManager(getActivity()));
        }

        @Override
        public void showToast(String message) {
            Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onListItemClick(GameEntity gameEntity) {
          //Here is where the NUll pointer exception is
            mListener.onGameSelected(gameEntity);
        }

        public interface OnGameSelectedListener{
            public void onGameSelected(GameEntity gameEntity);
        }
    }

MainActivity - which displays the performs the game selected operation to update the UI if detail fragment is available. I followed Android documentation to do this.

public class MainActivity extends AppCompatActivity implements FGames.OnGameSelectedListener {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //FGames fGames = new FGames();
        //getSupportFragmentManager().beginTransaction().add(R.id.games_container, fGames).commit();
    }

    @Override
    public void onGameSelected(GameEntity gameEntity) {
        FGameDetail gameDetailFrag = (FGameDetail) getSupportFragmentManager()
                .findFragmentById(R.id.fragment_fGameDetail);
        if (gameDetailFrag == null) {
            // DisplayFragment (Fragment B) is not in the layout (handset layout),
        } else {
            // DisplayFragment (Fragment B) is in the layout (tablet layout),
            // so tell the fragment to update
            gameDetailFrag.updateContent(gameEntity);
        }
    }
}

ErrorLog

Process: com.example.rao.igttest, PID: 21481
                                                                         java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.rao.igttest.Games.View.FGames$OnGameSelectedListener.onGameSelected(com.example.rao.igttest.Games.Entity.GameEntity)' on a null object reference
                                                                             at com.example.rao.igttest.Games.View.FGames.onListItemClick(FGames.java:73)
                                                                             at com.example.rao.igttest.Games.Presenter.GamesPresenterImpl.showGameDetail(GamesPresenterImpl.java:53)
                                                                             at com.example.rao.igttest.Games.View.GamesAdapter$GamesViewHolder.rowClick(GamesAdapter.java:72)
                                                                             at com.example.rao.igttest.Games.View.GamesAdapter$GamesViewHolder_ViewBinding$1.doClick(GamesAdapter$GamesViewHolder_ViewBinding.java:33)
                                                                             at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
                                                                             at android.view.View.performClick(View.java:5637)
                                                                             at android.view.View$PerformClick.run(View.java:22429)
                                                                             at android.os.Handler.handleCallback(Handler.java:751)
                                                                             at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                             at android.os.Looper.loop(Looper.java:154)
                                                                             at android.app.ActivityThread.main(ActivityThread.java:6119)
                                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                                             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
BRDroid
  • 3,920
  • 8
  • 65
  • 143
  • You should use the Pattern Framgnet Interactor from Google, so you have acess to the activity and get the other fragment by method – Marcos Vasconcelos Nov 23 '17 at 18:15
  • I tried to follow this https://developer.android.com/training/basics/fragments/communicating.html but mixing it with MVP, i think I am not doing it right. – BRDroid Nov 23 '17 at 18:18
  • In the MVP archtecture you must send the event to the Activity and the Activity dispatch to the other fragment – Marcos Vasconcelos Nov 23 '17 at 18:19
  • That is what I am trying to do moving from recycler adapter, to Presenter to Fragment then to Activity. but in @Override public void onListItemClick(GameEntity gameEntity) { //Here is where the NUll pointer exception is mListener.onGameSelected(gameEntity); } in FGames, should I be initialising mListener here too? – BRDroid Nov 23 '17 at 18:22
  • Probably not, mListener must be set before using it – Marcos Vasconcelos Nov 23 '17 at 18:24
  • mListsner is set in OnAttach of FGames, @Override public void onAttach(Context context) { super.onAttach(context); try { mListener = (OnGameSelectedListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener"); } } But I am coming from presenter layer back to FGames. not sure how to do it – BRDroid Nov 23 '17 at 18:26
  • The mListener will be in the Fragment, on attach you set it to the class, then in the click on the Fragment you call the method – Marcos Vasconcelos Nov 23 '17 at 18:28
  • All that is done in FGames as far I understand what you are saying. could you take a look at FGames in my question please. Have I done it the way you are saying. – BRDroid Nov 23 '17 at 18:31
  • Yes this is, but override onAttach(Activity ctx) also, instead of the Context one, I'm not sure which one is always called since the second is new – Marcos Vasconcelos Nov 23 '17 at 18:33
  • @Override public void onAttach(Activity activity) { super.onAttach(activity); } is deprecated inv4.app.fragment – BRDroid Nov 23 '17 at 18:47

0 Answers0