1

Sincere apologies for no code, I'm leaving for a wedding and it was either post code or explain my situation.

I've searched Stack and see many posts for Recycler Adapter to Fragment (that created the adapter) interfaces.

A minor few for Fragment(created the adapter) to Adapter posts but they are not nearly as clear.

My situation is this:

On Main Activity, when App is running:

1) Fragment Lyrics (created the REcycler ADapter that is set into a Lyric Recycler View)

2) Fragment Microphone ( speech recognition microphone functionality and XML icon).

What I want to happen is:

user activates Microphone and speaks, that resulting data is passed to the ADAPTER java file and activates a method on the ADAPTER, causing a visual change to RecyclerView Viewholder on the screen.

Yes, I know this is probably bad architecture. It's for a school project, I'm learning, and I've run out of time.

* What I can do so far *

I have activated the pre-made OnClick listerner for the Adapter (when a user clicks on a View) and OnScroll for the RecyclerView (user scrolls, it fires a method in the Adapter that causes the current View to change).

I have made interface for Passing Speech data from Microphone Fragment, through the Main Activity, to the Lyrics Fragment.

On Main, I simply create an instance of the Lyrics Fragment, then call a custom method on Lyrics Fragment that takes the speech data. Something like this.

LyricsFragment.TakeSpeechData(speech data);

* What my plans was...*

When the speech data arrives on Lyrics Fragment, I thought I could just write something like:

MyRecyclerAdapter.SomeMethodOnAdapter (speech data);

I would be home free at this point.

It doesn't work

No go. I get a null pointer exception here. The MyRecyclerAdapter part of the method call is null. I've looked that up and not sure how to fix it.

I'm assuming I'm referencing the original Adapter that was created when the Fragment layed down the RecyclerView and set everything. It's the same global variable for the Adapter on Fragment Lyrics and I'm assuming it "lives on".

I'm sure I'm missing on fundamental Java principles but i don't know what.

I've spent hours and hours on this trying , reading, researching. I'm totally stuck. Please help.

EDIT: Here is my code for VerseFragment (I'm referring to it as "Lyrics" Fragment in my post). Note this Fragment is loaded, created, and functional with recyclerView on screen. Before the user uses the micrphone fragment, which is also on screen, this has already been created.

public class VersesList extends Fragment {
    @BindView(R.id.versesRecycleView) RecyclerView versesRecycleView;
    @BindView(R.id.songNameTextView) TextView songName;
    @BindView(R.id.artistTextView)TextView artistName;
    private SharedPreferences mSharedPreferences;
    LinearLayoutManager llm;
    List verseList;
    List finalModVerseList;
    public VerseAdapter verseAdapter;
    // temporary
    private SharedPreferences.Editor editor;
    public VersesList() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment

        View view = inflater.inflate(R.layout.fragment_verses_list, container, false);
        ButterKnife.bind(this, view);
        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
        editor=mSharedPreferences.edit();
        displayArtistAndSongName();
        lyricsToVerseList();
        setVersesIntoRecyclerView();
        setVersesScrollListener();
        //temp
        storeAllVerseLevels();
        return view;
    }

    public static VersesList newInstance(String lyrics){
        VersesList versesListFragment = new VersesList();
        Bundle args = new Bundle();
        args.putString("lyrics", lyrics);
        versesListFragment.setArguments(args);
        return versesListFragment;

    }

    public void lyricsToVerseList(){
        String lyrics = getArguments().getString("lyrics", "");
        verseList = new ArrayList<String>();
        finalModVerseList = new ArrayList<String>();
        verseList= Arrays.asList(lyrics.split("\n"));
        int endOfFinalList=verseList.indexOf("...");
        for (int i = 0; i < endOfFinalList; i++) {
           if(!verseList.get(i).toString().equals("")){
               String addThisVerse = verseList.get(i).toString();
               //check on length of verse, if too short add next, check again
               int numberOfWords = addThisVerse.split(" ").length;
               while (numberOfWords < 10 && i < endOfFinalList) {
                   i++;
                   addThisVerse += " " + verseList.get(i).toString();
                   numberOfWords = addThisVerse.split(" ").length;
               }
               finalModVerseList.add(addThisVerse);
           }
        }
    }

    public void displayArtistAndSongName(){
        String song = '"'+mSharedPreferences.getString(SONG_NAME, null)+'"';
        String artist = "by "+mSharedPreferences.getString(ARTIST_NAME, null);
        songName.setText(song);
        artistName.setText(artist);
    }

    public void setVersesIntoRecyclerView(){
        verseAdapter = new VerseAdapter(finalModVerseList, (MainActivity)getActivity(), versesRecycleView);
        versesRecycleView.setAdapter(verseAdapter);
        llm = new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL, false);
        versesRecycleView.setLayoutManager(llm);
        PagerSnapHelper helper = new PagerSnapHelper();
        helper.attachToRecyclerView(versesRecycleView);
    }

    private void storeLevel(int indexNumber) {
        editor.putInt(String.valueOf(indexNumber), 1).apply();
    }

    private void storeAllVerseLevels(){
        for (int i=0; i< finalModVerseList.size();i++){
            storeLevel(i);
        }
        for (int j=0; j< finalModVerseList.size();j++){
            String level = String.valueOf(mSharedPreferences.getInt(String.valueOf(j), -1));
            Log.d("In Shared Preferences  ", "Verse "+j+" Level "+level);
        }
    }

    public void checkSpeech(String text){
        List<String> temp = new ArrayList<>();
        temp.add("test");

        VerseAdapter adapter = new VerseAdapter(temp, (MainActivity)getActivity(), versesRecycleView);
        try {
            adapter.resetVerse();
        }catch (NullPointerException e){
            Log.d("Null", e.toString());
        }
    }

    public void setVersesScrollListener(){
        versesRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if (newState == 0) {
                    verseAdapter.resetVerse();
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });
    }
}

