I'm having a problem with this code, I created a personal chat, but when I send a message the RecyclerView does not show automatically the last message sent but I have to scroll down, how can I make sure that automatically displays the last message?
public class ChatListAdapter extends RecyclerView.Adapter {
private Activity mActivity;
private DatabaseReference mDataBaseReference;
private String mDisplayName;
private ArrayList<DataSnapshot> mDataSnapshot;
private ChildEventListener mListener = new ChildEventListener () {
#Override
public void onChildAdded(DataSnapshot dataSnapshot,String s) {
mDataSnapshot.add (dataSnapshot);
notifyDataSetChanged ();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot,String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot,String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
public ChatListAdapter(Activity activity, DatabaseReference ref, String name){
mActivity = activity;
mDataBaseReference = ref.child ("messaggi");
mDisplayName = name;
mDataSnapshot = new ArrayList <> ();
mDataBaseReference.addChildEventListener (mListener);
}
public class ChatViewHolder extends RecyclerView.ViewHolder{
TextView autore;
TextView messaggio;
LinearLayout.LayoutParams params;
public ChatViewHolder(View itemView) {
super (itemView);
autore = (TextView)itemView.findViewById (R.id.tv_autore);
messaggio = (TextView)itemView.findViewById (R.id.tv_messaggio);
params = (LinearLayout.LayoutParams) autore.getLayoutParams ();
}
}
#NonNull
#Override
public ChatViewHolder onCreateViewHolder(#NonNull ViewGroup parent,int viewType) {
LayoutInflater inflater = (LayoutInflater)mActivity.getSystemService (Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate (R.layout.chat_msg_row, parent, false);
ChatViewHolder vh = new ChatViewHolder (v);
return vh;
}
#Override
public void onBindViewHolder(#NonNull ChatViewHolder holder,int position) {
DataSnapshot snapshot = mDataSnapshot.get (position);
Messaggio msg = snapshot.getValue (Messaggio.class);
holder.autore.setText (msg.getAutore ());
holder.messaggio.setText (msg.getMessaggio ());
boolean sonoIo = msg.getAutore ().equals (mDisplayName);
setChatItemStyle(sonoIo, holder);
}
private void setChatItemStyle(boolean sonoIo, ChatViewHolder holder){
if(sonoIo){
holder.params.gravity = Gravity.END;
holder.autore.setTextColor (Color.GREEN);
holder.messaggio.setBackgroundResource(R.drawable.in_msg_bg);
}else{
holder.params.gravity = Gravity.START;
holder.autore.setTextColor (Color.CYAN);
holder.messaggio.setBackgroundResource(R.drawable.out_msg_bg);
}
holder.autore.setLayoutParams (holder.params);
holder.messaggio.setLayoutParams (holder.params);
}
#Override
public int getItemCount() {
return mDataSnapshot.size ();
}
public void clean(){
mDataBaseReference.removeEventListener (mListener);
}
}
You can set stackFromEnd to be true on your LinearLayoutManager. If your chat messages are coming back most recent first, you can also reverse on the manager rather than in data. Then to auto scroll on dataset changed, simply set android:transcriptMode to normal (scroll only if already at bottom) or alwaysScroll (always scroll to bottom after a change) on your RecyclerView xml.
boolean reverseLayout = true; // Or false if your data is already reversed
LinearLayoutManager manager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, reverseLayout);
manager.setStackFromEnd(true);
yourRecyclerView.setLayoutManager(manager);
I am assuming you are using a recycler view to populate messages.
So here it is
Variable Declaration
private RecyclerView rvChat;
onCreate
rvChat = findViewById(R.id.a_individual_chat_rv_chat);
onChildAdded (After notifyDataSetChanged)
rvChat.smoothScrollToPosition(rvChat.getAdapter().getItemCount());
Related
I have an issue where notifyDataSetChanged() in a response call will blank out the recyclerview but if the Adapter is initiated manually with a onClick, the recyclerview works. I have tested that the List has the items inside before calling notifyDataSetChanged() so i'm not sure what's wrong here.
[Main Activity] This works but i have to manually click the bnQuery.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = API_client.getClient().create(APIInterface.class);
etCoin = (EditText) findViewById(R.id.etCoin);
bnQuery = (Button) findViewById(R.id.bnQuery);
rcvMain = findViewById(R.id.rcvMain);
getCoinData("2");
//initRCV_Main();
bnQuery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//getCoinData("2");
initRCV_Main();
}
});
}
private void initRCV_Main() {
rcvMainAdp = new rcvMainAdapter(cList);
rcvMain.setAdapter(rcvMainAdp);
rcvMain.setLayoutManager(new LinearLayoutManager(this));
}
private void getCoinData(String coinLimit){
Call<cInfoPack> call = apiInterface.doGetCoinData(coinLimit);
call.enqueue(new Callback<cInfoPack>() {
#Override
public void onResponse(Call<cInfoPack> call, Response<cInfoPack> response) {
cInfoPack list = response.body();
List<cData> listSorter = new ArrayList<>();
listSorter.addAll(list.getData());
Collections.sort(listSorter, new SortbyVolChg());
cList.clear();
cList = listSorter;
System.out.println("list " + list.getData().get(0).getQuote());
System.out.println("listSorter " + listSorter.get(0).getQuote());
System.out.println("cList " + cList.get(0).getQuote());
//rcvMainAdp.notifyDataSetChanged();
}
#Override
public void onFailure(Call<cInfoPack> call, Throwable t) {
Toast.makeText(MainActivity.this, "onFailure", Toast.LENGTH_SHORT).show();
Log.d("XXXX", t.getLocalizedMessage());
call.cancel();
}
});
}
[Main Activity] If i initiate the recyclerview during onCreate and use the notifyDataSetChanged() during getCoinData, I get a blank recycleview. system.out shows that the lists all contain information in them.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = API_client.getClient().create(APIInterface.class);
etCoin = (EditText) findViewById(R.id.etCoin);
bnQuery = (Button) findViewById(R.id.bnQuery);
rcvMain = findViewById(R.id.rcvMain);
getCoinData("2");
initRCV_Main();
bnQuery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//getCoinData("2");
//initRCV_Main();
}
});
}
private void initRCV_Main() {
rcvMainAdp = new rcvMainAdapter(cList);
rcvMain.setAdapter(rcvMainAdp);
rcvMain.setLayoutManager(new LinearLayoutManager(this));
}
private void getCoinData(String coinLimit){
Call<cInfoPack> call = apiInterface.doGetCoinData(coinLimit);
call.enqueue(new Callback<cInfoPack>() {
#Override
public void onResponse(Call<cInfoPack> call, Response<cInfoPack> response) {
cInfoPack list = response.body();
List<cData> listSorter = new ArrayList<>();
listSorter.addAll(list.getData());
Collections.sort(listSorter, new SortbyVolChg());
cList.clear();
cList = listSorter;
System.out.println("list " + list.getData().get(0).getQuote());
System.out.println("listSorter " + listSorter.get(0).getQuote());
System.out.println("cList " + cList.get(0).getQuote());
rcvMainAdp.notifyDataSetChanged();
}
#Override
public void onFailure(Call<cInfoPack> call, Throwable t) {
Toast.makeText(MainActivity.this, "onFailure", Toast.LENGTH_SHORT).show();
Log.d("XXXX", t.getLocalizedMessage());
call.cancel();
}
});
}
[Adapter]
public class rcvMainAdapter extends RecyclerView.Adapter<rcvMainAdapter.ViewHolder> {
private List<cData> idxCoin;
//ItemClickListener itemClickListener;
rcvMainAdapter(List<cData> data) {this.idxCoin = data;}
#NonNull
#NotNull
#Override
public ViewHolder onCreateViewHolder(#NonNull #NotNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.rcv_main,parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
cData cdata = idxCoin.get(position);
TextView tvSym = holder.tvSymbol;
tvSym.setText(cdata.getSymbol());
TextView tvQuo = holder.tvQuote;
BigDecimal tvQuote_BD = new BigDecimal(cdata.getQuote().getuSD().getPrice().toString());
tvQuote_BD.setScale(6, RoundingMode.DOWN);
tvQuo.setText(tvQuote_BD.toString());
TextView tvV24 = holder.tvVolume24;
BigDecimal tvVolume24_BD = new BigDecimal(cdata.getQuote().getuSD().getVolume24h().toString());
BigInteger tvVolume24_BI = tvVolume24_BD.toBigInteger();
tvV24.setText(tvVolume24_BI.toString());
}
#Override
public int getItemCount() {
return idxCoin.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvSymbol, tvQuote, tvVolume24;
public ViewHolder(#NonNull #NotNull View itemView) {
super(itemView);
tvSymbol = itemView.findViewById(R.id.tvSymbol);
tvQuote = itemView.findViewById(R.id.tvQuote);
tvVolume24 = itemView.findViewById(R.id.tvVolume24);
//itemView.setOnClickListener(this);
}
}
/*
public interface ItemClickListener{
void onItemClick(View view, int position);
}
*/
}
PS: apologies for the rubbish coding as this is self taught and modifying some codes found online.
Remove this in response.
cList.clear();
Add This line in response
rcvMainAdp.setdata(listSorter);
In rcvMainAdp Adapter, Create a Method setdata()
public void setdata(ArrayList<cData> data) {
this.idxCoin = data;
notifyDataSetChanged();
}
Problem most likely is that when you call initRCV_Main() You set the adapter to the list as in rcvMainAdp = new rcvMainAdapter(cList); And when list is changed and you set it to adapter it functions.
But when you call getCoinData() and rcvMainAdp.notifyDataSetChanged(); at the end you never set the changed list to the adapter until you click initRCV_Main() again.
So maybe the fix is calling rcvMainAdp = new rcvMainAdapter(cList) and then
rcvMainAdp.notifyDataSetChanged();
I did the code below, used the adapter to show the list, and a button to add a comment to list item, it shows the logcat "ViewPostIme pointer 0 and ViewPostIme pointer 1", upon clicking the comment button.
Upon compilation it doesn't show any error, I tried to use many references via google but nothing works out.
My basic idea is to call the comment button with an associated list item to perform the comment activity.
Adapter class
public class PostsAdapter extends FirestoreRecyclerAdapter<PostsModel, PostsAdapter.PostsHolder> {
private OnItemClickListener listener;
private View.OnClickListener buttonListener;
private String id;
private static final String TAG = "DocSnippets";
public PostsAdapter(#NonNull FirestoreRecyclerOptions<PostsModel> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull PostsHolder holder, int position, #NonNull PostsModel model) {
//retrieve the fields here
holder.textViewDescription.setText(model.getPostContent());
holder.textViewPriority.setText(String.valueOf(model.getSpinnerC()));
holder.textViewPriority.setText(String.valueOf(model.getTimestamp()));
}
#NonNull
#Override
public PostsHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_list_layout,
parent, false);
return new PostsHolder(v);
}
public void deleteItem(int position) {
getSnapshots().getSnapshot(position).getReference().delete();
}
public void setOnClickListener(OnClickListener postKey) {
}
class PostsHolder extends RecyclerView.ViewHolder {
//first declare here the elements to be displayed in the cardview.
TextView textViewTitle;
TextView textViewDescription;
TextView textViewPriority;
Button commentsbutton;
public PostsHolder(final View itemView) {
super(itemView);
textViewTitle = itemView.findViewById(R.id.post_etPostTitle);
textViewDescription = itemView.findViewById(R.id.post_description);
textViewPriority = itemView.findViewById(R.id.post_time);
commentsbutton = itemView.findViewById(R.id.commenting_button);
commentsbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && buttonListener != null) {
buttonListener.onClick(itemView);
}
}
});
}
}
public interface OnClickListener
{
void OnClickListener(DocumentSnapshot documentSnapshot, int position);
}
public void setOnClickListener(View.OnClickListener onClickListener) {
this.buttonListener = onClickListener;
}
public interface OnItemClickListener {
void onItemClick(DocumentSnapshot documentSnapshot, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
}
Fragment Class
public class HomeFragment extends Fragment {
RelativeLayout mParent;
//FloatingActionButton addButton;
private static final String TAG = "DocSnippets";
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference PostsRef = db.collection("posts");
private PostsAdapter adapter;
private FirestoreRecyclerOptions options;
private FirebaseAuth mAuth;
private String mUserId, id;
private Button commentsbutton;
RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//just change the fragment_dashboard
//with the fragment you want to inflate
//like if the class is HomeFragment it should have R.layout.home_fragment
//if it is DashboardFragment it should have R.layout.fragment_dashboard
View view = inflater.inflate(R.layout.fragment_home, container, false);
final FragmentActivity c = getActivity();
LinearLayoutManager layoutManager = new LinearLayoutManager(c);
Query query = PostsRef.orderBy("timestamp", Query.Direction.DESCENDING);
FirestoreRecyclerOptions<PostsModel> options = new FirestoreRecyclerOptions.Builder<PostsModel>()
.setQuery(query, PostsModel.class)
.build();
adapter = new PostsAdapter(options);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(c));
recyclerView.setAdapter(adapter);
commentsbutton = (Button) view.findViewById(R.id.commenting_button);
mParent =view.findViewById(R.id.relative_home);
mAuth = FirebaseAuth.getInstance();
mUserId = mAuth.getUid();
adapter.setOnItemClickListener(new PostsAdapter.OnItemClickListener() {
#Override
public void onItemClick(DocumentSnapshot documentSnapshot, int position) {
PostsModel note = documentSnapshot.toObject(PostsModel.class);
id = documentSnapshot.getId();
String path = documentSnapshot.getReference().getPath();
Log.d(TAG, "String post Id is: " + id);
}
});
adapter.setOnClickListener(new PostsAdapter.OnClickListener() {
#Override
public void OnClickListener(DocumentSnapshot documentSnapshot, int position) {
PostsModel note = documentSnapshot.toObject(PostsModel.class);
id = documentSnapshot.getId();
String path = documentSnapshot.getReference().getPath();
Log.d(TAG, "String post Id is: " + id);
Intent toCommentActivity = new Intent(getContext(), CommentActivity.class);
toCommentActivity.putExtra("PostKey", id);
getContext().startActivity(toCommentActivity);
}
});
return view;
}
private String getTime(long timestamp){
long ts = timestamp*1000;
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm a");
String time = sdf.format(new Date(ts));
return time;
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
}
After implementing all the Recyclerview codes, i still end up with this Error :E/RecyclerView: No adapter attached; skipping layout.
Im not sure if it is because of the layout or where it is placed...
I changed the location of it multiple times and i had to revert back to an older version as some of the solutions i found on the internet caused it to crash.
public class MainMenu extends AppCompatActivity implements
IFirebaseLoadListener {
private Context context;
private List<ItemData> itemDataList;
private List<ItemGroup> dataList;
AlertDialog dialog;
IFirebaseLoadListener iFirebaseLoadListener;
RecyclerView my_recycler_view;
DatabaseReference myData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
myData = FirebaseDatabase.getInstance().getReference("Data");
dialog = new SpotsDialog.Builder().setContext(this).build();
iFirebaseLoadListener = this;
//View
my_recycler_view = (RecyclerView)findViewById(R.id.my_recyclr_view);
my_recycler_view.setHasFixedSize(true);
my_recycler_view.setLayoutManager(new LinearLayoutManager(this));
getFirebaseData();
}
private void getFirebaseData () {
dialog.show();
myData.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot dataSnapshot) {
List<ItemGroup> itemGroups =new ArrayList<>();
for(DataSnapshot groupSnapShot:dataSnapshot.getChildren()){
ItemGroup itemGroup = new ItemGroup();
itemGroup.setHeaderTitle(groupSnapShot.child("headerTitle").getValue (String.class));
GenericTypeIndicator<ArrayList<ItemData>> genericTypeIndicator =new GenericTypeIndicator<ArrayList<ItemData>>(){};
itemGroup.setListItem(groupSnapShot.child("listItem").getValue(genericTypeIndicator));
itemGroups.add(itemGroup);
}
iFirebaseLoadListener.onFirebaseLoadSuccess(itemGroups);
}
#Override
public void onCancelled (#NonNull DatabaseError databaseError) {
iFirebaseLoadListener.onFirebaseLoadFailed(databaseError.getMessage());
}
});
}
#Override
public void onFirebaseLoadSuccess(List<ItemGroup> itemGroupList) {
MyItemGroupAdapter adapter = new
MyItemGroupAdapter(this,itemGroupList);
my_recycler_view.setAdapter(adapter);
dialog.dismiss();
}
#Override
public void onFirebaseLoadFailed(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
}
I have 2 other pages for my Adapters, "MyItemAdapter" and "MyItemGroupAdapter"
but im not sure why it isnt able to call them
My Adapters:
public class MyItemAdapter extends RecyclerView.Adapter<MyItemAdapter.MyViewHolder> {
private Context context;
private List<ItemData> itemDataList;
public MyItemAdapter(Context context, List<ItemData> itemDataList) {
this.context = context;
this.itemDataList = itemDataList;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(context).inflate(R.layout.layout_item,viewGroup,false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, final int i) {
myViewHolder.txt_item_title.setText(itemDataList.get(i).getName());
Picasso.get().load(itemDataList.get(i).getArt()).into(myViewHolder. img_item);
myViewHolder.setiItemClickListener(new IItemClickListener() {
#Override
public void onItemClickListener(View view, int position) {
Toast.makeText(context, ""+itemDataList.get(i).getName(), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {return (itemDataList != null ? itemDataList.size() : 0);
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView txt_item_title;
ImageView img_item;
IItemClickListener iItemClickListener;
public void setiItemClickListener(IItemClickListener iItemClickListener) {
this.iItemClickListener = iItemClickListener;
}
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txt_item_title = itemView.findViewById(R.id.name);
img_item = itemView.findViewById(R.id.art);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
iItemClickListener.onItemClickListener(view,getAdapterPosition());
}
}
}
There's no data for your RecyclerView to display when the view is created.
Inside of your onCreate you should create your adapter with empty or null data then call setAdapter() on your RecyclerView using the empty adapter. Later, you can update the adapter with new data and call notifyDataSetChanged() on your adapter to refresh it.
i created a recyclerview where i can see all my chats. Every item contains the name, the lastmessage and a thumb. if the user send or receive a new message, it should be displayed there and the item should go to position 0. Now i have the problem that the message is displayed, but the chat item goes only to the first position if the position was not 0 before. So if i receive two new messages its not working anymore because the data model is the same like before. So how can i fit my data model to the current position of every item. Currently my adapter looks like this:
private static final String TAG = "CustomAdapter";
private Context context;
private Activity mActivity;
private List<MatchesObject> matchesList;
private String currentUid;
public matchadapterino(Activity mActivity, ArrayList<MatchesObject> mDataSet) {
this.mActivity = mActivity;
this.matchesList = mDataSet;
}
// Create new views (invoked by the layout manager)
#Override
public matchadapterino.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view.
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.matchesitem, viewGroup, false);
return new matchadapterino.ViewHolder(v);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(final matchadapterino.ViewHolder viewHolder, final int position) {
viewHolder.getMatchname().setText(matchesList.get(position).getUsername());
if (!matchesList.get(position).getProfileImageUrl().equals("default")) {
Picasso.with(mActivity).load(matchesList.get(position).getProfileImageUrl()).into(viewHolder.getMatchImage());
getLastMSG(matchesList.get(position).getUserId(), viewHolder.getLastMSG(), position);
viewHolder.getMatchImage().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(mActivity, UserDetailView.class);
//
i.putExtra("userId", matchesList.get(position).getUserId());
mActivity.startActivity(i);
}
});
} else {
viewHolder.getMatchImage().setImageResource(R.mipmap.ic_launcher_round);
}
viewHolder.getForeground().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(mActivity, Chat.class);
Bundle b = new Bundle();
b.putString("matchId", matchesList.get(position).getUserId());
i.putExtras(b);
mActivity.startActivity(i);
}
});
}
#Override
public int getItemCount() {
return matchesList.size();
}
// Get element from your dataset at this position and replace the contents of the view
// with that element
public void removeItem(int position) {
matchesList.remove(matchesList.get(position));
notifyItemRemoved(position);
}
// Return the size of your dataset (invoked by the layout manager)
/**
* Provide a reference to the type of views that you are using (custom ViewHolder)
*/
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView matchname, lastMSG;
public ImageView matchImage;
private RelativeLayout foreground;
private RelativeLayout background;
public ViewHolder(View v) {
super(v);
currentUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
matchname = itemView.findViewById(R.id.matchname);
matchImage = itemView.findViewById(R.id.matchImages);
lastMSG = itemView.findViewById(R.id.lastmsg);
foreground = itemView.findViewById(R.id.foregroundmatch);
background = itemView.findViewById(R.id.backgroundmatch);
}
public RelativeLayout getForeground() {
return foreground;
}
public RelativeLayout getBackground() {
return background;
}
public TextView getLastMSG() {
return lastMSG;
}
public TextView getMatchname() {
return matchname;
}
public ImageView getMatchImage() {
return matchImage;
}
}
private void getLastMSG(final String userId, final TextView lastMSG, final int position) {
final String userid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("connections").child("matches").child(userId);
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
notifyItemMoved(0, position);
Toast.makeText(mActivity, "position changed" + position, Toast.LENGTH_LONG).show();
if (dataSnapshot.child("lastMsgBy").getValue().toString().equals(userid)) {
lastMSG.setTextColor(Color.parseColor("#d2403a3a"));
String lastMsg = dataSnapshot.child("lastMsg").getValue().toString();
lastMSG.setText(lastMsg);
} else {
lastMSG.setTextColor(Color.parseColor("#000000"));
String lastMsg = dataSnapshot.child("lastMsg").getValue().toString();
lastMSG.setText(lastMsg);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
I am relatively new to Android programming. I have a RecycleView with CardView in it settled in rows. On click of any row opens a new Activity associated to that row. Everything was working great until I added the filter functionality to this list. When I search the list and then click on one Item, it doesn't open the activity associated with the filtered results. It opens up an Activity related to the Item at that position in Original list.
Example - Original List : AA, BA, CC, DA, ED, FF
Search : 'A' Filtered results: AA, BA, DA
But when I click on item DA it opens up the Activity for CC. I have called notifyDataSetChanged() on the adapter.
I searched and found similar problems but couldn't implement in my code.
Here is the code:
public class MainActivity extends AppCompatActivity implements ExampleAdapter.OnItemClickListener{
public static final String EXTRA_IMG = "imageresource";
public static final String EXTRA_TXT1 = "text1";
public static final String EXTRA_TXT2 = "text2";
public static final String EXTRA_TXT3 = "text3";
private ArrayList<ExampleItem> mExampleList;
private RecyclerView mRecyclerView;
private ExampleAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createExampleList();
buildRecyclerView();
EditText editText = findViewById(R.id.editText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
}
private void filter(String text) {
ArrayList<ExampleItem> filteredList = new ArrayList<>();
for (ExampleItem item : mExampleList) {
if (item.getText1().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
mAdapter.filterList(filteredList);
}
private void createExampleList() {
//just creating list
}
private void buildRecyclerView() {
mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mAdapter = new ExampleAdapter(mExampleList);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(MainActivity.this);
}
#Override
public void onItemClick(int position) {
Intent detailintent = new Intent(this,DetailActivity.class);
ExampleItem clickedItem = mExampleList.get(position);
detailintent.putExtra(EXTRA_IMG,clickedItem.getImageResource());
detailintent.putExtra(EXTRA_TXT1,clickedItem.getText1());
detailintent.putExtra(EXTRA_TXT2,clickedItem.getText2());
detailintent.putExtra(EXTRA_TXT3,clickedItem.getText3());
startActivity(detailintent);
}
}
public class ExampleAdapter extends RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder> {
private ArrayList<ExampleItem> mExampleList;
private OnItemClickListener mListener;
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener=listener;
}
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public TextView mTextView1;
public TextView mTextView2;
public TextView mTextView3;
public ExampleViewHolder(View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.imageView);
mTextView1 = itemView.findViewById(R.id.textView);
mTextView2 = itemView.findViewById(R.id.textView7);
mTextView3 = itemView.findViewById(R.id.textView2);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mListener != null ){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
mListener.onItemClick(position);
}
}
}
});
}
}
public ExampleAdapter(ArrayList<ExampleItem> exampleList) {
mExampleList = exampleList;
}
#Override
public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.example_item,
parent, false);
ExampleViewHolder evh = new ExampleViewHolder(v);
return evh;
}
#Override
public void onBindViewHolder(ExampleViewHolder holder, int position) {
ExampleItem currentItem = mExampleList.get(position);
holder.mImageView.setImageResource(currentItem.getImageResource());
holder.mTextView1.setText(currentItem.getText1());
holder.mTextView2.setText(currentItem.getText2());
holder.mTextView3.setText(currentItem.getText3());
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public void filterList(ArrayList<ExampleItem> filteredList) {
mExampleList = filteredList;
notifyDataSetChanged();
}
}
You have two lists, one in your activity and one in your adapter.
After filtering your list, you only notify the adapter and only set the adapters mExampleList to the new, filtered list. The Activity's list remains the same.
When you click on an item, you let the activity handle the click event. But the activity still have the old, unfiltered list so it will send the wrong data to your new activity.
Solution: simply add mExampleList = filteredList next to the line mAdapter.filterList(filteredList); in your filtering method