Search code examples
javaandroidandroid-recyclerviewlocalbroadcastmanager

How can I turn off localbroadcast listener in recyclerview adapter? Of is there a better way to communicate with an activity called from it?


I'm new to SO and fairly new to coding, so please accept my apologies in advance if I break rules or expectations here.

I have an unusual setup involving two recyclerViews, which I'll explain here and also paste a simplified version of the code below (as there is so much not relevant to this question).

In what I'll call verticalRecyclerViewActivity, a verticalRecyclerViewAdapter is called, with data it fetches from Firebase and loads into arrayLists.

If the user clicks on an item in the vertical recyclerview, a new dialog fragment which I'll call horizontalRecyclerViewDialogFragment is inflated, and that loads what I'll call horizontalRecyclerView (which has similar items to the vertical one, in more detail, with options to click on them to review them).

If the user clicks on an item in the horizontalRecyclerView, a new activity which I'll call reviewItem is started (through an Intent). When the user submits their review, it finishes and returns (through the backstack) to the horizontal RecyclerView. That can also happen if they press the back button without actually submitting a review. That all works fine, but I need the horizontalRecyclerView to show that they have (or haven't) reviewed the item and state the score they gave it in a review.

Calling notifyDataSetChanged won't work for this because of how information comes through two recyclerViews and Firebase calls (or, at least, it would be very inefficient).

I've tried using startActivityForResult (I know it's deprecated, but if I could get that to work I could try using the newer equivalent which I don't yet understand) but the problem is that the result is returned to the original (VerticalRecylcerView) activity, which is two recyclerView adapters and one fragment beneath what needs to be updated, and I don't know how to pass that data to the horizontal Recyclerview.

I've also tried using interfaces but was unable to pass it through the Intent (tried using Parcelable and Serializable, but it seems neither can work in this situation?).

Since the review is updated on Firebase, I could have the horizontal Recyclerview listen for a change, but that seems very inefficient?

So I've found a solution using localBroadcast (which I know is also deprecated). The Intent (with the review score) is transmitted when it is reviewed and received in the horizontal recyclerView adapter. But when and how should I unregister the adapter? Ideally the receiver would be turned on when the user goes to the Review activity and turned off once the user returns from that activity and the (horizontal) recyclerView holder is updated, whether the review is successfully submitted or whether the user just presses the back button and never submits a review.

My question is similar to this one: How to unregister and register BroadcastReceiver from another class?

That is noted as a duplicate of this one: How to unregister and register BroadcastReceiver from another class?

There's a lot in those questions I don't understand, but the important difference I think between their and my cases is that I would just like the receiver to know when a review is submitted, and ideally be unregistered then, or possibly when the viewHolder is recycled, which I tried but also didn't work since it's not connected to the viewHolder (should it be?).

Any help would be much appreciated, thank you!

public class verticalRecyclerViewActivity extends AppCompatActivity {
            
// Loads an XML file and assembles an array from Firebase. 
            
mVerticalRecyclerView = findViewById(R.id.verticalRecyclerView);
            
verticalRecyclerViewAdaptor mVerticalRecyclerViewAdaptor  = new verticalRecyclerViewAdaptor (this); // also pass other information it needs
        
            
mVerticalRecyclerView .setAdapter(mVerticalRecyclerViewAdaptor);

}
        
        
public class verticalRecyclerViewAdaptor extends RecyclerView.Adapter<verticalRecyclerViewAdaptor.singleHolder> {
        
// Usual recyclerView content
        
holder.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

horizontalRecyclerViewFragment  mHorizontalRecyclerViewFragment = new horizontalRecyclerViewFragment();  

// lots of arguments passed it needs. 
        
FragmentManager fragmentManager = ((FragmentActivity) view.getContext()).getSupportFragmentManager();
        
mHorizontalRecyclerViewFragment.show(fragmentManager, null);
                                    
}
    
public class mHorizontalRecyclerViewFragment extends DialogFragment {
    
public Dialog onCreateDialog(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
    
mContext = getActivity().getApplicationContext(); // Not sure why I need this, but it works. 
    
View horizontalRecyclerViewView = getActivity().getLayoutInflater().inflate(R.layout.horizontal_recyclerview_holder, new CardView(getActivity()), false);

Dialog horizontalRecyclerViewDialog = new Dialog(getActivity());
            
horizontalRecyclerViewDialog.setContentView(horizontalRecyclerViewView);

mHorizontalRecyclerView = horizontalRecyclerViewView.getRootView().findViewById(R.id.horizontalRecyclerView);    
    
mHorizontalRecyclerViewAdapter  = new horizontalRecyclerViewAdapter (mContext) 
// Other arguments passed
    
mHorizontalRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), 

LinearLayoutManager.HORIZONTAL, false));

mHorizontalRecyclerView.setAdapter(mHorizontalRecyclerViewAdapter);
          
}
    
public class horizontalRecyclerViewAdapter extends RecyclerView.Adapter<horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder> {
    
    
public horizontalRecyclerViewAdapter(){}
    
// Blank constructor and also one with lots of arguments for it to work. 
    
public horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.horizontal_recyclerview_adaptor_holder, parent, false);

return new horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder(view);

}
    
public void onBindViewHolder(@NonNull horizontalRecyclerViewHolder holder, int position) {
    
    
// Connect up various views. 
    
holder.setOnClickListener(new View.OnClickListener() {
                
@Override
public void onClick(View view) {
    
LocalBroadcastManager.getInstance(mContext).registerReceiver(reviewSubmittedListener, new IntentFilter("reviewSubmitted"));
    
Intent reviewNow = new Intent(view.getContext(), ReviewActivity.class);
    
// Put extra details with the intent
    
view.getContext().startActivity(reviewNow);
    
}
    
BroadcastReceiver reviewSubmittedListener = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent reviewFinishedIntent) {
                       
int reviewScore = reviewFinishedIntent.getExtras().getInt("reviewScore");
                        

// Update the horizontal RecyclerView with the information received from the review Activity. 

}
};

}
    
public class ReviewActivity extends AppCompatActivity {
    
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_review_item);
    
// Set up the review, using Firebase and data passed through the intent.  
    
}
    
public void submitReview() {
    
// Check that the review is complete/valid and submit it through Firebase
    
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(ReviewItemActivity.this);

Intent reviewFinishedIntent = new Intent("reviewSubmitted");
    
reviewFinishedIntent.putExtra("reviewScore", overallScore);

    
LocalBroadcastManager.getInstance(this).sendBroadcast(reviewFinishedIntent);
    
finish();
    
}

Solution

  • If you are using RxJava you can use the RxBus else you can use one of many EventBus implementation for this.

    If that is not the path you want to take then you can have a shared view model object that can be used only for communication between fragments see this article.