When you are calling the method from your adapter, is MyRecyclerAdapter an instance or the class? To call someMethodOnAdpater(speechData), you must use an instance . Xia is using an instance.

If you need to call an adapter method from within the fragment in which it was created, you can store it in a variable like this.

MyRecyclerAdapter adapter;

@Override
public View onCreateView(...) {
    ...
    adapter = new MyRecyclerAdapter();
    myRecyclerView.setAdapter(adapter);
    ...
}

public void takeSpeechData(String data) {
    adapter.someMethodAdapter(data);
}

Edit:

I'm not sure why the same adapter used by your recyclerview is null after being set. Calling an adapter from is definitely possible (I tested a basic example). The code in my example doesn't differ from what you said you had previously, though. I have upvoted your question for visibility.

**Edit: Add Mic Fragment, it has the interface **

package com.blueoxgym.javainthedark.Fragments;


/**
 * A simple {@link Fragment} subclass.
 */
public class MicFragment extends Fragment implements  View.OnClickListener {
    @BindView(R.id.progressBarMic)
    ProgressBar micLevels;
    @BindView(R.id.btn_mic)
    ImageButton btnMicrophone;
    private SpeechRecognizer speech = null;
    private Intent recognizerIntent;
    public final static String TAG = "In speech mode";
    public FragmentManager fragmentManager;
    private SharedPreferences mSharedPreferences;
    private SharedPreferences.Editor mEditor;
    private String trackName;
    private String artistName;
    private CallMainLoadVerseFragment loadVerseFragment;
    private CheckSpeech checkSpeechOnVerse;


    public MicFragment() {
        // Required empty public constructor
    }

    public static MicFragment newInstance (){
        MicFragment micFragment = new MicFragment();
        return micFragment;
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_mic, container, false);
        ButterKnife.bind(this, view);
        this.loadVerseFragment = (CallMainLoadVerseFragment) getActivity();
        this.checkSpeechOnVerse = (CheckSpeech) getActivity();
        btnMicrophone.setOnClickListener(this);
        fragmentManager = getFragmentManager();
        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
        mEditor = mSharedPreferences.edit();
        return view;
    }

    @Override
    public void onClick(View v) {
        if (v == btnMicrophone) {
            startSpeechToText();
        }

    }

    class listener implements RecognitionListener {
       ...


    @Override
        public void onResults(Bundle results) {
            String str = new String();
            Log.d(TAG, "onResults " + results);
            ArrayList<String> data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
            String text = data.get(0).toLowerCase().replace("by","");
            Fragment currentFragment = fragmentManager.findFragmentById(R.id.content_frame);
            if (currentFragment.toString().contains("LyricSearch")){
                searchForSong(text);
            } else if (currentFragment.toString().contains("VersesList")){
 -----------> Here it is called  checkSpeechOnVerse.checkingSpeech(text);
            }

        }

    }

    public void startSpeechToText(){
        btnMicrophone.setBackgroundResource(R.drawable.circle_green);
        speech=SpeechRecognizer.createSpeechRecognizer(getContext());
        speech.setRecognitionListener(new listener());
        recognizerIntent= new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en-US");
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getActivity().getPackageName());
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
        speech.startListening(recognizerIntent);
    }

 ...
...

    public interface CheckSpeech {
        void checkingSpeech (String text);
    }
}

