Xamarin android filter recyclerview data - android

i am currently working with recyclerview in Xamarin android where i wish to filter the data displayed. I used to work with java on android and has successfully create a filter on recyclerview with using searchview. But for this one , i wish to display only the incomplete data where either one of the item in the view is empty. Below is my code for activity class and also recyclerview adapter class.Replies with code or explanation would be really helpful. Thank you.
class RemoveAlertActivity : Activity
{
private RecyclerView mRecyclerView_remove;
private RecyclerView.LayoutManager mLayoutManager_remove;
private RecyclerView.Adapter mAdapter_remove;
private List<Removerecyclerholder> mRemoverecyclerholder;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.RemoveAlert);
SQLiteDb obj = new SQLiteDb();
List<ScanSummary> scan = new List<ScanSummary>();
scan = obj.GetAllScanSummaryData();
ScanSummary[] array = scan.ToArray();
mRemoverecyclerholder = new List<Removerecyclerholder>();
for (int i = 0; i < array.Length; i++)
{
mRemoverecyclerholder.Add(new Removerecyclerholder() { partnumber_r = scan[i].SSPartNo, containernumber_r = scan[i].SSContainerSN, scanquantity_r = scan[i].SSPackType, totalquantity_r = scan[i].SSStatus });
}
mRecyclerView_remove = FindViewById<RecyclerView>(Resource.Id.recyclerview_remove);
mLayoutManager_remove = new LinearLayoutManager(this);
mRecyclerView_remove.SetLayoutManager(mLayoutManager_remove);
mAdapter_remove = new RecyclerAdapter_remove(mRemoverecyclerholder);
mRecyclerView_remove.SetAdapter(mAdapter_remove);
}
}
public class RecyclerAdapter_remove : RecyclerView.Adapter
{
private List<Removerecyclerholder> mRemoverecyclerholder;
public RecyclerAdapter_remove(List<Removerecyclerholder> removerecyclerholder)
{
mRemoverecyclerholder = removerecyclerholder;
}
public class MyView : RecyclerView.ViewHolder
{
public View mMainView { get; set; }
public TextView mpartnumber_r { get; set; }
public TextView mcontainernumber_r { get; set; }
public TextView mtotalquantity_r { get; set; }
public TextView mscanquantity_r { get; set; }
public MyView(View view) : base(view)
{
mMainView = view;
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
SQLiteDb obj = new SQLiteDb();
List<ScanSummary> scan = obj.GetAllScanSummaryData();
View RemoveRecycleLayout = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.RemoveAlertRecycleLayout, parent, false);
TextView txtpartnumber_r = RemoveRecycleLayout.FindViewById<TextView>(Resource.Id.partnumber_remove);
TextView txtcontainernumber_r = RemoveRecycleLayout.FindViewById<TextView>(Resource.Id.containernumber_remove);
TextView txttotalquantity_r = RemoveRecycleLayout.FindViewById<TextView>(Resource.Id.totalquantity_remove);
TextView txtscanquantity_r = RemoveRecycleLayout.FindViewById<TextView>(Resource.Id.scanquantity_remove);
MyView view = new MyView(RemoveRecycleLayout) { mpartnumber_r = txtpartnumber_r, mcontainernumber_r = txtcontainernumber_r, mtotalquantity_r = txttotalquantity_r, mscanquantity_r = txtscanquantity_r };
return view;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyView myholder = holder as MyView;
myholder.mpartnumber_r.Text = mRemoverecyclerholder[position].partnumber_r;
myholder.mcontainernumber_r.Text = mRemoverecyclerholder[position].containernumber_r;
myholder.mtotalquantity_r.Text = mRemoverecyclerholder[position].totalquantity_r;
myholder.mscanquantity_r.Text = mRemoverecyclerholder[position].scanquantity_r;
((MyView)holder).mMainView.Click += delegate (object sender, EventArgs e)
{
View mMainView = (View)sender;
mMainView.SetBackgroundColor(color: Color.Blue);
var activity = new Intent(Application.Context, typeof(RemoveDataDetails));
activity.PutExtra("partnumber_r", mRemoverecyclerholder[position].partnumber_r);
activity.PutExtra("containernumber_r", mRemoverecyclerholder[position].containernumber_r);
Application.Context.StartActivity(activity);
};
}
public override int ItemCount
{
get { return mRemoverecyclerholder.Count; }
}
}

Related

RecyclerView messes up when scrolling

