I've been researching this issue and, even though I found information, it looks like there are some details that I'm not grasping. I want to pass information from an Activity to a Fragment, which is already attached. This is the current code:
SearchActivity.java
public class SearchActivity extends AppCompatActivity {
private static final int FRAG_USERS = 0;
private static final int FRAG_MESSAGES = 1;
private int currentFrag = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
setupActionBar();
setupViewPager();
}
private void setupActionBar() {
ActionBar actionBar = this.getSupportActionBar();
actionBar.setTitle("");
actionBar.setDisplayHomeAsUpEnabled(true);
}
private void setupViewPager() {
ViewPager2 searchVP2 = findViewById(R.id.searchVP2);
searchVP2.setAdapter(new SearchPagerAdapter(this));
TabLayout searchTL = findViewById(R.id.searchTL);
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(searchTL, searchVP2, (tab, position) -> {
switch (position) {
case 0: {
tab.setText(R.string.users);
break;
} case 1: {
tab.setText(R.string.messages);
break;
}
}
});
tabLayoutMediator.attach();
searchTL.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
currentFrag = searchTL.getSelectedTabPosition();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) { }
@Override
public void onTabReselected(TabLayout.Tab tab) { }
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_searchable, menu);
SearchView searchView = (SearchView) menu.findItem(R.id.searchIT).getActionView();
searchView.setMaxWidth(Integer.MAX_VALUE);
searchView.setIconified(false);
searchView.setFocusable(true);
searchView.requestFocusFromTouch();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String query) {
if (!TextUtils.isEmpty(query)) {
if (currentFrag == FRAG_USERS) {
Bundle bundle = new Bundle();
bundle.putString(Constants.BUNDLE_SEARCH, query);
new SearchUserFragment().setArguments(bundle);
//SearchUserFragment.newInstance(query);
} else {
SearchMessageFragment.newInstance(query);
}
}
return true;
}
});
return true;
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
SearchUserFragment.java (message fragment is the same in this regard)
public class SearchUserFragment extends Fragment {
private TextView notFoundTV;
private RecyclerView searchUserRV;
private MaterialCardView searchTipCV;
private Map<String, User> userMap;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search_message, container, false);
notFoundTV = view.findViewById(R.id.notFoundTV);
searchTipCV = view.findViewById(R.id.searchTipCV);
refreshRV(view);
return view;
}
private void refreshRV(View view) {
userMap = new HashMap<>();
searchUserRV = view.findViewById(R.id.searchMessageRV);
searchUserRV.setHasFixedSize(true);
searchUserRV.setLayoutManager(new LinearLayoutManager(getContext()));
}
public void searchUser(String query) {
userMap.clear();
boolean isUserNameValid = true;
String finalQuery = query.trim();
if (finalQuery.isEmpty()) {
setViewsVisibility(View.GONE, View.VISIBLE, View.GONE);
isUserNameValid = false;
} else if (finalQuery.length() < 4 || !finalQuery.matches("^[a-zA-Z0-9]+$")) {
setViewsVisibility(View.GONE, View.VISIBLE, View.GONE);
isUserNameValid = false;
}
if (isUserNameValid) {
FirebaseDatabase.getInstance().getReference()
.child(KEY_COLLECTION_USER)
.child(finalQuery)
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
User user = snapshot.getValue(User.class);
if (user.getUserName().equals(CURRENT_USER.getUserName())) continue;
if (user.getUserName().toLowerCase().startsWith(finalQuery.toLowerCase())) {
userMap.put(user.getId(), user);
}
checkList();
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) { }
});
}
}
private void setViewsVisibility(int searchVis, int searchTipVis, int notFoundVis) {
searchUserRV.setVisibility(searchVis);
searchTipCV.setVisibility(searchTipVis);
notFoundTV.setVisibility(notFoundVis);
}
private void checkList() {
if (userMap.size() > 0) {
refreshRV(userMap);
setViewsVisibility(View.VISIBLE, View.GONE, View.GONE);
} else {
setViewsVisibility(View.GONE, View.GONE, View.VISIBLE);
}
}
private void refreshRV(@NotNull Map<String, User> userMap) {
SearchUserAdapter searchUserAdapter = new SearchUserAdapter(
getContext(),
userMap
);
searchUserRV.setAdapter(searchUserAdapter);
}
public static void newInstance(String query) {
SearchUserFragment searchUserFragment = new SearchUserFragment();
searchUserFragment.searchUser(query);
}
}
SearchPagerAdapter.java
public class SearchPagerAdapter extends FragmentStateAdapter {
public SearchPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
@NonNull
@Override
public Fragment createFragment(int position) {
if (position == 0) {
return new SearchUserFragment();
} else {
return new SearchMessageFragment();
}
}
@Override
public int getItemCount() {
return 2;
}
}
I don't understand why some newInstance() methods that I have found during my research returns the Fragment itself. I have already tried the following approach inside the SearchUserFragment:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null && getArguments().containsKey(BUNDLE_SEARCH)) {
searchUser(savedInstanceState.getString(BUNDLE_SEARCH));
}
}
public static SearchUserFragment newInstance(String query) {
SearchUserFragment searchUserFragment = new SearchUserFragment();
Bundle args = new Bundle();
args.putString(BUNDLE_SEARCH, query);
searchUserFragment.setArguments(args);
return searchUserFragment;
}
But it didn't work out, it's like if newInstance() doesn't instantiate another instance of the fragment. The app doesn't crash, but nothing will happen when I type something into the SearchView.
I have tried to do this as well: SearchActivity.java
@Override
public boolean onQueryTextChange(String query) {
if (!TextUtils.isEmpty(query)) {
if (currentFrag == FRAG_USERS) {
Bundle bundle = new Bundle();
bundle.putString(Constants.BUNDLE_SEARCH, query);
new SearchUserFragment().setArguments(bundle);
} else {
SearchMessageFragment.newInstance(query);
}
}
return true;
}
SearchUserFragment.java
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search_message, container, false);
notFoundTV = view.findViewById(R.id.notFoundTV);
searchTipCV = view.findViewById(R.id.searchTipCV);
refreshRV(view);
Bundle bundle = this.getArguments();
if (bundle != null) {
searchUser(bundle.getString(BUNDLE_SEARCH));
}
return view;
}
It didn't work out neither. Same behaviour as stated above.
In the current state of the code, I get the NullPointerException (rightfully so). The current code makes all my lists and views become null because onCreateView() is not being triggered due to being already created.
I just want to be able to get the query from the SearchActivity in order to execute searchUser()/searchMessage() from the fragments flawlessly.