MainActivity, implements CheckSpeech Interface

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener, MicFragment.CallMainLoadVerseFragment, MicFragment.CheckSpeech {
....
...
@Override
    public void checkingSpeech(String text) {
       VersesList versesList = new VersesList();
 --------> Now, I'm pass data from Main to VersesList Fragment(it has the original Adapter)     
     versesList.checkSpeech(text);
    }

VersesList Fragment, where I try to call Adapter

public class VersesList extends Fragment {
  ....
        private VerseAdapter verseAdapter;
setVersesIntoRecyclerView();
....
<---ADAPTER IS MADE AND SET HERE----.
public void setVersesIntoRecyclerView(){
        verseAdapter = new VerseAdapter(finalModVerseList, (MainActivity)getActivity(), versesRecycleView);
        versesRecycleView.setAdapter(verseAdapter);
        llm = new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL, false);
        versesRecycleView.setLayoutManager(llm);
        PagerSnapHelper helper = new PagerSnapHelper();
        helper.attachToRecyclerView(versesRecycleView);
    }


public void checkSpeech(String text){
-------> NPE NPE 
versesAdapter.someMethodOnAdapter(text);

    }

    public void setVersesScrollListener(){
        versesRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if (newState == 0) {
BUT THIS WORKS!!! No NPE. --------> verseAdapter.resetVerse();

                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
//                VerseAdapter.VerseViewHolder view = versesRecycleView.findViewHolderForAdapterPosition(llm.findFirstVisibleItemPosition());
            }
        });
    }
Xia
  • 43
  • 2
  • 7
  • In `checkSpeech()`, why are you creating a new adapter? – Adam Piziak Jul 23 '17 at 20:59
  • 1
    In checkingSpeech (), you instantiate a new fragment, but do not replace the original fragment. That fragment is created but is not added to the container. If you intend to replace the original fragment, you must create another transaction and replace the container with `transaction.replace(containerID, newFragment)` – Adam Piziak Jul 27 '17 at 12:48
  • If you want to use the same fragment, you can access it like you did elsewhere with `VersesList fragment = (VersesList) FragmentManager.findFragmentById(containerId).` – Adam Piziak Jul 27 '17 at 12:55
  • @AdamPiziak I wanted to say thank you so much for helping a newbie and doing it without snark. Your last comment was the key and its improved my Java understanding as a whole. Now I see how important it is when referring to methods in other classes, that I'm referring to classes that have been already instantiated. I was using a new VerseList, which had no VerseAdapter, which led to NPE. I believe I upvoted your answer. Let me know if I missed it – Xia Jul 30 '17 at 07:57
  • My pleasure! I'm glad it worked out. I edited my answer to make it more clear for anyone else that has the same question. :) – Adam Piziak Jul 30 '17 at 16:53

1 Answers1

0

If you need to call an adapter method from within the fragment in which it was created, you can store it in a variable within that fragment.

MyRecyclerAdapter adapter;

@Override
public View onCreateView(...) {
    ...
    adapter = new MyRecyclerAdapter();
    myRecyclerView.setAdapter(adapter);
    ...
}

public void takeSpeechData(String data) {
    adapter.someMethodAdapter(data);
}

Then you can call that method directly from another fragment. (link to accessing fragments)

VersesList versesList = (VersesList) getActivity().getSupportFragmentManager.findFragmentById(containerId);
versesList.takeSpeechData("data");

gif of example

Adam Piziak
  • 461
  • 5
  • 10
  • Thanks Adam for your response. I'm calling an instance. It's been set up exactly as you have it. The chain of events is like this right now. Mic Fragment, speech happens (at this time, Verse Fragment aalready exists on screen and the recycler view is active and functional), speech data is then passed through interface to MainActivity, where the interface method triggers a call to a method on Verse Fragment. Verse now has the data. On Verse Fragment, my adapter was already instantiated, set to variable, set to recyclerview view BEFORE the user used the microphone. – Xia Jul 23 '17 at 20:26
  • I've posted the code to the Fragment that has the adapter. – Xia Jul 23 '17 at 20:33
  • In `checkSpeech()`, why are you creating a new adapter? – Adam Piziak Jul 23 '17 at 20:45
  • When I didn't have it, and just used "verseAdapter" (global variable for already set adapte), I got an NPE "trying to invoke method on null object" - verseAdapter being null. So I thought maybe I need to create one. So NPE is gone but its a Different adapter instance than the one the current recyclerView is managed by. Being Different, I can call the Adapter method from my fragment...but it doesn't affect the current recycler view as it is a complete separate instance. – Xia Jul 24 '17 at 01:44
  • Note: Inside The verseSetScrollListner calls "verseAdapter".someMethod when scroll is detected and that has NO NPE error and it works great. I just don't understand what that is... – Xia Jul 24 '17 at 01:50
  • Can you show the code that calls `checkSpeech()` and the previous `checkSpeech()` which uses the same adapter? – Adam Piziak Jul 24 '17 at 16:06
  • I think might have updated your comment and my original post. But it now shows the path. I left out some stuff but hopefully its clear enough. Thank you for being interested in this problem! – Xia Jul 26 '17 at 00:44