I never asked any question before but hope you'll get my point.
I am making a chat app in which I am using a RecyclerView to show messages. The problem is when I scroll the RecyclerView some of the items disappear from the top and the whole items messes up when I try to add a message it doesn't even scroll to bottom nor added in the ListView.
Here is my RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/conversation_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:layout_above="#id/typingConversationLayout"
android:layout_below="#id/topLayout_conversation_activity"
android:layout_marginBottom="-5dp"
android:paddingBottom="7dp" />
Initializing and setting the RecycerView:
linearLayoutManager = new LinearLayoutManager(this);
adapter = new ConversationRecyclerViewAdapter();
conversationRecyclerView.setAdapter(adapter);
conversationRecyclerView.setLayoutManager(linearLayoutManager);
linearLayoutManager.setStackFromEnd(true);
conversationRecyclerView.setHasFixedSize(true);
conversationRecyclerView.setNestedScrollingEnabled(false);
Here is my Adapter class:
private class ConversationRecyclerViewAdapter
extends RecyclerView.Adapter<ConversationRecyclerViewAdapter.ConversationViewHolder> {
#NonNull
#Override
public ConversationViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
Log.d(TAG, "onCreateViewHolder: Users Find started");
View conversationsView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.layout_message_received, parent, false);
return new ConversationViewHolder(conversationsView);
}
#Override
public void onBindViewHolder(#NonNull final ConversationViewHolder holderConversation, int i) {
Log.d(TAG, "onBindViewHolder: Users Find started at position is " + i);
final int position = holderConversation.getAdapterPosition();
if (mOwnUser_1.get(position)) {
holderConversation.receivedMsgLayout.setVisibility(View.GONE);
holderConversation.sentProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.sentMsg.setText(mUserText_3.get(position));
} else {
holderConversation.sentMsgLayout.setVisibility(View.GONE);
holderConversation.receivedProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.receivedMsg.setText(mUserText_3.get(position));
}
Log.d(TAG, "onBindViewHolder: completed at " + position);
}
#Override
public int getItemCount() {
return mOwnUser_1.size();
}
public class ConversationViewHolder extends RecyclerView.ViewHolder {
RelativeLayout receivedMsgLayout, sentMsgLayout;
EmojiTextView receivedMsg, sentMsg;
CircleImageView receivedProfileImg, sentProfileImg;
public ConversationViewHolder(#NonNull View v) {
super(v);
receivedMsgLayout = v.findViewById(R.id.received_message_layout);
sentMsgLayout = v.findViewById(R.id.sent_message_layout);
receivedMsg = v.findViewById(R.id.received_message_text);
sentMsg = v.findViewById(R.id.sent_message_text);
receivedProfileImg = v.findViewById(R.id.received_message_user__profile_image);
sentProfileImg = v.findViewById(R.id.sent_message_user__profile_image);
}
}
}
Here I am adding data to ListView and displaying to the RecyclerView:
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String msg = editText.getText().toString().trim();
if (TextUtils.isEmpty(msg)) {
editText.setError("Please add a message");
editText.requestFocus();
} else {
Log.d(TAG, "onClick: send Btn ADDED TEXT.. ");
mOwnUser_1.add(user);
mUserProfileImg_2.add(image);
mUserText_3.add(message);
editText.setText("");
editText.requestFocus();
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
}
}
});
I don't know what i am doing wrong but it does not seem to work as i wanted.
Update Code:
The three listviews:
private ArrayList<Boolean> mOwnUser_1 = new ArrayList<>();
private ArrayList<Integer> mUserProfileImg_2 = new ArrayList<>();
private ArrayList<String> mUserText_3 = new ArrayList<>();
And the way of adding data to adapter:
mOwnUser_1.add(true);
mUserProfileImg_2.add(R.drawable.boy);
mUserText_3.add(edittext.getText().toString().trim());
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
My Whole Conversation Activity Class:
public class ConversationActivity extends AppCompatActivity {
private static final String TAG = "ConversationActivity";
private EditText editText;
private LinearLayout linearLayout;
private LinearLayoutManager linearLayoutManager;
private ImageView sendBtn;
private ImageView emojiImage;
private View rootView;
private Boolean popUpShown = false;
private Boolean micShown = false;
private ImageView micBtn;
private RelativeLayout micLayout;
private RecyclerView conversationRecyclerView;
// Array Lists for Find USERS
private ArrayList<Boolean> mOwnUser_1 = new ArrayList<>();
private ArrayList<Integer> mUserProfileImg_2 = new ArrayList<>();
private ArrayList<String> mUserText_3 = new ArrayList<>();
private ConversationRecyclerViewAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: started");
super.onCreate(savedInstanceState);
EmojiManager.install(new TwitterEmojiProvider());
setContentView(R.layout.activity_conversation);
editText = findViewById(R.id.conversationEditText);
linearLayout = findViewById(R.id.optionsOther);
emojiImage = findViewById(R.id.emojiIconOther);
rootView = findViewById(R.id.root_view_conversation);
micBtn = findViewById(R.id.microphoneBtn);
micLayout = findViewById(R.id.microphoneLayout);
conversationRecyclerView = findViewById(R.id.conversation_recyclerView);
sendBtn = findViewById(R.id.sendBtnConversation);
if (!(Build.VERSION.SDK_INT >= 21))
findViewById(R.id.typingConversationLayout).setBackgroundResource(R.drawable.edit_text_conversation_background_below_api);
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String msg = editText.getText().toString().trim();
if (TextUtils.isEmpty(msg)) {
editText.setError("Please add a message");
editText.requestFocus();
} else {
Log.d(TAG, "onClick: send Btn ADDED TEXT.. ");
addData(true, R.drawable.boy0, msg);
}
}
});
initConversationArrayList();
}
private void addData(Boolean user, int image, String message) {
mOwnUser_1.add(user);
mUserProfileImg_2.add(image);
mUserText_3.add(message);
editText.setText("");
editText.requestFocus();
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
}
private void initConversationArrayList() {
Log.d(TAG, "initConversationArrayList: created");
mOwnUser_1.add(true);
mUserProfileImg_2.add(R.drawable.boy0);
mUserText_3.add("Hello How are you?");
Log.d(TAG, "initConversationArrayList: completed");
initConversationRecyclerView();
}
private void initConversationRecyclerView() {
Log.d(TAG, "initConversationRecyclerView: started");
linearLayoutManager = new LinearLayoutManager(this);
adapter = new ConversationRecyclerViewAdapter();
conversationRecyclerView.setAdapter(adapter);
conversationRecyclerView.setLayoutManager(linearLayoutManager);
linearLayoutManager.setStackFromEnd(true);
conversationRecyclerView.setHasFixedSize(true);
conversationRecyclerView.setNestedScrollingEnabled(false);
Log.d(TAG, "initConversationRecyclerView: completed");
}
Currently I am also working on chat module, let me show you how am I doing this. I am going to show you in steps.
Step 1: make two separate layout for recyclerview items, one for message that has been sent from your side and one for message received from another side.
Step 2 : make two view holders to populate different layout according to your scenario, made in above step, like this:
public class ChatNewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Chat> chats;
public ChatNewAdapter(List<Chat> chats) {
this.chats = chats;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View viewSend = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message_send, parent, false);
return new ViewHolderSend(viewSend);
} else {
View viewReceive = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message_received, parent, false);
return new ViewHolderReceive(viewReceive);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolderSend viewHolderSend = (ViewHolderSend) holder;
viewHolderSend.messageSend.setText(chats.get(position).getMessage());
break;
case 1:
ViewHolderReceive viewHolderReceive = (ViewHolderReceive) holder;
viewHolderReceive.messageReceived.setText(chats.get(position).getMessage());
break;
}
}
#Override
public int getItemCount() {
return chats.size();
}
#Override
public int getItemViewType(int position) {
if (chats != null && !chats.get(position).fromAdmin) {
return 0;
} else
return 1;
}
class ViewHolderSend extends RecyclerView.ViewHolder {
TextView messageSend;
public ViewHolderSend(View itemView) {
super(itemView);
messageSend = (TextView) itemView.findViewById(R.id.messageSend);
}
}
class ViewHolderReceive extends RecyclerView.ViewHolder {
TextView messageReceived;
public ViewHolderReceive(View itemView) {
super(itemView);
messageReceived = (TextView) itemView.findViewById(R.id.messageReceived);
}
}
public int addMessages(Chat chat) {
chats.add(chat);
notifyDataSetChanged();
return chats.size();
}
Step 3 : now in your activity:
public class Test extends AppCompatActivity {
RecyclerView chatList;
RecyclerView.LayoutManager mLayoutManager;
ChatNewAdapter adapter;
ImageView sendButton;
EditText messageEditText;
boolean keyboardUp = false;
boolean isRunning = false;
ArrayList<Chat> chats;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
isRunning = true;
setUpComponents();
}
public void setUpComponents() {
chatList = (RecyclerView) findViewById(R.id.chat_list);
chatList.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
chatList.setLayoutManager(mLayoutManager);
messageEditText = (EditText) findViewById(R.id.messageText);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
sendButton = (ImageView) findViewById(R.id.send);
adapter = new ChatNewAdapter(chats);
chatList.setAdapter(adapter);
chatList.scrollToPosition(chatList.getAdapter().getItemCount() - 1);
messageEditText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (keyboardShown(messageEditText.getRootView())) {
Log.d("keyboard", "keyboard UP");
if (keyboardUp == false) {
if (chats.size() > 0)
chatList.smoothScrollToPosition(chats.size() + 1);
keyboardUp = true;
}
} else {
Log.d("keyboard", "keyboard Down");
keyboardUp = false;
}
}
});
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String message = messageEditText.getText().toString().trim();
if (!message.equals("")) {
Chat chat = new Chat();
String name = message;
chat.setMessage(name);
messageEditText.setText("");
adapter.addMessages(chat);
chatList.scrollToPosition(chatList.getAdapter().getItemCount() - 1);
} else {
Log.d("sending message Error", "error fetching dates");
}
}
});
}
private boolean keyboardShown(View rootView) {
final int softKeyboardHeight = 100;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
int heightDiff = rootView.getBottom() - r.bottom;
return heightDiff > softKeyboardHeight * dm.density;
}
And this is my model class, ignore #PrimaryKey and #Required annotation it just because I am using Realm for local DB. In your case you wont required these annotation.
public class Chat extends RealmObject {
#PrimaryKey
#Required
public Long id;
public boolean fromAdmin;
#Required
public String message;
public int type;
public boolean isRead;
public boolean isSent;
public Date date;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isFromAdmin() {
return fromAdmin;
}
public void setFromAdmin(boolean fromAdmin) {
this.fromAdmin = fromAdmin;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isRead() {
return isRead;
}
public void setRead(boolean read) {
isRead = read;
}
public boolean isSent() {
return isSent;
}
public void setSent(boolean sent) {
isSent = sent;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
I hope it will be helpful for you, you can ask further if you want to know anything else related to code.
RecyclerView as the name stands recycles the views. When binding data to a view, you need to ensure you set or reset all views that are touched in the adapter. Messups typically occur when there's data that is set only conditionally for some but not all items.
In particular:
if (mOwnUser_1.get(position)) {
holderConversation.receivedMsgLayout.setVisibility(View.GONE);
holderConversation.sentProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.sentMsg.setText(mUserText_3.get(position));
} else {
holderConversation.sentMsgLayout.setVisibility(View.GONE);
holderConversation.receivedProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.receivedMsg.setText(mUserText_3.get(position));
}
Both of these branches will need to reset the other layout back to visible.
Anyway with this kind of two-layout approach you are likely better off by having them as separate view types in your adapter. See How to create RecyclerView with multiple view type?

Get Data of List of Objects

I have created an input class like 3 inputs Map: String, List, List,
and then use it in a RecyclerView.
In my constructor, I input strings in the list then clear it, so I have to get my data from the mainExampleClass
How can I access it? For instance, in the List which contains the Examples List
List<Example> exampleList;
exampleList.get(0);
how could I access the data after get(i)?
The RecylerView Data object:
public class Example {
private static MainExampleObject exampleObject;
private static String StepName;
private static List<String> TemporaryCode = new ArrayList<>(), TemporaryExplanation = new ArrayList<>();
public Example(MainExampleObject exampleObject) {
this.exampleObject = exampleObject;
}
public static void addCode(String code) {
TemporaryCode.add(code);
}
public static void addExplanation(String explanation) {
TemporaryExplanation.add(explanation);
}
public static void setStepName(String stepName) {
StepName = stepName;
}
public static MainExampleObject getExampleObject() {
return exampleObject;
}
static List<String> getTemporaryCode() {
return TemporaryCode;
}
static List<String> getTemporaryExplanation() {
return TemporaryExplanation;
}
static String getStepName() {
return StepName;
}
public static void addExample(){
exampleObject = new MainExampleObject(StepName, TemporaryCode, TemporaryExplanation);
TemporaryCode.clear();
TemporaryExplanation.clear();
}
}
The example object class:
class MainExampleObject {
private static String StepName;
private static List<String> Code, Explanation;
MainExampleObject(String stepHeader, List<String> code, List<String> explanation) {
StepName = stepHeader;
Code = code;
Explanation = explanation;
}
public static String getStepNamex() {
return StepName;
}
}
More Details
The method by which I add the data to the list
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Example.setStepName(String.valueOf(dataSnapshot.getKey()));
for (DataSnapshot childSnapshot : dataSnapshot.child("Code").getChildren()) {
Example.addCode(String.valueOf(childSnapshot.getValue()));
}
for (DataSnapshot childSnapshot : dataSnapshot.child("Explaination").getChildren()) {
Example.addExplanation(String.valueOf(childSnapshot.getValue()));
}
addExample();
exampleList.add(new Example(getExampleObject()));
adapter.notifyDataSetChanged();
}
The Adapter
List<Example> exampleList;
ViewLesson viewLesson;
public interface OnItemSelectedListenerCustom {
void onItemClicked(int selectedPosition);
}
public class ExampleHolder extends RecyclerView.ViewHolder { // here is where you define what text have value
CardView cv;
LinearLayout ll;
public ExampleHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.CV);
ll = (LinearLayout) itemView.findViewById(R.id.CV_LL);
}
}
public ExampleAdapter(ViewLesson viewLesson, List<Example> exampleList) {
this.viewLesson = viewLesson;
this.exampleList = exampleList;
}
#Override
public ExampleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_view, parent, false);
return new ExampleHolder(itemView);
}
#Override
public void onBindViewHolder(final ExampleHolder holder, int position) {
TextView tv = new TextView(holder.cv.getContext());
if (Example.getStepName() != null) {
tv.setText(Example.getStepName());
holder.ll.addView(tv);
}
if (Example.getTemporaryCode() != null && Example.getTemporaryExplanation() != null) {
int i = 0;
for (String code : Example.getTemporaryCode()) {
tv = new TextView(holder.cv.getContext());
tv.setText(code);
holder.ll.addView(tv);
tv = new TextView(holder.cv.getContext());
tv.setText(Example.getTemporaryCode().get(i));
holder.ll.addView(tv);
i++;
}
tv = new TextView(holder.cv.getContext());
tv.setText(String.valueOf(exampleList.get(0).getClass().toString()));
holder.ll.addView(tv);
tv = new TextView(holder.cv.getContext());
tv.setText(String.valueOf(exampleList.get(1).getClass().toString()));
holder.ll.addView(tv);
}
}
#Override
public int getItemCount() {
return exampleList.size();
}
}
This is the exactly line that I want to get this example of data in separately
StepName = 2 Adding b, TemporaryCode = [1aaaa, 2baaa, 3caaa], TemporaryExplanation = [1sttt, 2nddd, 3rddd]
where
the string is 2 Adding b
the 1st List is [1aaaa, 2baaa, 3caaa]
the 2nd List is [1sttt, 2nddd, 3rddd]
The Line
tv.setText(String.valueOf(exampleList.get(0).getClass().toString()));
Based on this line seems you want a String representation of all the variables in your class.
exampleList.get(0).getClass().toString()
Well, getClass() returns you a Java Class variable, and toString on a Class tells nothing about its fields.
Please see How to override toString() properly in Java? and apply it to your class after you fix whatever you did to think you needed static everywhere
If done correctly, this would work.
setText(String.valueOf(exampleList.get(0)))

