I am using a RecyclerView inside a Fragment to display a list of categories ( as shown in the images). When a category is selected, a new Activity starts with a RecyclerView. Each item from the list has a TextView and an icon for favorite ( the star from the top-right corner ). Once the favorite icon is pressed, the TextView will be saved to another Activity called Favorites.
The problem: let's say that I select Category A and I press the favorite button for the first 2 items. Everything looks good, the items are saved to Favorites. If I select Category B, bamm, I find the first 2 items selected...I go to Category C, same thing!
So if I check the favorite button once, it checks for all Adapters, like they're communicating. Why is this happening?
Although the favorite buttons are checked, I can't find the TextView's from Category B or C in the Favorites activity.
The Activity that starts when a category item is selected:
public class CategoriesDetailActivity extends AppCompatActivity {
Adapter0 ca0;
RecyclerView recList;
public CategoriesDetailActivity() {
// Required empty public constructor
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.categories_detail_activity);
Bundle bundle = this.getIntent().getExtras();
// Bundle bundle = this.getArguments();
bundle.getInt("id");
int position = bundle.getInt("id");
if (bundle.containsKey("id")) {
position = bundle.getInt("id");
} else {
this.finish();
}
recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
switch (position) {
case 0:
ca0 = new Adapter0(this, createList0(99));
recList.setAdapter(ca0);
break;
case 1:
ca0 = new Adapter0(this, createList1(80));
recList.setAdapter(ca0);
break;
...
}
}
private List<BeanSampleList> createList0(int size) {
List<BeanSampleList> result = new ArrayList<>();
for (int i = 0; i <= size; i++) {
BeanSampleList ci = new BeanSampleList();
ci.title = DataText.Text1[i];
ci.id = i;
result.add(ci);
}
return result;
}
private List<BeanSampleList> createList1(int size) {
List<BeanSampleList> result = new ArrayList<>();
for (int i = 0; i <= size; i++) {
BeanSampleList ci = new BeanSampleList();
ci.title = DataText.Text2[i];
ci.id = i;
result.add(ci);
}
return result;
}
#Override
public void onResume() {
super.onResume();
if (recList.getAdapter() == ca0) {
ca0.notifyDataSetChanged();
} if (recList.getAdapter() == ca1) {
ca1.notifyDataSetChanged();
} else {
// nothing
}
}
}
The Adapter class:
public class Adapter0 extends RecyclerView.Adapter<Adapter0 .ContactViewHolder> {
private Context context;
List<BeanSampleList> postBeanSampleList;
SharedPreference sharedPreference;
BeanSampleList beanSampleList;
public Adapter0 (Context context, List<BeanSampleList> postBeanSampleList) {
this.context = context;
this.postBeanSampleList = postBeanSampleList;
sharedPreference = new SharedPreference();
}
#Override
public int getItemCount() {
return postBeanSampleList.size();
}
public Object getItem(int position) {
return postBeanSampleList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public void onBindViewHolder(final ContactViewHolder holder,final int i) {
beanSampleList = (BeanSampleList) getItem(i);
holder.vName.setText(beanSampleList.getTitle());
if (checkFavoriteItem(beanSampleList)) {
holder.btnFavourite.setLiked(true);
holder.btnFavourite.setTag("active");
} else {
holder.btnFavourite.setLiked(false);
holder.btnFavourite.setTag("deactive");
}
}
#Override
public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.categories_detail_adapter, viewGroup, false);
return new ContactViewHolder(itemView);
}
public class ContactViewHolder extends RecyclerView.ViewHolder implements OnLikeListener {
protected TextView vName;
protected LikeButton btnFavourite;
public ContactViewHolder(View v) {
super(v);
vName = (TextView) v.findViewById(R.id.t1);
btnFavourite = (LikeButton) v.findViewById(R.id.favouritesToggle);
btnFavourite.setOnLikeListener(this);
}
#Override
public void liked(LikeButton likeButton) {
final int position = getAdapterPosition();
if (likeButton.getId() == btnFavourite.getId()) {
String tag = btnFavourite.getTag().toString();
if (tag.equalsIgnoreCase("deactive")) {
sharedPreference.addFavorite(context, postBeanSampleList.get(position));
btnFavourite.setTag("active");
btnFavourite.setLiked(true);
}
Snackbar snackbar = Snackbar
.make(likeButton, "Added to Favorites!", Snackbar.LENGTH_SHORT);
snackbar.show();
}
}
#Override
public void unLiked(LikeButton likeButton) {
final int position = getAdapterPosition();
if (likeButton.getId() == btnFavourite.getId()) {
sharedPreference.removeFavorite(context, postBeanSampleList.get(position));
btnFavourite.setTag("deactive");
btnFavourite.setLiked(false);
Snackbar snackbar = Snackbar
.make(likeButton, "Removed from Favorites!", Snackbar.LENGTH_SHORT);
snackbar.show();
}
}
}
public boolean checkFavoriteItem(BeanSampleList checkProduct) {
boolean check = false;
List<BeanSampleList> favorites = sharedPreference.loadFavorites(context);
if (favorites != null) {
for (BeanSampleList product : favorites) {
if (product.equals(checkProduct)) {
check = true;
break;
}
}
}
return check;
}
}
For the past 2 days I've been trying to fix this but I just can't figure out what's causing this. I also tried to use different Adapters for each position but with no success.
public class BeanSampleList {
public int id;
public String title;
public String subTitle;
public String bottomTitle;
public String imageView;
public BeanSampleList() {
super();
}
public BeanSampleList(int id, String title, String subTitle, String bottomTitle, String imageView) {
super();
this.id = id;
this.title = title;
this.subTitle = subTitle;
this.bottomTitle = bottomTitle;
this.imageView = imageView;
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleBottom() {
return bottomTitle;
}
public void setTitleBottom(String bottomTitle) {
this.bottomTitle = bottomTitle;
}
public String getImageView() {
return imageView;
}
public void setImageView(String imageView) {
this.imageView = imageView;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BeanSampleList other = (BeanSampleList) obj;
if (id != other.id)
return false;
return true;
}
}
Thank you so much and sorry for my english.
I think the error is in your id generation for BeanSampleList
private List<BeanSampleList> createList0(int size) {
List<BeanSampleList> result = new ArrayList<>();
for (int i = 0; i <= size; i++) {
BeanSampleList ci = new BeanSampleList();
ci.title = DataText.Text1[i];
ci.id = i;
result.add(ci);
}
return result;
}
private List<BeanSampleList> createList1(int size) {
List<BeanSampleList> result = new ArrayList<>();
for (int i = 0; i <= size; i++) {
BeanSampleList ci = new BeanSampleList();
ci.title = DataText.Text2[i];
ci.id = i;
result.add(ci);
}
return result;
}
You need to generate unique id for your BeanSamleList objects in createList* method. As in your current code the objects are the same from the equals method side (their id are from 0 to list_size range).
Or add some unique modifier to compare in equal, like add the position of parent list
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BeanSampleList other = (BeanSampleList) obj;
if (id != other.id)
return false;
if (listId != other.listId)
return false;
return true;
}
You need to find a different way to uniquely identify each BeanSampleList element. Just matching id is not good enough. Perhaps you could also match the titles if you know they will be unique. Or generate unique Ids for each of these elements say by dividing the integer domain in different ranges e.g. 1 to 1000000 is for category 1, 1000001 to 2000000 for category 2 and so on.
Related
In my project, there is need of searching data from server using keyword. After search, i am displaying results using RecyclerView . While searching fast, the data in RecyclerView is duplicating. If searching slowly, it's working fine. Any suggestions are appreciated. Thank you.
The below code for making server call:
private void callSearchUserApi(final String searchText, int currentPage, boolean clearData) {
isApiCallInProcess = true;
String URL = "userinfo/api/v1/user-search/" + "?page=" + currentPage;
if (!Connectivity.isConnected(activity)) {
Common.snackBarNoConnection(activity, activity.getString(R.string.no_conection));
//setOnProgressbarVisibility(View.GONE);
return;
}
if (clearData) {
globalSearchUsersModelList.clear();
//BS globalSearchUserResultsAdapter.notifyDataSetChanged();
}
ApiInterface apiCall = ApiClient.getApiService(activity);
final Call<SearchUsersModel> globalUserSearchApiCall = apiCall.searchUser(
URL,
searchText);
globalUserSearchApiCall.enqueue(new Callback<SearchUsersModel>() {
#Override
public void onResponse(Call<SearchUsersModel> call, Response<SearchUsersModel> response) {
if (response.isSuccessful() && response.body().getStatus().equalsIgnoreCase(Common.SUCCESS_RESPONSE)) {
//BS globalSearchUsersModelList.addAll(response.body().getData().getData());
for (int i = 0; i < response.body().getData().getData().size(); i++) {
SearchUsersModel.DataBeanX.DataBean dataBean = new SearchUsersModel.DataBeanX.DataBean();
dataBean.setDesignation(response.body().getData().getData().get(i).getDesignation());
dataBean.setFull_name(response.body().getData().getData().get(i).getFull_name());
dataBean.setGender(response.body().getData().getData().get(i).getGender());
dataBean.setId(response.body().getData().getData().get(i).getId());
dataBean.setPlace(response.body().getData().getData().get(i).getPlace());
dataBean.setProfile_pic(response.body().getData().getData().get(i).getProfile_pic());
globalSearchUsersModelList.add(dataBean);
/*BS if (!globalSearchUsersModelList.contains(response.body().getData().getData().get(i)))
globalSearchUsersModelList.add(response.body().getData().getData().get(i));*/
}
CURRENT_PAGE = response.body().getData().getPage();
isLoading = false;
if (response.body().getData().isNext() == false)
isLastPage = true;
else
isLastPage = false;
if (globalSearchUsersModelList.size() == 0) {
rv_GlobalsearchList.setVisibility(View.GONE);
rl_placeholderGSPeople.setVisibility(View.VISIBLE);
tv_placeholderGSPeople.setText(activity.getString(R.string.no_search_found) + " " + searchText);
} else {
rv_GlobalsearchList.setVisibility(View.VISIBLE);
rl_placeholderGSPeople.setVisibility(View.GONE);
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
globalSearchUserResultsAdapter.notifyDataSetChanged();
}
});
}
if (searchTextsList.size() > 0) {
String sText = searchTextsList.get(0);
searchTextsList.remove(0);
callSearchUserApi(sText, FIRST_PAGE, true);
} else
isApiCallInProcess = false;
}
#Override
public void onFailure(Call<SearchUsersModel> call, Throwable t) {
isApiCallInProcess = false;
}
});
}
This is my Adapter.
public class GlobalSearchUserResultsAdapter extends RecyclerView.Adapter<GlobalSearchUserResultsAdapter.SearchViewHolder> {
private Context context;
private List<SearchUsersModel.DataBeanX.DataBean> searchUserList;
public GlobalSearchUserResultsAdapter(Context context, List<SearchUsersModel.DataBeanX.DataBean> searchUserList){
this.context = context;
this.searchUserList = searchUserList;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.v("Search", "Adapter Activity : "+context);
View view = LayoutInflater.from(context).inflate(R.layout.global_search_row, parent, false);
return new GlobalSearchUserResultsAdapter.SearchViewHolder(view);
}
#Override
public void onBindViewHolder(GlobalSearchUserResultsAdapter.SearchViewHolder holder, int position) {
if ( searchUserList.get(position).getGender().equals("M")) {
holder.iv_userImage.setBackgroundResource(R.drawable.white_border_with_circle_appblue);
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
/*searchUsersModel*/searchUserList.get(position).getProfile_pic(),
R.drawable.male,
true);
} else if (searchUserList.get(position).getGender().equals("F")) {
holder.iv_userImage.setBackgroundResource(R.drawable.white_border_with_circle_pink);
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
searchUserList.get(position).getProfile_pic(),
R.drawable.female,
true);
} else {
Common.setGlideImage((GlobalSearchActivity)context,
holder.iv_userImage,
searchUserList.get(position).getProfile_pic(),
R.drawable.deafult_profilepic,
true);
}
holder.tv_userName.setText(searchUserList.get(position).getFull_name());
holder.tv_userName.setTypeface(Common
.getFontTypeface(context, GlobalConstants.FONT_AVENIR_MEDIUM));
holder.tv_place.setText(searchUserList.get(position).getPlace());
holder.tv_place.setTypeface(Common
.getFontTypeface(context, GlobalConstants.FONT_AVENIR_MEDIUM));
holder.designation.setText(searchUserList.get(position).getDesignation());
}
#Override
public int getItemCount() {
return searchUserList.size();
}
public class SearchViewHolder extends RecyclerView.ViewHolder{
private ImageView iv_userImage;
private TextView tv_userName;
private TextView tv_place;
private TextView designation;
public SearchViewHolder(View itemView) {
super(itemView);
this.iv_userImage = (ImageView) itemView.findViewById(R.id.imageSearch);
this.tv_userName = (TextView) itemView.findViewById(R.id.nameSearch);
tv_userName.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_MEDIUM));
this.designation = (TextView) itemView.findViewById(R.id.designation);
designation.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_MEDIUM));
this.tv_place = (TextView) itemView.findViewById(R.id.placeSearch);
tv_place.setTypeface(Common.getFontTypeface(context,
GlobalConstants.FONT_AVENIR_LIGHT));
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
context.startActivity(new Intent(context, ProfileActivity.class)//ThirdParty
.putExtra(GlobalConstants.KEY_THIRD_PARTY_ID, searchUserList.get(getAdapterPosition()).getId()));
}
});
}
}
}
You just had to clear the globalSearchUsersModelList list just before for loop, because API call is asynchronous.
globalSearchUsersModelList.clear();// Important one
for (int i = 0; i < response.body().getData().getData().size(); i++) {
// do your stuff
}
I think the issue come from your getItemId implementation. The way you implement it, the recycler view will identify an item according to its position in the list.
If you change this and use unique identification for instance searchUserList.get(position).id (if your User object has an unique ID) the problem should be fixed
You can also add in your activity adapter.setHasStableIds(true)
I'm facing a bug while displaying some data through ListView with ArrayAdapter.
I have Order instances in my data defined by its ID.
The ListView displays it through a TextView with the ID.
The problem is that the first (and always the first) instance has its ID written 3 times.
ex:
Order n°111
Order n°2
Order n°3
It's not my first experience with ListViews and adapters and I've never had such a disagreement.
public class OrderAdapter extends ArrayAdapter<Order> {
public OrderAdapter(Context context, ArrayList<Order> orders) {
super(context, 0, orders);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Order order = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.order, parent, false);
}
TextView orderCheckedTextView = (TextView)convertView.findViewById(R.id.orderCheckedTextView);
System.out.println("###"+order.getId());
orderCheckedTextView.setText(orderCheckedTextView.getText()+Integer.toString(order.getId()));
return convertView;
}}
Here the print returns:
###1 ###1 ###1 ###2
Then I create 3 Orders add it to the data set and set the adapter to the listView:
Order order1 = new Order(Type.LIVRAISON,State.PREPARATION, PaymentType.CB, articleList, null);
Order order2 = new Order(Type.EMPORTE,State.PREPARATION, PaymentType.TICKET, articleList, null);
Order order3 = new Order(Type.EMPORTE,State.PREPARATION, PaymentType.TICKET, articleList, null);
parentActivity.model.getOrderlist().addOrder(order1);
parentActivity.model.getOrderlist().addOrder(order2);
parentActivity.model.getOrderlist().addOrder(order3);
this.inProgressOrderAdapter = new OrderAdapter(parentActivity.getApplicationContext(), parentActivity.model.getOrderlist().getInProgressOrderList());
final ListView inProgressOrderListView = (ListView)parentActivity.findViewById(R.id.inProgressOrderListView);
inProgressOrderListView.setAdapter(inProgressOrderAdapter);
public class Order {
private OrderList orderList= null;
private static int staticID = 0;
private int id = 0;
private Type type = null;
private PaymentType paymentType = null;
private State state = null;
private Date time = null;
private ArrayList<Object> articleList = new ArrayList<>();
private double totalPrice = 0;
private Deliver deliver = null;
public Order(Type type, State state, PaymentType paymentType, ArrayList<Object> articleList, Deliver deliver) {
staticID++;
this.id = staticID;
this.type = type;
this.state = state;
this.paymentType = paymentType;
this.articleList = articleList;
this.deliver = deliver;
for(Object article:articleList){
if(article instanceof Article){
totalPrice += ((Article)article).getPrice();
}
else if(article instanceof Menu){
totalPrice += ((Menu)article).getPrice();
}
}
}
public OrderList getOrderList() {
return orderList;
}
public void setOrderList(OrderList orderList) {
this.orderList = orderList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
if(state.equals(State.LIVRE) || state.equals(State.PREPARE))
this.orderList.switchOrderList(this);
}
public PaymentType getPaymentType() {
return paymentType;
}
public void setPaymentType(PaymentType paymentType) {
this.paymentType = paymentType;
}
public Date getTime() {
return this.time;
}
public ArrayList<Object> getArticleList() {
return articleList;
}
public void setArticleList(ArrayList<Object> articleList) {
this.articleList = articleList;
}
public void addArticle(Object article){
if(article instanceof Article || article instanceof Menu){
Boolean removed = this.articleList.add(article);
if(article instanceof Article && removed){
this.totalPrice += ((Article) article).getPrice();
}
else if(article instanceof Menu && removed){
this.totalPrice += ((Menu) article).getPrice();
}
}
}
public void removeArticle(Object article){
if(article instanceof Article || article instanceof Menu){
Boolean removed = this.articleList.remove(article);
if(article instanceof Article && removed){
this.totalPrice -= ((Article) article).getPrice();
}
else if(article instanceof Menu && removed){
this.totalPrice -= ((Menu) article).getPrice();
}
}
}
public double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(double totalPrice) {
this.totalPrice = totalPrice;
}
public Deliver getDeliver() {
return deliver;
}
public void setDeliver(Deliver deliver) {
this.deliver = deliver;
}}
public enum PaymentType {
INTERNET, CB, ESPECE, TICKET;}
Thanks
Problem is with line where you declared your private static int staticID = 0;
for each object creation of Order class and instance of order is created with all its memeber variable and static variable so for each order object this staticId = 0 at the time of object creation. Suppose your created first object Order1 here staticId = 0; and in custructor you incremented it to 1 so for order1 id= 1
2- Now for order2 Object created again this instance have staticId value = 0 not 1 (if you think i incremented it to 1 at the time of first order creation than you are taking static as wrong understanding bcs when ever a new object created all its member variable again created with it) so for order2 objects constructor you increment it 0 to 1 so for order2 id is again set to 1 not 2.
3- order3 continue same as order2.
Now solution is pass your id value in the constructor at the time of object creation like below:
Order order1 = new Order(1,Type.LIVRAISON,State.PREPARATION, PaymentType.CB, articleList, null);
Order order2 = new Order(2,Type.EMPORTE,State.PREPARATION, PaymentType.TICKET, articleList, null);
Order order3 = new Order(3,Type.EMPORTE,State.PREPARATION, PaymentType.TICKET, articleList, null);
parentActivity.model.getOrderlist().addOrder(order1);
parentActivity.model.getOrderlist().addOrder(order2);
parentActivity.model.getOrderlist().addOrder(order3);
And increase one parameter in Order Constructor:
public Order(int **orderid**,Type type, State state, PaymentType paymentType, ArrayList<Object> articleList, Deliver deliver) {
staticID++; **// dont use this staticId**
this.id = orderid;
this.type = type;
this.state = state;
this.paymentType = paymentType;
this.articleList = articleList;
this.deliver = deliver;
for(Object article:articleList){
if(article instanceof Article){
totalPrice += ((Article)article).getPrice();
}
else if(article instanceof Menu){
totalPrice += ((Menu)article).getPrice();
}
}
}
public RecyclerView.Adapter mAdapter;
public RecyclerView mMessagesView;
this is my list
public List<MessageList> Message_List = new ArrayList<MessageList>();
and this is my adapter
mAdapter = new MessageAdapter1(getApplicationContext(), Message_List);
mMessagesView = (RecyclerView) findViewById(R.id.messages);
mMessagesView.setLayoutManager(new LinearLayoutManager(this));
mMessagesView.setAdapter(mAdapter);
now i add some data on it
Message_List.add(messageList);
mAdapter.notifyItemInserted(Message_List.size() - 1);
its work without problems
now i want to change all Message_List data and show in in recyvlerView
i do this
public void swap(List<MessageList> datas){
Message_List.clear();
Message_List.addAll(datas);
mAdapter.notifyDataSetChanged();
}
here i replace all data in Message_List and notifyDataSetChanged now its shown new data of Message_List
empty Message_List show blank
but when i try add new item to list and notify adapter its shown to me some of old item i removed it from Message_List and in LOG i watch it no old data there
now this is image before update Message_List data
now change Message_List data and notify
like we see blank because there are no data on Message_List
now try add new item to Message_List and notify adapter
Message_List.add(balabala data);
mAdapter.notifyItemInserted(Message_List.size() - 1);
and result must be without old data
for more information this is my MessageList class
public class MessageList {
public static final int TYPE_MESSAGE_RIGHT = 0;
public static final int TYPE_MESSAGE_LEFT = 1;
public static final int TYPE_ACTION = 2;
public static final String TYPING = "typing";
private String message, thumbnailUrl;
private String date;
private int user_id;
private boolean status = false;
private String fname;
private String direction;
private ArrayList<String> messageList;
public MessageList() {
setMessageStatus(status);
}
public MessageList(String name, String thumbnailUrl, String date, String direction,
ArrayList<String> messageList, String fname, int user_id) {
this.message = name;
this.thumbnailUrl = thumbnailUrl;
this.date = date;
this.direction = direction;
this.messageList = messageList;
this.fname = fname;
this.user_id = user_id;
}
public String getMessage() {
return message;
}
public String getFname() {
return fname;
}
public void setMessage(String message) {
this.message = message;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
public String getDate() {
return date;
}
public boolean getMessageStatus() {
return status;
}
public void setMessageStatus(boolean status) {
this.status = status;
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public void setDate(String date) {
this.date = date;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getDir() {
return direction;
}
public void setDir(String direction) {
this.direction = direction;
}
public ArrayList<String> getMessageList() {
return messageList;
}
public void setMessageList(ArrayList<String> messageList) {
this.messageList = messageList;
}
}
and this is my adapter
public class MessageAdapter1 extends RecyclerView.Adapter<MessageAdapter1.ViewHolder> {
private List<MessageList> mMessages;
private int[] mUsernameColors;
private Context context;
public MessageAdapter1(Context context, List<MessageList> messages) {
mMessages = messages;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout = -1;
int type = -1;
if(mMessages.get(viewType).getDir().equals("left")) type = 1;
else if(mMessages.get(viewType).getDir().equals("right")) type = 0;
else if(mMessages.get(viewType).getDir().equals("typing")) type = 2;
switch (type) {
case MessageList.TYPE_MESSAGE_RIGHT:
layout = R.layout.right_message;
break;
case MessageList.TYPE_MESSAGE_LEFT:
layout = R.layout.left_message;
break;
case MessageList.TYPE_ACTION:
layout = R.layout.message_left;
break;
}
View v = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
MessageList message = mMessages.get(position);
viewHolder.setGroupMessage(message);
}
#Override
public int getItemCount() {
return mMessages.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout groupMessage;
//private ImageView typing;
public ViewHolder(View itemView) {
super(itemView);
groupMessage = (LinearLayout)itemView.findViewById(R.id.messages);
}
public void setGroupMessage(MessageList m) {
if (null == groupMessage) return;
int i = 0;
if(m.getMessageStatus() == false){
m.setMessageStatus(true);
for (String message : m.getMessageList()) {
//TextView text = new TextView(activity);
TextView text = new MyTextView(context);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
p.setMargins(0, 0, 0, 2);
if (m.getDir().equals("left")) {
text.setTextColor(Color.BLACK);
p.gravity = Gravity.LEFT;
if(m.getMessageList().size() == 1){
text.setBackgroundResource(R.drawable.message_left_default);
}
else if (i == 0) {
text.setBackgroundResource(R.drawable.message_left_first);
} else if (i + 1 == m.getMessageList().size()) {
text.setBackgroundResource(R.drawable.message_left_last);
} else {
text.setBackgroundResource(R.drawable.message_left);
}
} else{
p.gravity = Gravity.RIGHT;
text.setTextColor(Color.WHITE);
if(m.getMessageList().size() == 1){
text.setBackgroundResource(R.drawable.message_right_default);
}
else if (i == 0) {
text.setBackgroundResource(R.drawable.message_right_first);
} else if (i + 1 == m.getMessageList().size()) {
text.setBackgroundResource(R.drawable.message_right_last);
} else {
text.setBackgroundResource(R.drawable.message_right);
}
}
text.setLayoutParams(p);
text.setText(message);
text.setPadding(8, 8, 8, 8);
text.setTextSize(18f);
//text.setTextAppearance(context, android.R.style.TextAppearance_Large);
groupMessage.addView(text);
i++;
}
}
}
}
}
the problem is there is no connection between mMessages and Message_List. To achieve this, put swap() in the adapter class and call it from the activity or fragment.
public class MessageAdapter1 extends RecyclerView.Adapter<MessageAdapter1.ViewHolder> {
...
public void swap(List<MessageList> datas){
mMessages.clear();
mMessages.addAll(datas);
notifyDataSetChanged();
}
public void add(MessageList data){
mMessages.add(data);
notifyItemInserted(mMessages.size() - 1);
}
}
and from fragment or activity:
((MessageAdapter1) rv.getAdapter).add(data);
((MessageAdapter1) rv.getAdapter).swap(Message_List);
I inserted/removing from particular position in ArrayList onBindViewHolder . Now , i want to show this modified list on recyclerview .
Adapter Code:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyAdapterViewHolder> {
private List<Info> dataList;
private Context mAct;
private List<Info> totalCandidatesList;
private String TAG = "OWD";
public MyAdapter(List<Info> dataList, Context context) {
this.dataList = dataList;
this.mAct = context;
}
public void addApplications(List<Info> candidates) {
if(this.totalCandidatesList == null){
totalCandidatesList = new ArrayList<>();
}
this.dataList.addAll(candidates);
this.totalCandidatesList.addAll(candidates);
this.notifyItemRangeInserted(0, candidates.size() - 1);
}
public void clearApplications() {
int size = this.dataList.size();
if (size > 0) {
for (int i = 0; i < size; i++) {
dataList.remove(0);
totalCandidatesList.remove(0);
}
this.notifyItemRangeRemoved(0, size);
}
}
#Override
public int getItemCount() {
return dataList.size();
}
public void onBindViewHolder(MyAdapterViewHolder mAdapterViewHolder, int i) {
if (i % 2 == 1) {
mAdapterViewHolder.cardView.setCardBackgroundColor(Color.parseColor("#ecf5fe"));
mAdapterViewHolder.layoutRipple.setBackgroundColor(Color.parseColor("#ecf5fe"));
} else {
mAdapterViewHolder.cardView.setCardBackgroundColor(Color.parseColor("#e2f1ff"));
mAdapterViewHolder.layoutRipple.setBackgroundColor(Color.parseColor("#e2f1ff"));
}
final WorkHolders workHolders = SingleTon.getInstance().getWorkHolders();
final String customerName = SingleTon.getInstance().getCustomerName();
String siteName = null;
if(customerName !=null) {
String[] sitenamearray = customerName.split("--");
if (sitenamearray.length > 1) {
siteName = sitenamearray[1];
}
}
final Info ci = dataList.get(i);
mAdapterViewHolder.title.setText(ci.heading1);
mAdapterViewHolder.jobNumber.setText(ci.heading2);
mAdapterViewHolder.distance.setText(ci.distance);
if(siteName != null && siteName.equalsIgnoreCase(ci.heading2)) {
mAdapterViewHolder.cardView.setCardBackgroundColor(Color.parseColor("#a7ffeb"));
mAdapterViewHolder.layoutRipple.setBackgroundColor(Color.parseColor("#a7ffeb"));
if(i!=0){
> //Here i removed and inserted item in list .
> dataList.remove(i);
> dataList.add(0,ci);
}
}
final String finalSiteName = siteName;
final Bundle bundle = new Bundle();
mAdapterViewHolder.layoutRipple.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment fragment;
String name = ci.heading1 + "--" + ci.heading2;
Log.d(TAG,"new Jobname : "+ name);
if (finalSiteName == null || finalSiteName.equalsIgnoreCase("")) {
bundle.putString("name", customerName);
bundle.putString("oldwork", "yes");
bundle.putString("running_job_selected", "yes");
} else {
Log.d(TAG,"StartedOn Before Sending Bundle :" + workHolders.startedOn);
Log.d(TAG, "running Job is not selected");
bundle.putString("name", name);
bundle.putString("oldwork", "yes");
bundle.putString("running_job_selected", "no");
}
FragmentTransaction ft = ((FragmentActivity) mAct).getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.glide_fragment_horizontal_in, R.anim.glide_fragment_horizontal_out);
fragment = new WorkDescriptionFragment();
fragment.setArguments(bundle);
ft.addToBackStack("myadapter");
ft.replace(R.id.content_frame, fragment).commit();
SingleTon.getInstance().setWorkStatus("start");
}
});
}
#Override
public MyAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.single_item1, viewGroup, false);
return new MyAdapterViewHolder(itemView);
}
public static class MyAdapterViewHolder extends RecyclerView.ViewHolder {
protected TextView title;
protected TextView dateTime;
protected TextView distance;
protected TextView jobNumber;
protected CardView cardView;
protected LayoutRipple layoutRipple;
public MyAdapterViewHolder(View v) {
super(v);
title = (TextView) v.findViewById(R.id.title);
dateTime = (TextView) v.findViewById(R.id.dateTimeTextView);
distance = (TextView) v.findViewById(R.id.distanceTextView);
jobNumber = (TextView) v.findViewById(R.id.jobNumber);
cardView = (CardView) v.findViewById(R.id.cv);
layoutRipple = (LayoutRipple)v.findViewById(R.id.singleitemripple);
}
}
}
you will see following lines in above code where i am removing/inserting item in a list onBindview and would like to show same in recyclerview .
But right now i am getting normal datalist(unchanged) .
//Here i removed and inserted item in list .
dataList.remove(i);
dataList.add(0,ci);
Please help me to achieve this .
onBindViewHolder is not the place where you should update your adapter. The staregy is to update item inside your Adapter data list and then notifyDataChanged(). For example thise are the methods for updating info inside my adapter:
public void update(Track track) {
tracks.remove(track);
add(track);
}
public void add (Track track) {
tracks.add(track);
this.notifyDataSetChanged();
}
public void addTracks(List<Track> tracks){
this.tracks.addAll(tracks);
this.notifyDataSetChanged();
}
public void clearAndAddTracks(List<Track> tracks) {
for (int i = 0; i < this.tracks.size(); i++) {
if (!this.tracks.get(i).isRunning()){
}
}
this.tracks.clear();
this.tracks.addAll(tracks);
this.notifyDataSetChanged();
}
I have an app that allows the user to select an option and a certain list is displayed in a listview. I'm having issues with getting it to save and restore state. I have a list_mode that gets set depending on which option the user selects. so far I have it saving the state of the adapter but restoring it is causing an issue ( I know it's saving because when I examine customAdapter.onRestoreInstanceState(savedInstanceState) in the onCreate it's showing the right items based on selection but I'm having a hard time restoring it to the list view. It will crash on restore. It crashes on getAllItems.clear and .addAll. I have even hard coded this to one of the lists but it didn't work either. When I comment that line out then it crashes in the main activity. I'm a noob when it comes to Java and Android and am still learning. I do not know how close or how far off I am. Can anyone provide insight on what I'm doing wrong and how I can get it to work? Thanks
List Adapter:
public class ListAdapter extends BaseAdapter {
private static final String KEY_ADAPTER_STATE = "ListAdapter.KEY_ADAPTER_STATE";
public enum ListMode {
IMAGES_AND_TEXT,
IMAGES_ONLY,
TEXT_ONLY
}
private ListMode mListMode = ListMode.IMAGES_AND_TEXT;
private ArrayList<Item> mItems;
private ArrayList<Item> mImages;
private ArrayList<Item> mTexts;
#Override
public int getCount() {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems == null ? 0 : mItems.size();
case IMAGES_ONLY:
return mImages == null ? 0 : mImages.size();
case TEXT_ONLY:
return mTexts == null ? 0 : mTexts.size();
}
return 0;
}
#Override
public Item getItem(int position) {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems == null ? null : mItems.get(position);
case IMAGES_ONLY:
return mImages == null ? null : mImages.get(position);
case TEXT_ONLY:
return mTexts == null ? null : mTexts.get(position);
}
return null;
}
public ArrayList getAllItems() {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems;
case IMAGES_ONLY:
return mImages;
case TEXT_ONLY:
return mTexts;
}
return null;
}
#Override
public long getItemId(int position) {
return position; // not really used
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = null;
TextView tn = null;
ImageView img = null;
if (convertView == null) {
LayoutInflater vi;
vi = LayoutInflater.from(parent.getContext());
v = vi.inflate(R.layout.list, null);
} else {
v=convertView;
}
Item p = getItem(position);
tn = (TextView) v.findViewById(R.id.tvText);
img = (ImageView) v.findViewById(R.id.thumbnail);
if (p.getmType().equals("image")) {
img.setVisibility(View.VISIBLE);
Picasso.with(parent.getContext()).load(p.getmData()).error((R.drawable.placeholder_error)).placeholder(R.drawable.placeholder).resize(90,0).into(img);
tn.setText("ID: " + p.getmID()+"\nTYPE: " + p.getmType() +"\nDate: " + p.getmDate()+ "\nImage URL: " + p.getmData());
} else {
img.setVisibility(View.GONE);
tn.setText("ID: " + p.getmID()+"\nTYPE: " + p.getmType() +"\nDate: " + p.getmDate()+ "\nText Data: " + p.getmData());
}
return v;
}
public void setListMode(ListMode listMode) {
mListMode = listMode;
notifyDataSetChanged();
}
public void setItems(JSONArray jsonArray) throws JSONException {
mItems = new ArrayList<>();
mImages = new ArrayList<>();
mTexts = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
Item item = new Item((JSONObject) jsonArray.get(i));
mItems.add(item);
if (item.getmType().equals("image")) {
mImages.add(item);
}
if (item.getmType().equals("text")) {
mTexts.add(item);
}
}
notifyDataSetChanged();
}
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putParcelableArrayList("list_items", getAllItems());
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.containsKey("list_items")) {
//ArrayList<Item> objects = savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
//getAllItems().clear();
//getAllItems().addAll(objects);
//mImages.clear();
//mImages.addAll(objects);
}
}
}
Main Activity:
myListView = (ListView) findViewById(R.id.listViewID);
customAdapter = new ListAdapter();
if(savedInstanceState!=null) {
//When I put a breakpoint here I see it's getting the correct list
customAdapter.onRestoreInstanceState(savedInstanceState);
}
if (savedInstanceState != null && savedInstanceState.containsKey("list_items")) {
//When I remove the .clear and .addAll it then crashes on this line
myListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_items"));
myListView.setAdapter(customAdapter);
}
else{
if (isNetworkAvailable()) {
getData theJsonData = new getData();
theJsonData.execute();
}
else{
Toast.makeText(getApplicationContext(), "No internet connection", Toast.LENGTH_SHORT).show();
tvNoInet.setVisibility(View.VISIBLE);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//Not sure what I should put here or if what I have is right
customAdapter.onSaveInstanceState(savedInstanceState);
}
//This is the code that sets the list_mode when the user selects an option
if (id == R.id.all) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_AND_TEXT);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.images) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.text) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.TEXT_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
And my Item class:
public class Item implements Parcelable{
private String mID;
private String mType;
private String mDate;
private String mData;
public Item(String mID,String mType, String mDate, String mData) {
this.mType = mType;
this.mID = mID;
this.mDate = mDate;
this.mData = mData;
}
public Item(JSONObject jsonItem) throws JSONException {
String itemID=null;
String itemType=null;
String itemDate=null;
String itemData=null;
if (jsonItem.has("id")) {
itemID=jsonItem.getString("id");
}
if (jsonItem.has("type")) {
itemType=jsonItem.getString("type");
}
if (jsonItem.has("date")){
itemDate=jsonItem.getString("date");
}
if (jsonItem.has("data")){
itemData=jsonItem.getString("data");
}
this.mID=itemID;
this.mType=itemType;
this.mDate=itemDate;
this.mData=itemData;
}
protected Item(Parcel in) {
String[] data = new String[4];
in.readStringArray(data);
this.mID = data[0];
this.mType = data[1];
this.mDate = data[2];
this.mData = data[3];
}
public static final Creator<Item> CREATOR = new Creator<Item>() {
#Override
public Item createFromParcel(Parcel in) {
return new Item(in);
}
#Override
public Item[] newArray(int size) {
return new Item[size];
}
};
public String getmID() {
return mID;
}
public String getmType() {
return mType;
}
public String getmDate() {
return mDate;
}
public String getmData() {
return mData;
}
#Override
public String toString() {
return "{" +
"ID='" + mID + '\'' +
", Type='" + mType + '\'' +
", Date='" + mDate + '\'' +
", Data='" + mData + '\'' +
'}';
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
String[] data = new String[4];
data[0] = mID;
data[1] = mType;
data[2] = mDate;
data[3] = mData;
dest.writeStringArray(data);
}
}
UPDATE: This is what the code looks like now. When I select a certain list and then rotate the screen it's working. It's staying on the currently selected list. Now the issue is when they select a new list after the device has been rotated the listview is completely blank
if(savedInstanceState!=null) {
customAdapter.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey(KEY_LIST_VIEW_STATE)) {
myListView.onRestoreInstanceState(savedInstanceState.getParcelable(KEY_LIST_VIEW_STATE));
myListView.setAdapter(customAdapter);
}
}
else{
if (isNetworkAvailable()) {
getData theJsonData = new getData();
theJsonData.execute();
tvNoInet.setVisibility(View.GONE);
btnRetry.setVisibility(View.GONE);
} else {
Toast.makeText(getApplicationContext(), "No internet connection", Toast.LENGTH_SHORT).show();
tvNoInet.setVisibility(View.VISIBLE);
btnRetry.setVisibility(View.VISIBLE);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
customAdapter.onSaveInstanceState(savedInstanceState);
savedInstanceState.putParcelable(KEY_LIST_VIEW_STATE, myListView.onSaveInstanceState());
}
and my updated list adapter:
private ListMode mListMode = ListMode.IMAGES_AND_TEXT;
private ArrayList<Item> mItems= new ArrayList<>();
private ArrayList<Item> mImages= new ArrayList<>();
private ArrayList<Item> mTexts= new ArrayList<>();
public void onSaveInstanceState(Bundle savedInstanceState) {
//savedInstanceState.putParcelableArrayList("list_items", getAllItems());
savedInstanceState.putParcelableArrayList(KEY_ADAPTER_STATE, getAllItems());
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.containsKey(KEY_ADAPTER_STATE)) {
ArrayList<Item> objects = savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
getAllItems().clear();
getAllItems().addAll(objects);
//mImages.clear();
//mImages.addAll(objects);
}
}
And this is the code in the options menu that switches the list. If I haven't rotated the screen yet this works. When I rotate the screen and select a different list the listview is then blank. If I select "All" which should display All items then whatever list I had selected before if restored. So If I selected Images Only at first, rotate the screen everything is displayed right, if I rotate the screen back and select Text Only or Images Only again, the list is blank, if I select "All" then whatever list I picked the first time (in this case Images Only) is displayed
if (id == R.id.all) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_AND_TEXT);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.images) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.text) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.TEXT_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
Well, since I got you into this mess.... :)
Something I do in situations like this is to pass in the saved instance state Bundle to the constructor for the adapter. Since you made your Item class Parcelable, everything else should be easy.
public ListAdapter(Bundle savedInstanceState) {
if (savedInstanceState != null) {
int ordinal = savedInstanceState.getInt("adapter_mode", 0);
mListMode = ListMode.values()[ordinal];
ArrayList<Item> items =
savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
if (items != null) {
setItems(items);
}
}
}
public void onSaveInstanceState(Bundle outState) {
outState.putInt("adapter_mode", mListMode.ordinal());
outState.putParcelableArrayList(KEY_ADAPTER_STATE, mItems);
}
So with the saved instance state in the constructor, you don't need an explicit method to restore the adapter state.
public void setItems(JSONArray jsonArray) throws JSONException {
List<Item> items = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
items.add(new Item((JSONObject) jsonArray.get(i)));
}
setItems(items);
}
private void setItems(List<Item> items) {
for (Item item : items) {
mItems.add(item);
if (item.getmType().equals("image")) {
mImages.add(item);
}
if (item.getmType().equals("text")) {
mTexts.add(item);
}
}
notifyDataSetChanged();
}
Your app crashes if you call ListAdapter.getAllItems().clear() without calling ListAdapter.setItems() before
Cause: ListAdapter.getAllItems() may return null if mItems, mImages, mTexts are not initialized.
One workaround may be
public class ListAdapter extends ... {
...
private ArrayList<Item> mItems = new ArrayList<>();
private ArrayList<Item> mImages = new ArrayList<>();
private ArrayList<Item> mTexts = new ArrayList<>();
...
}