I process and setup content which is being delivered as a String which includes http links (http://..) and (can include) normal text (info, mirror, ...).
I am trying to set the normal text as headers in my recylcerview.
This all needs to happen dynamically as I do not know upfront if the String includes normal text and if it is included, how many normal text items there are.
So the recylcerview (could) should look like this:
http://www.link1.com
MIRROR
https://www.link2.com
https://www.link3.com
INFO
http://www.link4.com
http://www.link5.com
Now it looks like this:
MIRROR
http://www.link1.com
http://www.link2.com
http://www.link3.com
According the log it looks ok:
/testpackage.com E/ITEM: https://link1.com
/testpackage.com E/ITEM: MIRROR
/testpackage.com E/ITEM: https://link2.com
/testpackage.com E/ITEM: https://link3.com
I am having trouble getting the headers in the right position in the recyclerview.
I already looked up some solutions and I did read about several possibilities like SectionedRecyclerViewAdapter but I wouldn`t know how to implement it for my needs.
I guess the tricky part of my problem is it needs to be setup dynamically.
My code till now:
SectionViewHolder class:
public class SectionViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public SectionViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.sectionHeader);
}
}
Activity where I setup things:
#EActivity(R.layout.downloads_activity)
public class DownloadsActivity extends BaseActivity {
String outPut;
RecyclerView recyclerView;
AdapterSectionRecycler adapterRecycler;
List<SectionHeader> sectionHeaders;
...
#AfterViews
public void init() {
int i = 0;
List<Child> childList = new ArrayList<>();
sectionHeaders = new ArrayList<>();
//initialize RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//setLayout Manager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
...
// GET RID OF BAD HTML and SPLIT AS OF <br/>
String html = outPut.replaceAll("<br/></a>", "</a>");
String[] lines = html.split("<br/>");
...
// loop and process URL links and normal text
for (String str : Arrays.asList(lines)) {
Jsoup.parse(str).text();
str.split(",");
// function to parse the URL links and normal text from String
String item = unescapeJavaString(String.valueOf(Html.fromHtml(str)));
if (!str.contains("Use following links")) {
// Add URL links to list
if (item.startsWith("http")) {
childList.add(new Child(item));
}
// Add section headers
if (!item.startsWith("http")) {
sectionHeaders.add(new SectionHeader(childList, item, i));
}
}
Log.i("ITEM", item);
}
adapterRecycler = new AdapterSectionRecycler(this, sectionHeaders);
recyclerView.setAdapter(adapterRecycler);
}
SectionHeader class:
public class SectionHeader implements Section<Child>, Comparable<SectionHeader> {
List<Child> childList;
public String sectionText;
int index;
public SectionHeader(List<Child> childList, String sectionText, int index) {
this.childList = childList;
this.sectionText = sectionText;
this.index = index;
}
#Override
public List<Child> getChildItems() {
return childList;
}
public String getSectionText() {
return sectionText;
}
#Override
public int compareTo(SectionHeader another) {
if (this.index > another.index) {
return -1;
} else {
return 1;
}
}
}
ChildViewHolder:
public class ChildViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public ChildViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.child);
}
}
Child class:
public class Child {
String name;
public Child(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
My AdapterSectionRecycler:
public class AdapterSectionRecycler extends SectionRecyclerViewAdapter<SectionHeader, Child, SectionViewHolder, ChildViewHolder> {
Context context;
public AdapterSectionRecycler(Context context, List<SectionHeader> sectionHeaderItemList) {
super(context, sectionHeaderItemList);
this.context = context;
}
#Override
public SectionViewHolder onCreateSectionViewHolder(ViewGroup sectionViewGroup, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.section_item, sectionViewGroup, false);
return new SectionViewHolder(view);
}
#Override
public ChildViewHolder onCreateChildViewHolder(ViewGroup childViewGroup, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.child, childViewGroup, false);
return new ChildViewHolder(view);
}
#Override
public void onBindSectionViewHolder(SectionViewHolder sectionViewHolder, int sectionPosition, SectionHeader sectionHeader) {
sectionViewHolder.name.setText(sectionHeader.sectionText);
}
#Override
public void onBindChildViewHolder(ChildViewHolder childViewHolder, int sectionPosition, int childPosition, Child child) {
childViewHolder.name.setText(child.getName());
}
}
Related
I would like to update panel wise items in inner adapter item android recyclerview. When we pass the data dynamically.Data displaying is working fine. When we go to update the inner adapter item, it's not getting updated. But last item was getting update fine.
Activity.
public class PannelCreation extends AppCompatActivity {
RecyclerView userPanelRecycler;
List<String> roomPanels = new ArrayList<>();
List<JSONObject> roomItemObject = new ArrayList<JSONObject>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pannel_creation);
userPanelRecycler = findViewById(R.id.user_panel_recycler);
for(int i=0; i<=5;i++){
roomPanels.add("Panels "+i)
}
PanelAdapter panelAdapter = new PanelAdapter(getApplicationContext(),roomPanels);
userPanelRecycler.setLayoutManager(new LinearLayoutManager(this));
userPanelRecycler.setHasFixedSize(true);
userPanelRecycler.setAdapter(panelAdapter);
}
}
// OuterAdapter
class PanelAdapter extends RecyclerView.Adapter<PanelAdapter.ViewHolder>{
Context context;
List<String> roomPanelList;
RecyclerView.RecycledViewPool recycledViewPool;
List<ItemData> itemDataList = new ArrayList<>();
public PanelAdapter(Context context, List<String> roomPanels) {
this.context = context;
this.roomPanelList = roomPanels;
recycledViewPool = new RecyclerView.RecycledViewPool();
}
#NonNull
#Override
public PanelAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.panel_wise_layout,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.itemsRecycler.setRecycledViewPool(recycledViewPool);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull PanelAdapter.ViewHolder holder, int position) {
holder.userPanelName.setText(roomPanelList.get(position));
String cur_panelName = roomPanelList.get(position);
itemsAdapter = new ItemsAdapter(context);
holder.itemsRecycler.setLayoutManager(new GridLayoutManager(context,3));
holder. itemsRecycler.setHasFixedSize(true);
holder.itemsRecycler.setAdapter(itemsAdapter);
holder.itemsRecycler.setNestedScrollingEnabled(false);
try {
roomItemObject.clear();
JSONArray metaArray = new JSONArray(metaData);
int count = 0;
for(int i = 0;i<metaArray.length();i++){
JSONObject object = metaArray.getJSONObject(i);
String name = object.getString("name");
String[] rNum = name.split("_");
if(rNum[0].equalsIgnoreCase(roomNumber)){
roomItemObject.add(object);
}
count = count+1;
}
if(count == metaArray.length()){
int count1 = 0;
itemDataList.clear();
for(int i =0; i < roomItemObject.size();i++){
JSONObject itemObject1 = roomItemObject.get(i);
String groupNames = itemObject1.getString("groupNames");
String types = itemObject1.getString("type");
String metaValue = itemObject1.getString("metadata");
JSONObject panelObject = new JSONObject(metaValue);
String panel_name = panelObject.getString("panelName");
JSONObject valueObject = new JSONObject(panel_name);
String value = valueObject.getString("value");
if(value.equalsIgnoreCase(cur_panelName)){
String labels = itemObject1.getString("label");
String names = itemObject1.getString("name");
String state = itemObject1.getString("state");
String groupName = itemObject1.getString("groupNames");
String tags = itemObject1.getString("tags");
ItemData itemData = new ItemData();
itemData.setLabelName(labels);
itemData.setState(state);
itemData.setItemName(names);
itemData.setTags(tags);
itemData.setTypes(types);
itemData.setGroup(groupName);
itemDataList.add(itemData);
itemsAdapter.addItems(itemDataList);
itemsAdapter.notifyDataSetChanged();
}
}
}
}catch (JSONException e){
e.printStackTrace();
}
}
#Override
public int getItemCount() {
return roomPanelList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public class ViewHolder extends RecyclerView.ViewHolder{
RecyclerView itemsRecycler;
TextView userPanelName;
Button deletePanel;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemsRecycler = itemView.findViewById(R.id.panel_item_recycler);
userPanelName = itemView.findViewById(R.id.test_panel_name);
deletePanel = itemView.findViewById(R.id.delete_panel);
}
}
}
//Inner Adapter
class ItemsAdapter extends RecyclerView.Adapter<ItemsAdapter.ItemHolder>{
Context mContext;
List<ItemData> innerItemDataList = new ArrayList<>();
List<JSONObject> itemObjectList = new ArrayList<JSONObject>();
List<JSONObject> recObjectList = new ArrayList<>();
ItemData itemData;
public ItemsAdapter(Context context) {
this.mContext = context;
this.itemData = new ItemData();
}
public void addItems(List<ItemData> itemData){
this.innerItemDataList.clear();
this.innerItemDataList.addAll(itemData);
notifyDataSetChanged();
}
#NonNull
#Override
public ItemsAdapter.ItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.panel_wise_item,parent,false);
return new ItemHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ItemHolder holder, int position) {
itemData = innerItemDataList.get(position);
if(itemData != null){
holder.itemNames.setText(itemData.getLabelName());
}
#Override
public int getItemCount() {
return innerItemDataList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public class ItemHolder extends RecyclerView.ViewHolder {
TextView itemNames;
LinearLayout itemLayout;
ImageView itemImg;
public ItemHolder(#NonNull View itemView) {
super(itemView);
itemNames = itemView.findViewById(R.id.panel_item_name);
itemLayout = itemView.findViewById(R.id.panel_light_linear);
itemImg = itemView.findViewById(R.id.panel_item_img);
}
}
public void updateItem(String itemName,String state){
for(int j=0;j<innerItemDataList.size();j++){
if(itemName.equalsIgnoreCase(innerItemDataList.get(j).getItemName())){
innerItemDataList.get(j).setState(state);
notifyDataSetChanged();
}
}
}
}
//Inner adapter data model class
public class ItemData {
String labelName;
String itemName;
String state;
String group;
String tags;
String types;
public String getTypes() {
return types;
}
public void setTypes(String types) {
this.types = types;
}
public String getTags() {
return tags;
}
public void setTags(String tags) {
this.tags = tags;
}
public String getLabelName() {
return labelName;
}
public void setLabelName(String labelName) {
this.labelName = labelName;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}
How to update specific item in inner adapter android nested recycler view.Can anyone guide me how to deal with update specific item. One more thing have observed inner adapter returns last position of array list only. Items are updating fine in last position of array list. When trying to updating the rest of position array list items, not getting updated.
Thanks in Advance.
Amar.
In outer adapter when ever data changes need to notify. Try this code
panelAdapter.notifyDataSetChanged();
I have 2 view models observing 2 tables in room each emitting live data, they should update my recycler view when a value changes. My adapter is equipped to handle more than one model and view holder, but I'm not sure how to update the recycler views adapter with new data without overwriting the current data or duplicating any data any ideas?
So my adapter takes a list of Visitable (Visitable pattern)
I have 2 objects that implement this interface, the interface has a type so I can tell what view holder it wants and I update the recycler view using diff utils, it look like this
public class CardAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private final List<Visitable> elements;
private final TypeFactory typeFactory;
private final ItemTouchListener onItemTouchListener;
private final Context context;
private String cardType;
private final String layoutIdentifier;
private static final String TAG = "Adptr-Card";
private String CARD_CLICK_UPDATE = "card_click_update";
private final String[] imageFilePathNames;
private RequestManager glide;
public CardAdapter(List<Visitable> elements, TypeFactory typeFactory, ItemTouchListener onItemTouchListener,
Context context,
String cardType, String layoutIdentifier, RequestManager glide) {
this.glide = glide;
this.elements = elements;
this.typeFactory = typeFactory;
this.onItemTouchListener = onItemTouchListener;
this.context = context;
this.cardType = cardType;
this.layoutIdentifier = layoutIdentifier;
this.imageFilePathNames = context.getResources().getStringArray(R.array.image_set_names);
}
#NonNull
#Override
public BaseViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View contactView = LayoutInflater.from(context).inflate(viewType, parent, false);
return typeFactory.createViewHolder(contactView, viewType, onItemTouchListener, glide, cardType);
}
#Override
public void onBindViewHolder(#NonNull BaseViewHolder holder, int position) {
holder.bind(elements.get(position), position);
}
#Override
public int getItemViewType(int position) {
return elements.get(position).type(typeFactory);
}
public void setCardType(String cardType) {
this.cardType = cardType;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return elements.size();
}
public List<Visitable> getList() {
return elements;
}
public List<Sentence> getSentencesList() {
ArrayList<Sentence> sentences = new ArrayList<>();
for (Visitable visitable : elements) {
if (visitable.type(typeFactory) == CardViewHolder.LAYOUT) {
sentences.add((Sentence) visitable);
}
}
return sentences;
}
public Visitable getItem(int position) {
if (position > 0 && position < elements.size()) {
return elements.get(position);
}
return elements.get(0);
}
class CalculateDiffUtils extends AsyncTask<Void, Void, DiffResult> {
private final List<Visitable> oldCardList;
private final List<Visitable> newCardList;
CalculateDiffUtils(List<Visitable> oldCardList, List<Visitable> newCardList) {
this.oldCardList = oldCardList;
this.newCardList = newCardList;
}
#Override
protected DiffUtil.DiffResult doInBackground(Void... params) {
return DiffUtil.calculateDiff(new VisitableDiffUtils(oldCardList, newCardList, typeFactory));
}
#Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
super.onPostExecute(diffResult);
dispatchUpdates(diffResult, newCardList);
}
}
private void dispatchUpdates(DiffUtil.DiffResult diffResult, List<Visitable> newCardList) {
this.elements.clear();
this.elements.addAll(newCardList);
diffResult.dispatchUpdatesTo(this);
}
public void refreshDiffUtilsList(List<Visitable> sentences) {
new CalculateDiffUtils(elements, sentences).execute();
}
public void removeItem(int position) {
elements.remove(position);
notifyItemRemoved(position);
}
public void addCard(Sentence sentence) {
elements.add(getItemCount(), sentence);
notifyItemInserted(getItemCount());
}
public void addGroup(GroupsWithSentences sentence) {
elements.add(getItemCount(), sentence);
notifyItemInserted(getItemCount());
}
public void updateCardClick(int position) {
notifyItemChanged(position, CARD_CLICK_UPDATE);
}
public void refreshList(List<Visitable> newElements) {
ArrayList<Visitable> elementArrayList = new ArrayList<>(newElements);
elements.clear();
elements.addAll(elementArrayList);
notifyDataSetChanged();
}
}
My 2 view models sit in a fragment, they observe some data from my Room database and are updated when changes happen, but this means I will only ever have the data from one of the view models, I guess I want a way to combine these view models maybe using some kind of mediator live data, here are my 2 view models (I've removed stuff for brevity, they are both initiated using factories)
GROUP VIEW MODEL
public class GroupViewModel extends ViewModel {
private final GroupRepository groupRepository;
private final LiveData<List<GroupsWithSentences>> groups;
public GroupViewModel(#NonNull Application application, String[] cardArgs) {
groupRepository = new GroupRepository(application);
groups = groupRepository.getGroupsByWordDescriptionAndWordType(cardArgs[0],cardArgs[1]);
}
public LiveData<List<GroupsWithSentences>> getGroups() {
return groups;
}
}
SENTENCE VIEW MODEL
public class CardViewModel extends ViewModel {
private final SentenceRepository sentenceRepository;
private final LiveData<List<Sentence>> cards;
private static final String TAG = "view_model";
public CardViewModel(#NonNull Application application , int clicks){
sentenceRepository = new SentenceRepository(application);
search = new MutableLiveData<>();
cardArgs = new MutableLiveData<>();
cards = Transformations.switchMap(search, mySearch -> sentenceRepository.searchLiveCardListByWordTypeAndWordDescriptionAndSearchWord(getCardArgs()[0],getCardArgs()[1],mySearch));
}
public LiveData<List<Sentence>> getLiveCardList(){
return cards;
}
}
CALLING ADAPTER IN MY FRAGMENT
private void setUpCardViewModelObserver(String[] args) {
cardViewModel.getLiveCardList().observe(getViewLifecycleOwner(), sentenceList -> {
if (sentenceList != null) {
ArrayList<Visitable> list = new ArrayList<>(sentenceList);
cardAdapter.refreshDiffUtilsList(list);
checkResults(list.size());
}
});
}
private void setUpGroupViewModelObserver() {
groupViewModel.getGroups().observe(getViewLifecycleOwner(), groupsWithSentencesList -> {
if (groupsWithSentencesList != null) {
ArrayList<Visitable> list = new ArrayList<>(groupsWithSentencesList);
cardAdapter.refreshDiffUtilsList(list);
checkResults(groupsWithSentencesList.size());
}
});
}
Any help is welcome, many thanks.
So the answer was to use Mediator Live data, i set the new mediator live data to respond to changes to my existing live data objects and then mediate those changes so i now only have one stream of data so my card view model now looks like this
public CardViewModel(#NonNull Application application , int clicks, String[] cardArgs){
sentenceRepository = new SentenceRepository(application);
search = new MutableLiveData<>();
cards = Transformations.switchMap(search, mySearch -> sentenceRepository.searchLiveCardListByWordTypeAndWordDescriptionAndSearchWord(cardArgs[0],cardArgs[1],mySearch));
groupRepository = new GroupRepository(application);
groups = groupRepository.getGroupsByWordDescriptionAndWordType(cardArgs[0],cardArgs[1]);
sentencesAndGroups = new MediatorLiveData<>();
sentencesAndGroups.addSource(cards, sentences -> {
sentencesAndGroups.setValue(combineLatest(sentences, groups.getValue()));
});
sentencesAndGroups.addSource(groups, groupsWithSentences -> {
sentencesAndGroups.setValue(combineLatest(cards.getValue(), groupsWithSentences));
});
}
and my new combine latest method looks like this
private List<Visitable> combineLatest(List<Sentence> sentenceList, List<GroupsWithSentences> groupsWithSentences) {
List<Visitable> visitableList = new ArrayList<>();
if (sentenceList != null){
visitableList.addAll(sentenceList);
}
if (groupsWithSentences != null){
visitableList.addAll(groupsWithSentences);
}
return visitableList;
}
I am developing an android app that displays the ranks of students based on their marks retrieved from the firebase database. Everything is working fine but, when I update the marks in the db, it keeps the old data and adds the new data in the recyclerView. I can restart the app to refresh the data. But while it is still running, it shows the old data too.
Below is my firebase data:
Student1: {
c: 70,
cPlus: 90,
java: 70,
name: "Samson",
regno: "16sksb7034",
unix: 60
}
Student2: {
c: 20,
cPlus: 85,
java: 68,
name: "Samson",
regno: "16sksb7034",
unix: 86
}
Student3: {
c: 70,
cPlus: 70,
java: 80,
name: "Samson",
regno: "16sksb7034",
unix: 90
}
Here is my dataModel class:
public class Marks {
private String name;
private String regno;
private int c;
private int cPlus;
private int java;
private int unix;
private int percentage;
public Marks() {}
public Marks(int c, int cPlus, int java, int unix) {
this.c = c;
this.cPlus = cPlus;
this.java = java;
this.unix = unix;
}
public int getPercentage() {
return percentage;
}
public void setPercentage(int percentage) {
this.percentage = percentage;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRegno() {
return regno;
}
public void setRegno(String regno) {
this.regno = regno;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
public int getcPlus() {
return cPlus;
}
public void setcPlus(int cPlus) {
this.cPlus = cPlus;
}
public int getJava() {
return java;
}
public void setJava(int java) {
this.java = java;
}
public int getUnix() {
return unix;
}
public void setUnix(int unix) {
this.unix = unix;
}
}
class MarksComparator implements Comparator<Marks> {
#Override
public int compare(Marks marks1, Marks marks2) {
int Marks1Total = marks1.getPercentage();
int Marks2Total = marks2.getPercentage();
if (Marks2Total < Marks1Total) {
return -1;
} else if (Marks2Total > Marks1Total) {
return 1;
} else {
return 0;
}
}
}
Here's my activity class:
public class MarksFragment extends Fragment{
private List<Marks> mMarksList = new ArrayList<>();
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private FirebaseDatabase mDatabase;
private DatabaseReference mReference;
private int total=0;
public MarksFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_marks, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
// specify an adapter (see also next example)
/*mAdapter = new MyAdapter(getContext(),mMarksList);
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);*/
//get Firebase Reference
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mDatabase = FirebaseDatabase.getInstance();
mReference = mDatabase.getReference();
mReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return view;
}
public void findPercentage(Marks value) {
total =value.getC() + value.getcPlus() + value.getJava() + value.getUnix();
value.setPercentage(total);
}
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
mMarksList.add(value);
Collections.sort(mMarksList, new MarksComparator());
// specify an adapter (see also next example)
mAdapter = new MyAdapter(getContext(),mMarksList);
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);
Here is my adapter class:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
private Context mContext;
private List<Marks> marksList;
public MyAdapter(Context mContext, List<Marks> marksList) {
this.mContext = mContext;
this.marksList = marksList;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mItemName, mItemRegNo, mItemNo, mTotal;
CircleImageView mImageView;
public MyViewHolder(View view) {
super(view);
mItemName = (TextView) view.findViewById(R.id.card_name);
mItemRegNo = (TextView) view.findViewById(R.id.card_regno);
mItemNo = (TextView) view.findViewById(R.id.item_id);
mImageView = (CircleImageView) view.findViewById(R.id.item_photo);
mTotal = view.findViewById(R.id.card_total);
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Marks marks = marksList.get(position);
int count = position + 1;
holder.mItemName.setText("" + marks.getName());
holder.mItemRegNo.setText("" + marks.getRegno());
holder.mItemNo.setText("" + count);
holder.mImageView.setImageResource(R.drawable.after_cookie);
holder.mTotal.setText(""+ marks.getPercentage());
}
#Override
public int getItemCount() {
return marksList.size();
}
}
So the code does what its intended to do it retrieves the data and calculates the total and ranks the students. but when I update the data in firebase console the views in recyclerView duplicates temporarily. Like for example if I update Student1 unix value as 10 then two views will be shown in the recyclerView: 1 for previous value and 2 for updated value and again if I update the values it will yet show another views representing the new data without removing the old views. But if I restart recyclerView gets refreshed and its all ok but while I am running the app during the update it shows temporary duplicate views too.
I am new here and this is my first question so I can't even upload picture as you need 10 points to upload photo. I really hope someone help me out on this. I thank you in advance.
UPDATE
Here is link to the image:
When I start the app, the image is:
first Image
when I update the unix value of Student3, the image in recyclerView becomes like this:
After updating the data in firebase console
So, you see it adds new data as well as keeps the old data untill I restart.
Your problem is that you're never checking if the student already exists in your mMarksList so you're simply duplicating him by adding him again with new grades.
What I would do in you case is to add an unique id in firebase to each student.
Then you can check in your fetchData whether the student with that id is already in the array, delete him and add the new one.
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
// Get an iterator.
Iterator<Marks> ite = mMarksList.iterator();
while(ite.hasNext()) {
Marks iteValue = ite.next();
if(iteValue.getId().equals(value.getId())) ite.remove();
}
mMarksList.add(value);
....
}
Optionally To make that even cleaner, you can override the equals and hashcode methods in your Marks data model, so that a Marks object is considered the same if the id is equal. More
//ASSUMING THAT ID IS int
#Override
public int hashCode() {
return id;
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
Marks other = (Marks) obj;
if (this.getId != other.getId) {
return false;
}
return true;
}
Then it's possible to either use a hashmap, which will override the old student automatically or a arraylist as is and iterate through it before and check if a student equals your new student, like this:
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
// Use an iterator.
Iterator<Marks> ite = mMarksList.iterator();
while(ite.hasNext()) {
Marks iteValue = ite.next();
if(iteValue.equals(value)) ite.remove();
}
mMarksList.add(value);
....
}
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)))
How to show date or today , yesterday like text in between conversation
like whatsapp
MainActivity
public class MainActivity extends AppCompatActivity {
private ChatAdapter chatAdapter;
private RecyclerView recyclerView;
private Context context;
private int loggedInUserID;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindRecyclerView();
// TODO get logged in user id and initialize into 'loggedInUserID'
}
#Override
protected void onResume() {
super.onResume();
getData();
}
private void getData() {
/**
*Your server call to get data and parse json to your appropriate model
* after parsing json to model simply call the
*/
List<ChatModel> chatModelList = ParseData.chatParser(jsonArray);
groupDataIntoHashMap(chatModelList);
}
private void bindRecyclerView() {
chatAdapter = new ChatAdapter(null);
chatAdapter.setUser(loggedInUserID);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(chatAdapter);
}
private void groupDataIntoHashMap(List<ChatModel> chatModelList) {
LinkedHashMap<String, Set<ChatModel>> groupedHashMap = new LinkedHashMap<>();
Set<ChatModel> list = null;
for (ChatModel chatModel : chatModelList) {
//Log.d(TAG, travelActivityDTO.toString());
String hashMapKey = DateParser.convertDateToString(chatModel.getChatTime());
//Log.d(TAG, "start date: " + DateParser.convertDateToString(travelActivityDTO.getStartDate()));
if (groupedHashMap.containsKey(hashMapKey)) {
// The key is already in the HashMap; add the pojo object
// against the existing key.
groupedHashMap.get(hashMapKey).add(chatModel);
} else {
// The key is not there in the HashMap; create a new key-value pair
list = new LinkedHashSet<>();
list.add(chatModel);
groupedHashMap.put(hashMapKey, list);
}
}
//Generate list from map
generateListFromMap(groupedHashMap);
}
private List<ListObject> generateListFromMap(LinkedHashMap<String, Set<ChatModel>> groupedHashMap) {
// We linearly add every item into the consolidatedList.
List<ListObject> consolidatedList = new ArrayList<>();
for (String date : groupedHashMap.keySet()) {
DateObject dateItem = new DateObject();
dateItem.setDate(date);
consolidatedList.add(dateItem);
for (ChatModel chatModel : groupedHashMap.get(date)) {
ChatModelObject generalItem = new ChatModelObject();
generalItem.setChatModel(chatModel);
consolidatedList.add(generalItem);
}
}
chatAdapter.setDataChange(consolidatedList);
return consolidatedList;
}
}
ChatModel.java
public class ChatModel implements Serializable {
private String messageId;
private int userId;
private String firstName;
private String userName;
private String message;
private Date chatTime;
//TODO generate getter and setter
}
ListObject.java (to determind the type of message)
public abstract class ListObject {
public static final int TYPE_DATE = 0;
public static final int TYPE_GENERAL_RIGHT = 1;
public static final int TYPE_GENERAL_LEFT = 2;
abstract public int getType(int userId);
}
DateObject.java
public class DateObject extends ListObject {
private String date;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
#Override
public int getType(int userId) {
return TYPE_DATE;
}
}
ChatModelObject.java
public class ChatModelObject extends ListObject {
private ChatModel chatModel;
public ChatModel getChatModel() {
return chatModel;
}
public void setChatModel(ChatModel chatModel) {
this.chatModel = chatModel;
}
#Override
public int getType(int userId) {
if (this.chatModel.getUserId() == userId) {
return TYPE_GENERAL_RIGHT;
} else
return TYPE_GENERAL_LEFT;
}
}
DateParse.java to parse date for grouping the chat
public class DateParser {
private static DateFormat dateFormat1 = new SimpleDateFormat("dd/MM/yyyy");
public static String convertDateToString(Date date) {
String strDate = "";
strDate = dateFormat1.format(date);
return strDate;
}
}
ChatAdapter.java
public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ListObject> listObjects;
private int loggedInUserId;
public ChatAdapter(List<ListObject> listObjects) {
this.listObjects = listObjects;
}
public void setUser(int userId) {
this.loggedInUserId = userId;
}
public void setDataChange(List<ListObject> asList) {
this.listObjects = asList;
//now, tell the adapter about the update
notifyDataSetChanged();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ListObject.TYPE_GENERAL_RIGHT:
View currentUserView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_chat_list_row_right, parent, false);
viewHolder = new ChatRightViewHolder(currentUserView); // view holder for normal items
break;
case ListObject.TYPE_GENERAL_LEFT:
View otherUserView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_chat_list_row_left, parent, false);
viewHolder = new ChatLeftViewHolder(otherUserView); // view holder for normal items
break;
case ListObject.TYPE_DATE:
View v2 = inflater.inflate(R.layout.date_row, parent, false);
viewHolder = new DateViewHolder(v2);
break;
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
switch (viewHolder.getItemViewType()) {
case ListObject.TYPE_GENERAL_RIGHT:
ChatModelObject generalItem = (ChatModelObject) listObjects.get(position);
ChatRightViewHolder chatViewHolder = (ChatRightViewHolder) viewHolder;
chatViewHolder.bind(generalItem.getChatModel());
break;
case ListObject.TYPE_GENERAL_LEFT:
ChatModelObject generalItemLeft = (ChatModelObject) listObjects.get(position);
ChatLeftViewHolder chatLeftViewHolder = (ChatLeftViewHolder) viewHolder;
chatLeftViewHolder.bind(generalItemLeft.getChatModel());
break;
case ListObject.TYPE_DATE:
DateObject dateItem = (DateObject) listObjects.get(position);
DateViewHolder dateViewHolder = (DateViewHolder) viewHolder;
dateViewHolder.bind(dateItem.getDate());
break;
}
}
#Override
public int getItemCount() {
if (listObjects != null) {
return listObjects.size();
}
return 0;
}
#Override
public int getItemViewType(int position) {
return listObjects.get(position).getType(loggedInUserId);
}
public ListObject getItem(int position) {
return listObjects.get(position);
}
}
ChatRightViewHolder.java for current user message
public class ChatRightViewHolder extends RecyclerView.ViewHolder {
private final String TAG = ChatRightViewHolder.class.getSimpleName();
public ChatRightViewHolder(View itemView) {
super(itemView);
//TODO initialize your xml views
}
public void bind(final ChatModel chatModel) {
//TODO set data to xml view via textivew.setText();
}
}
ChatLeftViewHolder.java for display other user messages.
public class ChatLeftViewHolder extends RecyclerView.ViewHolder {
private final String TAG = ChatRightViewHolder.class.getSimpleName();
public ChatLeftViewHolder(View itemView) {
super(itemView);
//TODO initialize your xml views
}
public void bind(final ChatModel chatModel) {
//TODO set data to xml view via textivew.setText();
}
}
DateViewHolder.java to display date
public class DateViewHolder extends RecyclerView.ViewHolder {
public DateViewHolder(View itemView) {
super(itemView);
//TODO initialize your xml views
}
public void bind(final String date) {
//TODO set data to xml view via textivew.setText();
}
}
You need to create a new ViewHolder for that purpose
For example:
// Different types of rows
private static final int TYPE_ITEM_LEFT = 0;
private static final int TYPE_ITEM_RIGHT = 1;
private static final int TYPE_ITEM_DATE_CONTAINER = 2;
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class ViewHolder0 extends RecyclerView.ViewHolder {
// Viewholder for row type 0
}
class ViewHolder1 extends RecyclerView.ViewHolder {
// Viewholder for row type 1
}
class ViewHolder2 extends RecyclerView.ViewHolder {
// Viewholder for row type 2
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder.getItemViewType() == TYPE_ITEM_LEFT) {
// Code to populate type 0 view here
} else if (viewHolder.getItemViewType() == TYPE_ITEM_RIGHT) {
// Code to populate type 1 view here
} else if (viewHolder.getItemViewType() == TYPE_ITEM_DATE_CONTAINER) {
// Code to populate type 2 view here
}
}
You just have to compare the date when scrolling and set the visibility of date view. The advantage of this is there's no hard-coded today/yesterday in data list and is able to refresh the correct date immediately (scrolling) after 12.00 a.m.
e.g. in your onBindViewHolder() in recycleview:
if (position != 0) {
processDate(holder.topDateTextView, myData.getDate()
, this.myDataList.get(position - 1).getDate()
, false)
;
} else {
processDate(holder.topDateTextView, data.getDay()
, null
, true)
;
}
Method to process that date view (Assume your list has format "dd/MM/yyyy"):
private void processDate(#NonNull TextView tv, String dateAPIStr
, String dateAPICompareStr
, boolean isFirstItem) {
SimpleDateFormat f = new SimpleDateFormat("dd/MM/yyyy");
if (isFirstItem) {
//first item always got date/today to shows
//and overkill to compare with next item flow
Date dateFromAPI = null;
try {
dateFromAPI = f.parse(dateAPIStr);
if (DateUtils.isToday(dateFromAPI.getTime())) tv.setText("today");
else if (DateUtils.isToday(dateFromAPI.getTime() + DateUtils.DAY_IN_MILLIS)) tv.setText("yesterday");
else tv.setText(dateAPIStr);
tv.setIncludeFontPadding(false);
tv.setVisibility(View.VISIBLE);
} catch (ParseException e) {
e.printStackTrace();
tv.setVisibility(View.GONE);
}
} else {
if (!dateAPIStr.equalsIgnoreCase(dateAPICompareStr)) {
try {
Date dateFromAPI = f.parse(dateAPIStr);
if (DateUtils.isToday(dateFromAPI.getTime())) tv.setText("today");
else if (DateUtils.isToday(dateFromAPI.getTime() + DateUtils.DAY_IN_MILLIS)) tv.setText("yesterday");
else tv.setText(dateAPIStr);
tv.setIncludeFontPadding(false);
tv.setVisibility(View.VISIBLE);
} catch (ParseException e) {
e.printStackTrace();
tv.setVisibility(View.GONE);
}
} else {
tv.setVisibility(View.GONE);
}
}
}
Note: You also need to do yourAdapter.notifyDataSetChanged(); if append new item to redraw to dismiss previous "today"/date after 12.00 a.m on the same page, not just rely on yourAdapter.notifyItemInserted(new_item_position) which doesn't redraw previous items.