list only updates when I scroll - android listview

I'm trying to build a chat app that uses a listview to display messages. I'm using SignalR for realtime communication. The issue I'm having is, the listview adapter only updates the list on the receivers end when I scroll but on the senders end, the message shows immediately.
Here's the activity for that chat:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
ISharedPreferences pref = Application.Context.GetSharedPreferences("UserInfo", FileCreationMode.Private);
loggeduser = pref.GetString("Username", String.Empty);
SetContentView(Resource.Layout.chat_activity);
initControls();
}
private async void initControls()
{
SignalRClientHelper proxySubscriber = SignalRClientHelper.GetInstance();
await proxySubscriber.StartConnection();
proxySubscriber.OnMessageReceived += proxySubscriber_OnMessageReceived;
messagesContainer = FindViewById<ListView>(Resource.Id.messagesContainer);
messageET = FindViewById<EditText>(Resource.Id.messageEdit);
sendBtn = FindViewById<Button>(Resource.Id.chatSendButton);
adapter = new ChatAdapter(this, new List<ChatMessage>(), loggeduser);
messagesContainer.Adapter = adapter;
//loadDummyHistory();
sendBtn.Click += (o, e) =>
{
string messageText = messageET.Text.ToString();
if (TextUtils.IsEmpty(messageText))
{
return;
}
proxySubscriber.InvokeSendMessage("psyoptica", messageText);
ChatMessage chatMessage = new ChatMessage();
chatMessage.Message = messageText;
chatMessage.Username = loggeduser;
messageET.Text = "";
displayMessage(chatMessage);
};
RelativeLayout container = FindViewById<RelativeLayout>(Resource.Id.container);
}
void proxySubscriber_OnMessageReceived(string username, string message)
{
ChatMessage chatMessage = new ChatMessage { Username = username, Message = message };
displayMessage(chatMessage);
}
public void displayMessage(ChatMessage message)
{
adapter.add(message);
adapter.NotifyDataSetChanged();
scroll();
}
private void scroll()
{
messagesContainer.SetSelection(messagesContainer.Count - 1);
}
}
and here's the code for the listview adapter:
class ChatAdapter : BaseAdapter
{
private List<ChatMessage> messages;
private Activity context;
private string loggedUsername;
public ChatAdapter(Activity context, List<ChatMessage> messages, string loggedUsername)
{
this.messages = messages;
this.context = context;
this.loggedUsername = loggedUsername;
}
public override int Count
{
get { return messages.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view;
ViewHolder holder;
if (convertView == null)
{
view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.chatMessage, parent, false);
holder = createViewHolder(view);
}
else
{
view = convertView;
holder = createViewHolder(view);
}
bool isMe = messages[position].Username == loggedUsername;
setAlignment(holder, isMe);
holder.txtMessage.Text = messages[position].Message;
return view;
}
private void setAlignment(ViewHolder holder, bool isMe)
{
if (!isMe)
{
holder.contentWithBG.SetBackgroundResource(Resource.Drawable.in_message_bg);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)holder.contentWithBG.LayoutParameters;
layoutParams.Gravity = GravityFlags.Right;
holder.contentWithBG.LayoutParameters = layoutParams;
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)holder.content.LayoutParameters;
lp.AddRule(LayoutRules.AlignParentLeft, 0);
lp.AddRule(LayoutRules.AlignParentRight);
holder.content.LayoutParameters = lp;
layoutParams = (LinearLayout.LayoutParams)holder.txtMessage.LayoutParameters;
layoutParams.Gravity = GravityFlags.Right;
holder.txtMessage.LayoutParameters = layoutParams;
}
else
{
holder.contentWithBG.SetBackgroundResource(Resource.Drawable.out_message_bg);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)holder.contentWithBG.LayoutParameters;
layoutParams.Gravity = GravityFlags.Left;
holder.contentWithBG.LayoutParameters = layoutParams;
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)holder.content.LayoutParameters;
lp.AddRule(LayoutRules.AlignParentRight, 0);
lp.AddRule(LayoutRules.AlignParentLeft);
holder.content.LayoutParameters = lp;
layoutParams = (LinearLayout.LayoutParams)holder.txtMessage.LayoutParameters;
layoutParams.Gravity = GravityFlags.Left;
holder.txtMessage.LayoutParameters = layoutParams;
}
}
public void add(ChatMessage message)
{
messages.Add(message);
}
public void add(List<ChatMessage> chatMessages)
{
messages.AddRange(chatMessages);
}
private ViewHolder createViewHolder(View v)
{
ViewHolder holder = new ViewHolder();
holder.txtMessage = v.FindViewById<TextView>(Resource.Id.txtMessage);
holder.content = v.FindViewById<LinearLayout>(Resource.Id.content);
holder.contentWithBG = v.FindViewById<LinearLayout>(Resource.Id.contentWithBackground);
holder.txtInfo = v.FindViewById<TextView>(Resource.Id.txtInfo);
return holder;
}
private class ViewHolder
{
public TextView txtMessage { get; set; }
public TextView txtInfo { get; set; }
public LinearLayout content { get; set; }
public LinearLayout contentWithBG { get; set; }
}
}
The method displayMessage() is called each time there's a new message added to the list. I don't understand why I have to scroll for the change to show on the receivers end. Can anyone point out the error in my code?
Try with scrollToPosition() instead of setSelection() and in the add() method in the adapter notifyDataSetChanged();
On your class ChatAdapter:
public void add(ChatMessage message)
{
messages.Add(message);
NotifyDataSetChanged();
}
public void add(List<ChatMessage> chatMessages)
{
messages.AddRange(chatMessages);
NotifyDataSetChanged();
}
Try running the process inside the proxySubscriber_OnMessageReceived():
void proxySubscriber_OnMessageReceived(string username, string message)
{
runOnUiThread(new Runnable() {
#Override
public void run() {
ChatMessage chatMessage = new ChatMessage { Username = username, Message = message };
displayMessage(chatMessage);
}
});
}

RecyclerView + SearchView Android

I have a recycler view which has cards in it. I am using a SearchView to filter the List I pass to the Adapter. I have done something like so :-
class EmployeeAdapter : RecyclerView.Adapter, IFilterable
{
private IEnumerable<IGrouping<string, EmployeeModel>> dupes;
private Activities.EmployeeActivity employeeActivity;
private List<IGrouping<string, EmployeeModel>> _items;
private List<EmployeeModel> mList;
private CustomTextView txtPlace;
private CustomTextView txtTime;
public EmployeeAdapter(List<EmployeeModel> mList, Activities.EmployeeActivity employeeActivity)
{
// TODO: Complete member initialization
this.mList = mList;
this.employeeActivity = employeeActivity;
dupes = mList.GroupBy(x => x.EmployeeName).Where(x => x.Skip(1).Any());
//Filter = new SuggestionFilter(this);
}
public override int ItemCount
{
get { return dupes.Count(); }
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
ViewHolder vh = holder as ViewHolder;
Typeface faceThin = Typeface.CreateFromAsset(employeeActivity.Assets, "Fonts/Roboto-Thin.ttf");
Typeface faceBold = Typeface.CreateFromAsset(employeeActivity.Assets, "Fonts/Roboto-Bold.ttf");
vh.txtEmployeeName.Text = dupes.ElementAt(position).Key;
foreach(EmployeeModel x in dupes.ElementAt(position))
{
var layoutChild = new RelativeLayout(employeeActivity);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WrapContent, RelativeLayout.LayoutParams.WrapContent);
lp.AddRule(LayoutRules.AlignParentLeft);
txtPlace = new CustomTextView(employeeActivity);
txtPlace.Text = x.Place;
txtPlace.LayoutParameters = lp;
layoutChild.AddView(txtPlace);
RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WrapContent, RelativeLayout.LayoutParams.WrapContent);
lp1.AddRule(LayoutRules.AlignParentRight);
txtTime = new CustomTextView(employeeActivity);
txtTime.LayoutParameters = lp1;
layoutChild.AddView(txtTime);
vh.layoutPeopleRow.AddView(layoutChild);
vh.layoutVerticalCheckin.RemoveView(vh.layoutPeopleRow);
vh.layoutVerticalCheckin.AddView(vh.layoutPeopleRow);
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.people_card_row, parent, false);
// Create a ViewHolder to hold view references inside the CardView:
ViewHolder vh = new ViewHolder(itemView);
return vh;
}
public class ViewHolder : RecyclerView.ViewHolder
{
public CustomTextView txtEmployeeName{ get; private set; }
public ImageView Image { get; private set; }
public LinearLayout layoutPeopleRow { get; private set; }
public LinearLayout layoutVerticalCheckin { get; private set; }
public CustomTextView txtCheckinTime { get; private set; }
public ViewHolder(View itemView)
: base(itemView)
{
// Locate and cache view references:
Image = itemView.FindViewById<ImageView>(Resource.Id.imgEmpPic);
txtEmployeeName = itemView.FindViewById<CustomTextView>(Resource.Id.txtEmployeeName);
layoutPeopleRow = itemView.FindViewById<LinearLayout>(Resource.Id.layoutPeopleRow);
layoutVerticalCheckin = itemView.FindViewById<LinearLayout>(Resource.Id.layoutVerticalCheckin);
}
}
public Filter Filter
{
get;
private set;
}
public void AnimateTo(List<EmployeeModel> models)
{
ApplyAndAnimateRemovals(models);
ApplyAndAnimateAdditions(models);
ApplyAndAnimateMovedItems(models);
}
private void ApplyAndAnimateMovedItems(List<EmployeeModel> newModels)
{
for (int toPosition = newModels.Count() - 1; toPosition >= 0; toPosition--) {
EmployeeModel model = newModels.ElementAt(toPosition);
int fromPosition = mList.IndexOf(model);
if (fromPosition >= 0 && fromPosition != toPosition) {
MoveItem(fromPosition, toPosition);
}
}
}
private void MoveItem(int fromPosition, int toPosition)
{
EmployeeModel model = mList.ElementAt(fromPosition);
mList.Remove(model);
mList.Add(model);
this.NotifyItemMoved(fromPosition, toPosition);
}
private void ApplyAndAnimateAdditions(List<EmployeeModel> newModels)
{
for (int i = 0, count = newModels.Count(); i < count; i++) {
EmployeeModel model = newModels.ElementAt(i);
if (!mList.Contains(model)) {
AddItem(i, model);
}
}
}
private void AddItem(int i, EmployeeModel model)
{
mList.Add(model);
this.NotifyItemInserted(i);
}
private void ApplyAndAnimateRemovals(List<EmployeeModel> newModels)
{
for (int i = mList.Count() - 1; i >= 0; i--) {
EmployeeModel model = mList.ElementAt(i);
if (!newModels.Contains(model)) {
RemoveItem(i);
}
}
}
private EmployeeModel RemoveItem(int i)
{
EmployeeModel model = mList.ElementAt(i);
mList.Remove(model);
this.NotifyItemRemoved(i);
return model;
}
}
There are two problems:
The Search is working fine, but when I make a search OnBinderViewHolder is triggered and the components in the OnBindViewHolder show up multiple times.
The list goes empty when I search once and the next search shows nothing when I clear the SearchView.
This is my Activity
void search_QueryTextChange(object sender, Android.Support.V7.Widget.SearchView.QueryTextChangeEventArgs e)
{
if (string.IsNullOrEmpty(e.NewText))
{
List<EmployeeModel> filteredModelList = filter(mList, "");
}
else
{
List<EmployeeModel> filteredModelList = filter(mList, e.NewText);
mAdapter.AnimateTo(filteredModelList);
mRecyclerView.ScrollToPosition(0);
}
}
private List<EmployeeCheckinModel> filter(List<EmployeeModel> models, String query)
{
query = query.ToLower();
List<EmployeeModel> filteredModelList = new List<EmployeeModel>();
foreach (EmployeeModel model in models) {
String text = model.EmployeeName.ToLower();
if (text.Contains(query)) {
filteredModelList.Add(model);
}
}
return filteredModelList;
}

ViewHolder doesn's work anymore after retrieve data from JSON

Everything work fine before i implement the json things. When i click the detail button(imageView), dialogFragment should be pop up. It is okay with the pop up, the problem is, it's generate two or more dialogFragment. Is implement json affect the viewholder? Or its related to settag gettag?
Here my code(I used api link from the tutorial) :
RecyclerAdapter :
public class MyVoteMAPOAdapter : RecyclerView.Adapter
{
public List<string> mImageList;
public List<MyVote_Data> mVoteData;
Activity context;
public event EventHandler<int> ItemClick;
public MyVoteMAPOAdapter (Activity context, List<MyVote_Data> data)
{
this.context = context;
mVoteData = data;
}
public override RecyclerView.ViewHolder OnCreateViewHolder (ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From (parent.Context).Inflate (Resource.Layout.ItemList, parent, false);
ViewHolder vh = new ViewHolder (itemView, OnClick);
itemView.Tag = vh;
return vh;
}
public override void OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
{
//int item = mData[position];
IWindowManager windowManager = context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
ViewHolder vh = holder as ViewHolder;
setImage (holder, position);
//var imageBitmap = GetImageBitmapFromUrl(mVoteData[position].mPosterImage);
vh.IVDetail.SetTag (Resource.Id.ivDetails, position);
//vh.IVPoster.SetImageBitmap (imageBitmap);
vh.IVDetail.SetImageResource (Resource.Drawable.ic_information);
vh.IVVote.SetImageResource (Resource.Drawable.undi_color);
vh.IVPoster.Click += delegate {
Android.App.FragmentTransaction trans = context.FragmentManager.BeginTransaction();
FullImageView fImage = FullImageView.newInstance(position);
vh.IVPoster.SetTag(Resource.Id.ivPoster, position);
trans.AddToBackStack(null);
trans.Replace(Resource.Id.place_holder, fImage);
trans.Commit();
};
vh.IVVote.Click += delegate {
if(vh.IVVote.Drawable.GetConstantState().Equals(Resource.Drawable.undi_color)){
vh.IVVote.SetImageResource(Resource.Drawable.undi_grey);
}
else {
vh.IVVote.SetImageResource(Resource.Drawable.undi_color);
}
};
//set a tag for the button to the current clicked position
vh.IVDetail.Click += IVDetail_Click;
vh.TVLike.Text = position.ToString();
}
public void IVDetail_Click (object sender, EventArgs e)
{
//retrieve the tag
int position = (int) (((ImageView) sender).GetTag(Resource.Id.ivDetails));
dialogShow (position);
Console.WriteLine ("KELUARRRRRR!~");
}
public void dialogShow(int position)
{
Android.App.FragmentTransaction transaction = context.FragmentManager.BeginTransaction();
//instantiate a fragment
DetailDialogFragment dialogFragment = DetailDialogFragment.newInstance (mVoteData[position].mName, mVoteData[position].mDescription);
dialogFragment.Show (transaction, "dialog_Fragment");
}
//
public async void setImage(RecyclerView.ViewHolder holder, int position)
{
ViewHolder vh = holder as ViewHolder;
string tempImageString = mVoteData[position].mPosterImage;
var imageBMP = GetImageBitmapFromUrl (tempImageString);
//vh.IVPoster.SetImageBitmap (imageBMP);
vh.IVPoster.SetTag (Resource.Id.ivPoster, position);
if (imageBMP.IsCompleted) {
vh.IVPoster.SetImageBitmap (imageBMP.Result);
} else {
vh.IVPoster.SetImageBitmap (await imageBMP);
}
}
//decode string into bitmap
public async static Task<Bitmap> GetImageBitmapFromUrl(string url)
{
Bitmap imageBitmap = null;
try{
if (url != null)
using (var webClient = new WebClient ()) {
var imageBytes = await webClient.DownloadDataTaskAsync (url);
if (imageBytes != null && imageBytes.Length > 0) {
imageBitmap = BitmapFactory.DecodeByteArray (imageBytes, 0, imageBytes.Length);
}
}
}
catch (Exception e){
Console.WriteLine ("Image Exception : {0}", e);
}
return imageBitmap;
}
public override int ItemCount{
get { return mVoteData.Count; }
}
void OnClick (int position)
{
if (ItemClick != null)
ItemClick (this, position);
}
}
public class ViewHolder : RecyclerView.ViewHolder
{
public ImageView IVPoster { get; private set; }
public TextView TVLike { get; private set; }
public ImageView IVDetail { get; private set; }
public ImageView IVVote { get; private set; }
public ViewHolder (View itemView, Action<int> listener) : base(itemView)
{
IVPoster = itemView.FindViewById <ImageView>(Resource.Id.ivPoster);
TVLike = itemView.FindViewById <TextView>(Resource.Id.tvLike);
IVDetail = itemView.FindViewById <ImageView> (Resource.Id.ivDetails);
IVVote = itemView.FindViewById <ImageView> (Resource.Id.ivVote);
itemView.Click += (sender, e) => listener (base.Position);
}
}
}
Child_Data.cs :
public class MyVote_Data
{
public string mPosterImage { get; set; }
public string mName { get; set; }
public string mDescription { get; set; }
public MyVote_Data ()
{
}
public MyVote_Data(string image, string name, string description)
{
mPosterImage = image;
mName = name;
mDescription = description;
}
public List<MyVote_Data> GetVoteData( string jsonString)
{
string tempImage, temptName, tempDescription;
var myVoteData = new List<MyVote_Data> ();
var data = JsonConvert.DeserializeObject <RootObject2> (jsonString);
foreach (var tempData in data.actors)
{
tempImage = tempData.image;
temptName = tempData.name;
tempDescription = tempData.description;
myVoteData.Add (new MyVote_Data(tempImage, temptName, tempDescription));
}
return myVoteData;
}
}
public class Actor
{
public string name { get; set; }
public string description { get; set; }
public string dob { get; set; }
public string country { get; set; }
public string height { get; set; }
public string spouse { get; set; }
public string children { get; set; }
public string image { get; set; }
}
public class RootObject2
{
public List<Actor> actors { get; set; }
}
There is one issue that I see that sticks out:
vh.IVPoster.Click += delegate {
Android.App.FragmentTransaction trans = context.FragmentManager.BeginTransaction();
FullImageView fImage = FullImageView.newInstance(position);
vh.IVPoster.SetTag(Resource.Id.ivPoster, position);
trans.AddToBackStack(null);
trans.Replace(Resource.Id.place_holder, fImage);
trans.Commit();
};
Every time your re-use your view holder, it still contains the contents of the previous view holder. What you're doing there is your adding an addtional click event handler for every view holder. That is why you're seeing multiple dialog fragments.
What you have to do is unregister the vh.IVPoster.Click event handler before you assigned the new one. So what I would do is refactor your delegate code into an actual method. You can then use "-=" to unregister any previous event handlers and "+=" to register the current event handler.
Update:
I'm doing this by hand, so you'll have to put in a little effort and fix any compile errors and use some basic troubleshooting to get it to work.
So first, create a new method called OnIVPosterClicked:
private void OnIVPosterClicked(object sender, EventArgs e)
{
Android.App.FragmentTransaction trans = context.FragmentManager.BeginTransaction();
FullImageView fImage = FullImageView.newInstance(position);
vh.IVPoster.SetTag(Resource.Id.ivPoster, position);
trans.AddToBackStack(null);
trans.Replace(Resource.Id.place_holder, fImage);
trans.Commit();
}
Then Register your event handler instead of using a delegate:
vh.IVPoster.Click += OnIVPosterClicked;
To Unregister the event handler when ViewHolders get recycled, you'll have to override the:
public override void OnViewRecycled(Java.Lang.Object holder)
{
//Unregister any View specific event handlers
var viewHolder = holder as ViewHolder;
viewHolder.IVPoster.Click -= OnIVPosterClicked;
base.OnViewRecycled(holder);
}
That's the basic gist of it. It probably won't compile if you blindly copy and paste it into your program, so you'll have to fix those. I'm not sure why you're setting tags. With this pattern, that should eliminate the use for keeping track of tags.

Categories

Resources