very new to Android App development here. I'm not sure if I'm asking the right question, but here's my situation. I have a simple gradebook app that looks like the following:
My goal is to display multiple courses on the app. I am using the RecyclerView to implement this, using the following onCreate function in my activity to pass the course information to the adapter:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grade);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
for(int i = 0; i<2; i++)
{
Course course = Course.generateRandomCourse();
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
mAdapter = new RecyclerViewAdapter(course);
recyclerView.setAdapter(mAdapter);
}
My goal for the for loop was to have two courses show up, but this is not the case. I'm not sure whether I need to rework my Recycling Adapter to take a list of courses, or if there is a simple way to display multiple courses.
Any help is much appreciated! Thank you for your time :)
EDIT: After adding 2 recyclers in XML and trying to invoke both, this is the code I used:
Course course = Course.generateRandomCourse();
Course course1 = Course.generateRandomCourse();
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades1);
layoutManager = new LinearLayoutManager(this);
layoutManager1 = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView1.setLayoutManager(layoutManager1);
mAdapter = new RecyclerViewAdapter(course);
mAdapter1 = new RecyclerViewAdapter(course1);
recyclerView.setAdapter(mAdapter);
recyclerView1.setAdapter(mAdapter1);
Now the output is just overlapped.
EDIT 2: Including full adapter code below.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Course> courses = new ArrayList<>();
private ArrayList<Assignment> assignments;
private String course;
private String average;
public RecyclerViewAdapter(List<Course> courses)
{
for(Course c : courses)
{
course = c.getCourseTitle();
assignments = c.getAssignments();
if(assignments.size()==0)
{
average = "0";
}
else
{
Integer grade_total = new Integer(0);
Integer assignment_total = new Integer(0);
for (Assignment i : assignments)
{
grade_total += i.getAssignmentGrade();
assignment_total++;
}
average = Integer.toString(grade_total /assignment_total);
}
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType)
{
if(viewType == TYPE_HEADER)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
else if(viewType == TYPE_ITEM)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_course, parent, false);
return new ViewHolderItem(v);
}
else return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if(holder instanceof ViewHolderHeader)
{
ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
VHHeader.title.setText(course);
VHHeader.average.setText("Average: " + average + "%");
}
else if(holder instanceof ViewHolderItem)
{
String assignemnt_name = assignments.get(position-1).getAssignmentTitle();
String assignment_grade = Integer.toString(assignments.get(position-1).getAssignmentGrade());
ViewHolderItem VHItem = (ViewHolderItem)holder;
VHItem.name.setText(assignemnt_name);
VHItem.grade.setText(assignment_grade + "%");
}
}
#Override
public int getItemCount()
{
return assignments.size() + 1;
}
private boolean isPositionHeader(int position)
{
if(position == 0)
return true;
else
return false;
}
#Override
public int getItemViewType(int position)
{
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
public class ViewHolderHeader extends RecyclerView.ViewHolder
{
TextView title;
TextView average;
public ViewHolderHeader(View view)
{
super(view);
this.title = (TextView)itemView.findViewById(R.id.course);
this.average = (TextView)itemView.findViewById(R.id.average);
}
}
public class ViewHolderItem extends RecyclerView.ViewHolder
{
TextView name;
TextView grade;
public ViewHolderItem(View view)
{
super(view);
this.name = (TextView)itemView.findViewById(R.id.assignment);
this.grade = (TextView)itemView.findViewById(R.id.grade);
}
}
}
You do not need to use multiple RecyclerView.
Just look at your code and you will find the answer why your code is not working.
RecyclerView is similar like listview where you have to pass a list. But in your code you are passing just 1 object of course.
You must not add recyclerview inside for loop, as recyclerview purpose is to store list,not 1 object.
So you should pass List to the recyclerview.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grade);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
List<Course> listCourse=new ArrayList();
for(int i = 0; i<2; i++)
{
listCourse.add(Course.generateRandomCourse());
}
mAdapter = new RecyclerViewAdapter(listCourse);
recyclerView.setAdapter(mAdapter);
}
And inside the adapter you can use this list of course.
For more reference you can check this link, on how to use RecyclerView with Adapter
EDIT: In your adapter look at this code:
private boolean isPositionHeader(int position)
{
if(position == 0)
return true;
else
return false;
}
Here position == 0 is static code, which will not work for dynamic code.If your list contains 1 Header and 1 Item, than you can change simply position%2 == 0 condition. else you have to create other recyclerview inside adapter.
Check out below code and comments I have added , at the end of onBindViewHolder you can add new recyclerview, for that you have to add recyclerview in your layout also.
Checkout how you can use List<Course> courses in your constructor and onBindViewHolder.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Course> courses = new ArrayList<>();
public RecyclerViewAdapter(List<Course> courses)
{
this.courses = courses;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
Course c=courses.get(position);
String course = c.getCourseTitle();
String assignments = c.getAssignments();
if(assignments.size()==0)
{
average = "0";
}
else
{
Integer grade_total = new Integer(0);
Integer assignment_total = new Integer(0);
for (Assignment i : assignments)
{
grade_total += i.getAssignmentGrade();
assignment_total++;
}
average = Integer.toString(grade_total /assignment_total);
}
ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
VHHeader.title.setText(course);
VHHeader.average.setText("Average: " + average + "%");
//Here you can set new recyclerview in your adapter for assignments
}
#Override
public int getItemCount()
{
return courses.size();
}
/*private boolean isPositionHeader(int position)
{
//Change logic here as per your list, because you have list not single object.
//if(position == 0)
// return true;
//else
// return false;
}*/
//Remove this code as you have not different views in your list
/*#Override
public int getItemViewType(int position)
{
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}*/
public class ViewHolderHeader extends RecyclerView.ViewHolder
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
}
You need to have 2 RecycleViews in your activity_grade xml layout, and do what you're doing inside the for loops for each.
What your for loop is currently doing, is creating a random course, and then setting that course to the RecyclerView called togglegrades, then on the second iteration, it does the same thing again, on the same RecyclerView
So what I would do after adding a second RecyclerView in the xml layout, is replace the for loop with something like this:
recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades);
recyclerView2 = (RecyclerView) findViewById(R.id.togglegrades2);
layoutManager = new LinearLayoutManager(this);
recyclerView1.setLayoutManager(layoutManager);
recyclerView2.setLayoutManager(layoutManager);
mAdapter = new RecyclerViewAdapter(course);
// Maybe create a different course here and set it to a new adapter variable
recyclerView.setAdapter(mAdapter);
recyclerView2.setAdapter(mAdapter);
You can do it in a very optimal way. Please see the tutorial titled Recyclerview multiple view type. Here you don't need to create multiple recycler views and you can use a single recycler view for multiple types of data.
Why do you use two RecyclerView?
Your Layout looks simple, just use one recyclerView with two viewType.
This is a sample
Related
I am designing a relatively complex UI, i have searched stackoverflow and haven't found similar design. There could be many approaches to this, but i would like to ask expert opionions on how to achieve this and i would like to share my approach and make sure i am doing it the right way. My approach is that i have created a recycleview with header and inside header recycleview i am using an expandable recycleview library developed by h6ah4i (taken from github). Please let me know if there's a better approach to this.
The following image preview is a live mockup of final result i would like to get. It's not the actual screen. My question is what is the best way to achieve this, should i use expandable recycleview or expandable listview in recycleview header. I appreciate any answer as approaches or libraries it doesn't have to be similar to my code. Any suggestions are welcomed. I hope this post will also help other people like me in search of similar solution.
RecycleView Adapter
public class RecycleAdapterPlantSearch extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Plants> plantsList;
private Context context;
private OnItemClickListener onItemClickListener;
public RecycleAdapterPlantSearch(Context context, List<Plants> plantsList, OnItemClickListener onClickListener) {
this.context = context;
this.plantsList = plantsList;
onItemClickListener = onClickListener;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
// Here Inflating your recyclerview item layout
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_plant_search_plant_item, parent, false);
return new ItemViewHolder(itemView, onItemClickListener);
} else if (viewType == TYPE_HEADER) {
// Here Inflating your header view
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_plant_search_header, parent, false);
return new HeaderViewHolder(itemView, onItemClickListener);
} else return null;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
/*
position 0 is for header
*/
if (holder instanceof HeaderViewHolder) {
// setheadersdata_flag = true;
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
// You have to set your header items values with the help of model class and you can modify as per your needs
// Setup expandable feature and RecyclerView
RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null);
SimpleDemoExpandableItemAdapter.OnListItemClickMessageListener clickListener = message -> {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
};
List<BadgesVM> badgesVMList = null;
badgesVMList = new ArrayList() {{
add(new BadgesVM("447", "Bienenfreundlich", "bienenfreundlich", false));
add(new BadgesVM("320,322", "Vogelfreundlich", "vogelfreundlich", false));
add(new BadgesVM("321", "Insektenfreundlich", "insektenfreundlich", false));
add(new BadgesVM("445", "Ökologisch wertvoll", "oekologisch", false));
add(new BadgesVM("531", "Schmetterlings freundlich", "schmetterlings", false));
add(new BadgesVM("530", "Heimische Pflanze'", "heimische Pflanze'", false));
}};
// Create wrapped adapter: MyItemAdapter -> expMgr.createWrappedAdapter -> MyHeaderFooterAdapter
RecyclerView.Adapter adapter;
adapter = new SimpleDemoExpandableItemAdapter(context, expMgr,badgesVMList, clickListener);
adapter = expMgr.createWrappedAdapter(adapter);
//adapter = new DemoHeaderFooterAdapter(adapter, null);
headerViewHolder.recyclerViewExpandable.setAdapter(adapter);
headerViewHolder.recyclerViewExpandable.setLayoutManager(new LinearLayoutManager(context));
// NOTE: need to disable change animations to ripple effect work properly
((SimpleItemAnimator) headerViewHolder.recyclerViewExpandable.getItemAnimator()).setSupportsChangeAnimations(false);
expMgr.attachRecyclerView(headerViewHolder.recyclerViewExpandable);
} else if (holder instanceof ItemViewHolder) {
final ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.plantDescText.setText(plantsList.get(position - 1).getDescription());
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.drawable.background_small);
String imageUrl = APP_URL.BASE_ROUTE_INTERN + plantsList.get(position - 1).getImages().get(0).getSrcAttr();
Glide.with(context).load(imageUrl).apply(options).into(itemViewHolder.plantImg);
}
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
#Override
public long getItemId(int position) {
return position;
}
// getItemCount increasing the position to 1. This will be the row of header
#Override
public int getItemCount() {
return plantsList.size() + 1;
}
public interface OnItemClickListener {
void OnItemClickListener(View view, int position);
void RecycleViewExtraDetails(ChipGroup chipGroup);
void nestedRecycleViewsSpecialOdd(RecyclerView nestedRecycleView);
}
private class HeaderViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView searchNameTxt, searchFamilyTxt, plantGroupTxt, plantFamilySearchTxt, ecologyFilterTxt,
frostSearchTxt;
private ChipGroup chipGroup;
private Button filterSearchBtn;
private CardView ecologyCv;
private CardView detailSearchCv;
private RecyclerView recyclerViewExpandable;
public HeaderViewHolder(View headerView, OnItemClickListener onItemClickListener) {
super(headerView);
searchNameTxt = headerView.findViewById(R.id.textView_plant_search_header_plant_search);
searchFamilyTxt = headerView.findViewById(R.id.textView_plant_search_header_plant_search);
ecologyCv = headerView.findViewById(R.id.cardView_plant_search_header_ecology);
detailSearchCv = headerView.findViewById(R.id.cardView_plant_search_header_detail_search);
plantGroupTxt = headerView.findViewById(R.id.textView_plant_search_header_plant_group);
plantFamilySearchTxt = headerView.findViewById(R.id.textView_plant_search_header_plant_family);
ecologyFilterTxt = headerView.findViewById(R.id.textView_plant_search_header_ecology_filter);
frostSearchTxt = headerView.findViewById(R.id.textView_plant_search_header_frost_filter);
chipGroup = headerView.findViewById(R.id.chip_group_plant_search_header);
filterSearchBtn = headerView.findViewById(R.id.button_plant_search_header_filter_search);
recyclerViewExpandable = headerView.findViewById(R.id.expandable_list_view_plant_search);
searchNameTxt.setOnClickListener(this);
searchFamilyTxt.setOnClickListener(this);
ecologyCv.setOnClickListener(this);
detailSearchCv.setOnClickListener(this);
plantGroupTxt.setOnClickListener(this);
plantFamilySearchTxt.setOnClickListener(this);
ecologyFilterTxt.setOnClickListener(this);
filterSearchBtn.setOnClickListener(this);
frostSearchTxt.setOnClickListener(this);
}
#Override
public void onClick(View view) {
onItemClickListener.OnItemClickListener(view, getAdapterPosition());
onItemClickListener.RecycleViewExtraDetails(chipGroup);
}
}
public class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private Button readMoreBtn;
private TextView plantDescText;
private ImageView plantImg;
public ItemViewHolder(View itemView, OnItemClickListener onItemClickListener) {
super(itemView);
plantDescText = itemView.findViewById(R.id.textView_plant_search_plants_item_description_text);
readMoreBtn = itemView.findViewById(R.id.button_plant_search_plant_item_read_more);
plantImg = itemView.findViewById(R.id.imageView_plant_search_plants_item_plant_image);
readMoreBtn.setOnClickListener(this);
}
#Override
public void onClick(View view) {
onItemClickListener.OnItemClickListener(view, getAdapterPosition() - 1);
}
}
}
nested header recycleview
public class SimpleDemoExpandableItemAdapter extends AbstractExpandableItemAdapter<SimpleDemoExpandableItemAdapter.MyGroupViewHolder,
SimpleDemoExpandableItemAdapter.MyChildViewHolder> implements View.OnClickListener {
RecyclerViewExpandableItemManager mExpandableItemManager;
List<MyBaseItem> mItems;
OnListItemClickMessageListener mOnItemClickListener;
List<BadgesVM> badgesVMList;
Context context;
static class MyBaseItem {
public final int id;
public final String text;
public MyBaseItem(int id, String text) {
this.id = id;
this.text = text;
}
}
static abstract class MyBaseViewHolder extends AbstractExpandableItemViewHolder {
TextView textView;
Slider frostSlider;
RecyclerView detailRecycleView;
public MyBaseViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(android.R.id.text1);
frostSlider = itemView.findViewById(R.id.slider_plant_search_expandable);
detailRecycleView = itemView.findViewById(R.id.recycle_view_plant_search_detail_search);
}
}
static class MyGroupViewHolder extends MyBaseViewHolder {
public MyGroupViewHolder(View itemView) {
super(itemView);
}
}
static class MyChildViewHolder extends MyBaseViewHolder {
public MyChildViewHolder(View itemView) {
super(itemView);
}
}
public SimpleDemoExpandableItemAdapter(Context context, RecyclerViewExpandableItemManager expMgr, List<BadgesVM> badgesVMList, OnListItemClickMessageListener clickListener) {
mExpandableItemManager = expMgr;
mOnItemClickListener = clickListener;
this.badgesVMList = badgesVMList;
this.context = context;
setHasStableIds(true); // this is required for expandable feature.
mItems = new ArrayList<>();
mItems.add(new MyBaseItem(0, "Filter nach ökologischen Kriterien"));
mItems.add(new MyBaseItem(1, "Frosthärte"));
mItems.add(new MyBaseItem(2, "Detailsuche"));
}
#Override
public int getGroupCount() {
return mItems.size();
}
#Override
public int getChildCount(int groupPosition) {
int childCount = 0;
int groupId = mItems.get(groupPosition).id;
if (groupId == 0) {
childCount = badgesVMList.size();
} else if (groupId == 1) {
childCount = 1; //contains only one item
} else if (groupId == 2) {
childCount = 1; //contains only one item
}
return childCount;
}
#Override
public long getGroupId(int groupPosition) {
// This method need to return unique value within all group items.
return mItems.get(groupPosition).id;
}
#Override
public long getChildId(int groupPosition, int childPosition) {
// This method need to return unique value within the group.
int groupId = mItems.get(groupPosition).id;
int childId = 0;
if (groupId == 0) {
badgesVMList.get(childPosition).getId();
} else if (groupId == 1) {
childId = 0;
} else if (groupId == 2) {
childId = 0;
}
return childId;
}
#Override
#NonNull
public MyGroupViewHolder onCreateGroupViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_group_item_for_expandable_minimal, parent, false);
MyGroupViewHolder vh = new MyGroupViewHolder(v);
vh.itemView.setOnClickListener(this);
return vh;
}
#Override
#NonNull
public MyChildViewHolder onCreateChildViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_child_item_for_expandable_minimal, parent, false);
MyChildViewHolder vh = new MyChildViewHolder(v);
vh.itemView.setOnClickListener(this);
return vh;
}
#Override
public void onBindGroupViewHolder(#NonNull MyGroupViewHolder holder, int groupPosition, int viewType) {
MyBaseItem group = mItems.get(groupPosition);
holder.textView.setText(group.text);
}
#Override
public void onBindChildViewHolder(#NonNull MyChildViewHolder holder, int groupPosition, int childPosition, int viewType) {
int groupId = mItems.get(groupPosition).id;
if (groupId == 0) {
BadgesVM badgesVM = badgesVMList.get(childPosition);
holder.textView.setVisibility(View.VISIBLE);
holder.frostSlider.setVisibility(View.GONE);
holder.detailRecycleView.setVisibility(View.GONE);
holder.textView.setText(badgesVM.getName());
} else if (groupId == 1) {
holder.textView.setVisibility(View.GONE);
holder.frostSlider.setVisibility(View.VISIBLE);
holder.detailRecycleView.setVisibility(View.GONE);
} else if (groupId == 2) {
holder.textView.setVisibility(View.GONE);
holder.frostSlider.setVisibility(View.GONE);
holder.detailRecycleView.setVisibility(View.VISIBLE);
// Setup expandable feature and RecyclerView
RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null);
DetailSearchExpandableItemAdapter.OnListItemClickMessageListener clickListener = message -> {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
};
List<BadgesVM> badgesVMList = null;
badgesVMList = new ArrayList() {{
add(new BadgesVM("447", "Bienenfreundlich", "bienenfreundlich", false));
add(new BadgesVM("320,322", "Vogelfreundlich", "vogelfreundlich", false));
add(new BadgesVM("321", "Insektenfreundlich", "insektenfreundlich", false));
add(new BadgesVM("445", "Ökologisch wertvoll", "oekologisch", false));
add(new BadgesVM("531", "Schmetterlings freundlich", "schmetterlings", false));
add(new BadgesVM("530", "Heimische Pflanze'", "heimische Pflanze'", false));
}};
// Create wrapped adapter: MyItemAdapter -> expMgr.createWrappedAdapter -> MyHeaderFooterAdapter
RecyclerView.Adapter adapter2;
adapter2 = new DetailSearchExpandableItemAdapter(context, expMgr, badgesVMList, clickListener);
adapter2 = expMgr.createWrappedAdapter(adapter2);
//adapter = new DemoHeaderFooterAdapter(adapter, null);
holder.detailRecycleView.setAdapter(adapter2);
holder.detailRecycleView.setLayoutManager(new LinearLayoutManager(context));
// NOTE: need to disable change animations to ripple effect work properly
((SimpleItemAnimator) holder.detailRecycleView.getItemAnimator()).setSupportsChangeAnimations(false);
expMgr.attachRecyclerView(holder.detailRecycleView);
}
}
#Override
public boolean onCheckCanExpandOrCollapseGroup(#NonNull MyGroupViewHolder holder, int groupPosition, int x, int y, boolean expand) {
// handles click event manually (to show Snackbar message)
return false;
}
#Override
public void onClick(View v) {
RecyclerView rv = RecyclerViewAdapterUtils.getParentRecyclerView(v);
RecyclerView.ViewHolder vh = rv.findContainingViewHolder(v);
int rootPosition = vh.getAdapterPosition();
if (rootPosition == RecyclerView.NO_POSITION) {
return;
}
// need to determine adapter local flat position like this:
RecyclerView.Adapter rootAdapter = rv.getAdapter();
int localFlatPosition = WrapperAdapterUtils.unwrapPosition(rootAdapter, this, rootPosition);
long expandablePosition = mExpandableItemManager.getExpandablePosition(localFlatPosition);
int groupPosition = RecyclerViewExpandableItemManager.getPackedPositionGroup(expandablePosition);
int childPosition = RecyclerViewExpandableItemManager.getPackedPositionChild(expandablePosition);
String message;
if (childPosition == RecyclerView.NO_POSITION) {
// Clicked item is a group!
// toggle expand/collapse
if (mExpandableItemManager.isGroupExpanded(groupPosition)) {
mExpandableItemManager.collapseGroup(groupPosition);
message = "COLLAPSE: Group " + groupPosition;
} else {
mExpandableItemManager.expandGroup(groupPosition);
message = "EXPAND: Group " + groupPosition;
}
} else {
// Clicked item is a child!
message = "CLICKED: Child " + groupPosition + "-" + childPosition;
}
mOnItemClickListener.onItemClicked(message);
}
public interface OnListItemClickMessageListener {
void onItemClicked(String message);
}
}
You were right to search a library that does most of the work for you, but I don't like the library you picked. It does not seem very flexible and concise. I suggest to take a look at Groupie, its API is pretty clean. Also check Reddit for some discussion on libraries.
If you want to write it yourself I think you can solve it without nested Adapter's. Just create an 'expandable group' item type. Then in getItemCount() you count all items and their nested items (when expanded). Take a look at the Groupie source code.
Some additional feedback on your code:
I would explicitly add the header to the list of items you give to your adapter. So instead of a List<Plants>, you rather provide a List<Item> and have a HeaderItem and PlantsItem. This way you have a clear separation between your domain models (Plants) and view models (the items) in your adapter.
Your onBindViewHolder() method does way too much. Let your ViewHolder subclasses take care of that. Create an abstract ViewHolder with an abstract bindTo(Item item) method. Then in your HeaderViewHolder subclass it and do the actual work (after an instanceof check).
Have a look at view binding, it can make your code more concise. (So does Kotlin.)
You can use ConcatAdapter to have multiple adapters with ViewHolders that hold different type of layouts even with the ones that contain RecyclerViews, i used in my last project and it works fine, you can check it out here, dashboard module uses multiple adapters to have different type of layouts.
You can also use the approach they used in Google iosched app to have one adapter with multiple layouts in better way where you move logic from adapter to ViewHolders and their wrapper class ViewBinders. ViewBinder is responsible of
calling onViewHolder, onCreateViewHolder and bind data type to a ViewBinder and ViewBinder to a layout. There is an article about how to use it in medium, i will post the link if i can find it. You can also check out this sample i created for animations but used ViewBinders in a simple form to create different type of layouts.
Below is the type of data and layout i wish to show in GridLayout and in which order
val data = mutableListOf<Any>().apply {
// Add Vector Drawables
add(HeaderModel("Animated Vector Drawable"))
add(AVDModel(R.drawable.avd_likes))
add(AVDModel(R.drawable.avd_settings))
add(HeaderModel("Seekable Vector Drawable"))
add(SeekableVDModel(R.drawable.avd_compass_rotation))
add(SeekableVDModel(R.drawable.avd_views))
add(SeekableVDModel(R.drawable.avd_hourglass))
add(HeaderModel("Clocks"))
add(AVDModel(R.drawable.avd_clock_alarm))
add(AVDModel(R.drawable.avd_clock_clock))
add(AVDModel(R.drawable.avd_clock_stopwatch))
}
These are correspond type of data i want to use in my RecyclerView, it's the types and binding to ViewHolder and layout in these classes.
private fun createViewBinders(): HashMap<ItemClazz, MappableItemBinder> {
val avdViewBinder = AVDViewBinder()
val seekableVDViewBinder = SeekableVDViewBinder()
val headViewBinder = HeaderViewBinder()
return HashMap<ItemClazz, MappableItemBinder>()
.apply {
put(
avdViewBinder.modelClazz,
avdViewBinder as MappableItemBinder
)
put(
seekableVDViewBinder.modelClazz,
seekableVDViewBinder as MappableItemBinder
)
put(
headViewBinder.modelClazz,
headViewBinder as MappableItemBinder
)
}
}
And set the data List to adapter and let adapter call corresponding layout that is bound to data
val dataList:List<Any> = getVectorDrawableItemList()
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val adapter = MultipleViewBinderListAdapter(
createViewBinders(),
RecyclerView.Adapter.StateRestorationPolicy.ALLOW
).apply {
submitList(dataList)
}
For the expandable list, iosched app good way of doing it, there is video about how to animate expandable items in RecyclerVİew here. You can set state in ViewHolder and even use MotionLayout for animating from collapsed to expandable state. All can be done without any third party library and very clean way.
I'm currently new to android development and I'm in the process of building my first app. I'm stuck on a particular tricky bit in which I'm trying to implement and Endless Recycler View.
I'm using a recycler view (currently) just to get the current dates and display them in a calendar output. There is two things that are really holding me up. One there is not much documentation out there on using an endless recycler view without a database (I do plan to but again not right now), and not knowing where/how to make it endless. The recyclerView I have Implemented is already working I just need it to load endlessly. The only examples I could find essentially required me to be using a database, or were to create one gigantic method in the onCreate method. Again I have also tried to implement my own setOnScrollListener inside the home class but alas I couldn't get it working.
Below are the desired results that I've got pictured in my head.
Link to UI picture
Here is my code so far. Ideally I would like to keep the on scroll Listener in the RecyclerAdapter as I will probably reuse it later on in other aspects of the app. Thank you in advanced.
HOME CLASS
public class Home extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private static final String TAG = "Home Class" ;
//Recycler View Variables and myCalendarClass Variables
private myCalendarClass myCalendarObj;
private int weeksInView = 1; //will need to make this update dynamicallly based on when a user toggles the view
private ArrayList<Calendar> calendarArrayList = new ArrayList<>();
private ArrayList<Integer> iconList = new ArrayList<>();
private ArrayList<Integer> eventCounterList = new ArrayList<>();
//recycler view dynamic loading variables
private RecyclerView calendarRecyclerView;
private boolean isLoading = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
initializeNavMenuButtons();
setViewSpinner();
this.myCalendarObj = new myCalendarClass();
setAllCalendarFields(this.myCalendarObj);
getImagesAndEventCounters();
//Example of the order to call the methods when a user switches the view
//setCalendarItrToCurrentSunday();
//setCalendarArrayList();
//getImagesAndEventCounters();
}
//Recycler view code
//gets images and events to feed into the recycler view
private void getImagesAndEventCounters() {
Log.d(TAG, "initImageBitMaps: called");
ArrayList<Calendar> weekView = getCalendarArrayList();
int daysInView = this.weeksInView * 7;
System.out.println("Days in view " + daysInView + " Weeks In View " + this.weeksInView);
for (int i = 0; i < daysInView; i++) {
calendarArrayList.add(weekView.get(i));
iconList.add(R.id.fitnessIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.educationIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.workIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.personalIcon);
eventCounterList.add(R.string.XEventsDefault);
initRecyclerView();
}
}
//passes data to the parent recycler view and sets the view
private void initRecyclerView() {
//For one recyclerView
this.parentLayoutManager = new GridLayoutManager(this, 7);
//GridLayoutManager layoutManager = parentLayoutManager;
RecyclerView recyclerView = findViewById(R.id.calendarRecyclerView);
recyclerView.setLayoutManager(parentLayoutManager);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this,iconList,eventCounterList,weeksInView,calendarArrayList);
recyclerView.setAdapter(adapter);
//ViewSpinner(Drop Down Menu)
private void setViewSpinner(){
Spinner viewSpinner = findViewById(R.id.ViewDropDownButton);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.Calendar_View_List, android.R.layout.simple_spinner_item);
viewSpinner.setAdapter(adapter);
viewSpinner.setOnItemSelectedListener(this);
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String text = parent.getItemAtPosition(position).toString();
myCalendarClass myCalendarObj;
if(text.equals("Week View")){
myCalendarObj = new myCalendarClass("Week View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
else if (text.equals("Biweekly View")){
myCalendarObj = new myCalendarClass("Biweekly View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
else if (text.equals("Month View")){
myCalendarObj = new myCalendarClass("Month View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
//setters
//sets all the local variables needed from myCalendarClass
private void setAllCalendarFields(myCalendarClass c){
this.myCalendarObj = c;
this.calendarArrayList = c.getCalendarArrayList();
this.weeksInView = c.getWeeksInView();
}
//getters
//returns the calendar arraylist
private ArrayList<Calendar> getCalendarArrayList(){
return this.calendarArrayList;
}
RECYCLER ADAPTER
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private static final String TAG = "RecyclerViewAdapter";
//variables for creating the recyclerView cards
private ArrayList<Integer> iconList;
private ArrayList<Integer> eventCounterList;
private Context mContext;
private int itemCount;
private ArrayList<Calendar> calendarArrayList;
private ArrayList<String> dayStr;
private ArrayList<String> dayNum;
public RecyclerViewAdapter(Context context, ArrayList<Integer> imageList, ArrayList<Integer> eventArray, int weeksInView,ArrayList<Calendar> calendarArrayList){
this.iconList = imageList;
this.eventCounterList = eventArray;
this.mContext = context;
this.itemCount = weeksInView*7;
this.calendarArrayList = calendarArrayList;
setDayStrNDayNum();
}
#NonNull
#Override
//this is the method that actually inflates each individual layout
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: called.");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem,parent,false);
return new ViewHolder(view);
}
#Override
//this is where we bind the data to each individual list items
//essentially all the data and stuff is actually attached in this method to each indiviual list item
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: called");
holder.fitnessIcon.getDrawable();
holder.educationIcon.getDrawable();
holder.personalIcon.getDrawable();
holder.workIcon.getDrawable();
holder.fitnessEventCounter.setText(eventCounterList.get(position));
holder.educationEventCounter.setText(eventCounterList.get(position));
holder.workEventCounter.setText(eventCounterList.get(position));
holder.personalEventCounter.setText(eventCounterList.get(position));
holder.dayString.setText(dayStr.get(position));
holder.dayNum.setText(dayNum.get(position));
}
//returns the amount of items we wish to include in the recycler view (not the individual items within a card layout
// but instead how many cards we wish to include...probably
#Override
public int getItemCount() {
return itemCount;
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView fitnessIcon;
ImageView educationIcon;
ImageView workIcon;
ImageView personalIcon;
TextView dayString;
TextView dayNum;
TextView fitnessEventCounter;
TextView educationEventCounter;
TextView workEventCounter;
TextView personalEventCounter;
public ViewHolder(View itemView){
super(itemView);
//icons within the layout_listitem
fitnessIcon = itemView.findViewById(R.id.fitnessIcon);
educationIcon = itemView.findViewById(R.id.educationIcon);
workIcon = itemView.findViewById(R.id.workIcon);
personalIcon = itemView.findViewById(R.id.personalIcon);
//text fields within the layout_listitem
dayNum = itemView.findViewById(R.id.dayNum);
dayString = itemView.findViewById(R.id.dayStr);
fitnessEventCounter = itemView.findViewById(R.id.fitnessEventCounter);
educationEventCounter = itemView.findViewById(R.id.educationEventCounter);
workEventCounter = itemView.findViewById(R.id.workEventCounter);
personalEventCounter = itemView.findViewById(R.id.personalEventCounter);
}
}
public void setItemCount(int newItemCount){
itemCount = newItemCount;
}
//sets the dayStr arrays and the dayNum array using CalendarArrayList
// to pull the day in the month and the string value(monday,tuesday,etc.)
private void setDayStrNDayNum(){
Iterator<Calendar> itr = this.calendarArrayList.iterator();
ArrayList<String> tempDayNum = new ArrayList<>();
ArrayList<String> tempDayStr = new ArrayList<>();
int i = 0;
while(itr.hasNext()){
String dayInMonth = getDayInMonth(calendarArrayList.get(i));
String weekDayToString = weekDayToString(calendarArrayList.get(i));
tempDayNum.add(dayInMonth);
tempDayStr.add(weekDayToString);
i++;
itr.next();
}//while
this.dayNum = tempDayNum;
this.dayStr = tempDayStr;
}
//takes in a Calendar Obj, gets the int, and returns it in a String Format
private String getDayInMonth (Calendar c){
Integer temp = c.get(Calendar.DAY_OF_MONTH);
String answer = Integer.toString(temp);
return answer;
}
//takes in a Calendar Obj, gets the weekday digit from 1 to 7, and returns a String
// 1 being Su for Sunday, 2 Mo for Monday, and etc.
private String weekDayToString (Calendar x){
int temp = x.get(Calendar.DAY_OF_WEEK);
switch(temp){
case 1:
return "Su";
case 2:
return "Mo";
case 3:
return "Tu";
case 4:
return "We";
case 5:
return "Th";
case 6:
return "Fr";
case 7:
return "Sa";
}
return "null";
}//weekDayToString
}
here is an idea: in onCreate create some base list containing the next 1000 dates surrounding the current date for your recyclerview and use the following link to understand how to find out after any scrollevent at which position you are on the recyclerview. once you get close to the edges simply add an additional 1000 dates on that end of the spectrum.
Look at the following link to see how to set an onscrolllistener on a recyclerview inside of the adapter (basically you need to pass the recyclerview to the adapters constructor)
hope this helps
I've started building a new app from scratch, so currently it contains only one activity. This activity contains 2 vertical RecyclerViews, first contains 30 Buttons, then space separating RecyclerViews and then second RecyclerView, containing 50 Buttons. Each RecyclerView has set GridLayoutManager. Any of RecyclerView doesn't fetch data from database/network, they have just fixed size and count. When I run this code, it launches in ~ 2 secs and logcat shows skipped frames at rate ~70-80.
When I skip setting layout it launches just fine.
This is my activity:
private GridLayoutManager mLayoutManager, mSecondManager;
private RecyclerView mRecyclerViewFirst, mRecyclerViewSecond;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvAdapter firstAdapter = new rvAdapter();
rvAdapter2 secondAdapter = new rvAdapter2();
mRecyclerViewFirst = findViewById(R.id.recycler_view1);
mRecyclerViewSecond = findViewById(R.id.recycler_view2);
mRecyclerViewFirst.setNestedScrollingEnabled(false);
mRecyclerViewSecond.setNestedScrollingEnabled(false);
mRecyclerViewFirst.setAdapter(firstAdapter);
mRecyclerViewSecond.setAdapter(secondAdapter);
}
#Override
protected void onResume() {
super.onResume();
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mLayoutManager = new GridLayoutManager(getApplicationContext(), 8);
mSecondManager = new GridLayoutManager(getApplicationContext(), 8);
} else {
mLayoutManager = new GridLayoutManager(getApplicationContext(), 5);
mSecondManager = new GridLayoutManager(getApplicationContext(), 5);
}
mRecyclerViewFirst.setLayoutManager(mSecondManager);
mRecyclerViewSecond.setLayoutManager(mLayoutManager);
}
one of adapters (second is the same, just count different):
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(viewGroup.getContext().getApplicationContext())
.inflate(R.layout.items_recyclerview, viewGroup, false));
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.mButton.setText(String.valueOf(position + 1));
}
#Override
public int getItemCount() {
return 50;
}
class ViewHolder extends RecyclerView.ViewHolder {
private Button mButton;
private ViewHolder(#NonNull View view) {
super(view);
mButton = view.findViewById(R.id.button);
}
}
}
Layouts: https://gitlab.com/Domin_PL/sample/blob/master/activity_main
I had troubles in adding XML code to stack
This is the whole application for now, so the problem is with LayoutManager. Btw, detecting device's orientation in onResume doesn't cause this lag.
Any ideas how can I fix this lag issue?
Thanks in advance
May be your problem with your xml file . If you use constraintlayout for recyclerview cell then lagging too much. Because too much load on main ui thread. Avoid constraintlayout in Recyclerview item. Please share your xml file of items of recyclerview.
I managed to get it down to an average 30~35 skipped frames upon cold launch of the app. I used a single RecyclerView with different view types and ViewHolders for the titles and the buttons. Here is the code with which I obtained the that result:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setAdapter(new RvAdapter(
ContextCompat.getColor(this, R.color.colorPrimaryDark),
ContextCompat.getColor(this, R.color.white)
));
}
#Override
protected void onResume() {
super.onResume();
final int orientation = getResources().getConfiguration().orientation;
final int columnCount = orientation == Configuration.ORIENTATION_LANDSCAPE ? 8 : 5;
final GridLayoutManager layoutManager = new GridLayoutManager(getApplicationContext(), columnCount);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int i) {
return (i == 0 || i == 31) ? columnCount : 1;
}
});
mRecyclerView.setLayoutManager(layoutManager);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
RvAdapter.java
public class RvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int BUTTON_FIRST_GROUP_COUNT = 30;
private static final int BUTTON_SECOND_GROUP_COUNT = 50;
private static final int TYPE_TITLE = 0;
private static final int TYPE_BUTTON = 1;
#ColorInt
private final int colorPrimary;
#ColorInt
private final int colorWhite;
private String[] content = new String[BUTTON_FIRST_GROUP_COUNT + BUTTON_SECOND_GROUP_COUNT + 2 /* titles */];
RvAdapter(#ColorInt final int colorPrimary, #ColorInt final int colorWhite) {
this.colorPrimary = colorPrimary;
this.colorWhite = colorWhite;
initContent();
}
private void initContent() {
for (int i = 0; i < content.length; i++) {
final String prefix = isTitle(i) ? "Title " : "";
content[i] = prefix + (i + 1);
}
}
private boolean isTitle(final int position) {
return (position == 0 || position == BUTTON_FIRST_GROUP_COUNT + 1);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
if (viewType == TYPE_TITLE) {
return new TitleViewHolder(layoutInflater.inflate(R.layout.item_title, viewGroup, false));
}
return new ButtonViewHolder(layoutInflater.inflate(R.layout.item_button, viewGroup, false));
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
final String currentContent = content[position];
if (holder instanceof TitleViewHolder) {
final TitleViewHolder titleViewHolder = (TitleViewHolder) holder;
titleViewHolder.mTextView.setText(currentContent);
} else {
final ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;
buttonViewHolder.setBackgroundColor(position <= BUTTON_FIRST_GROUP_COUNT ? colorPrimary : colorWhite);
buttonViewHolder.mButton.setText(currentContent);
}
}
#Override
public int getItemViewType(int position) {
return isTitle(position) ? TYPE_TITLE : TYPE_BUTTON;
}
#Override
public int getItemCount() {
return content.length;
}
}
Hope that it is going to help you.
i have a recyclerView where i added some sections using this library
and my RecyclerView is now looking like this
as you can see that my data is not organised yet, right now my data is on this form :
Section 2015
2016-05-03
2016-04-03
2015-12-03
Section 2016
2016-05-03
2016-04-03
2015-12-03
i want it to be like :
Section 2015
2015-12-03
Section 2016
2016-05-03
2016-04-03
as you can see i want to manage the data according to their value and section's value
this is my MainAdapter Class:
public class MainAdapter extends SectionedRecyclerViewAdapter<MainAdapter.MainVH> {
Context context;
LayoutInflater inflater;
List<Data> dataArray = Collections.emptyList();
ArrayList<String> data;
ArrayList<String > sectionData;
public MainAdapter(Context context ,ArrayList<String> data , ArrayList<String> sectionData ){
inflater = LayoutInflater.from(context);
this.context = context;
this.data = data;
this.sectionData = sectionData;
}
#Override
public int getSectionCount() {
return sectionData.size(); // number of sections.
}
#Override
public int getItemCount(int section) {
return data.size(); // odd get 8
// return 8; // number of items in section (section index is parameter).
}
#Override
public void onBindHeaderViewHolder(MainVH holder, int section) {
// Setup header view.
String current = sectionData.get(section);
holder.title.setText(current);
}
#Override
public void onBindViewHolder(MainVH holder, int section, int relativePosition, int absolutePosition) {
// Setup non-header view.
// 'section' is section index.
// 'relativePosition' is index in this section.
// 'absolutePosition' is index out of all non-header items.
// See sample project for a visual of how these indices work.
if (data.get(relativePosition).contains("2015")){
Toast.makeText(context , "it contains 2015",Toast.LENGTH_SHORT).show();
}
String currentRow = data.get(relativePosition);
holder.title.setText(currentRow);
// holder.title.setText(String.format("S:%d, P:%d, A:%d", section, relativePosition, absolutePosition));
}
#Override
public int getItemViewType(int section, int relativePosition, int absolutePosition) {
if (section == 1)
return 0; // VIEW_TYPE_HEADER is -2, VIEW_TYPE_ITEM is -1. You can return 0 or greater.
return super.getItemViewType(section, relativePosition, absolutePosition);
}
#Override
public MainVH onCreateViewHolder(ViewGroup parent, int viewType) {
// Change inflated layout based on 'header'.
int layout;
switch (viewType) {
case VIEW_TYPE_HEADER:
layout = R.layout.section;
break;
case VIEW_TYPE_ITEM:
layout = R.layout.row;
break;
default:
Toast.makeText(context,"Default",Toast.LENGTH_SHORT).show();
break;
}
View v = LayoutInflater.from(parent.getContext())
.inflate(viewType == VIEW_TYPE_HEADER ? R.layout.section : R.layout.row, parent, false);
return new MainVH(v);
}
public static class MainVH extends RecyclerView.ViewHolder {
final TextView title;
public MainVH(View itemView) {
super(itemView);
// Setup view holder.
// You'd want some views to be optional, e.g. for header vs. normal.
title = (TextView) itemView.findViewById(R.id.TV);
}
}
}
and this is my MainActivity :
public class MainActivity extends AppCompatActivity {
MainAdapter mainAdapter;
RecyclerView recyclerView;
ArrayList<String> mData;
ArrayList<String > mDataSection,RawmDataSection;
ArrayList<Data> managedDataArray;
HashMap hMap;
ArrayList<ArrayList<String>> organizedData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mData = new ArrayList<String>();
mDataSection = new ArrayList<String>();
RawmDataSection = new ArrayList<String>();
hMap = new HashMap();
managedDataArray = new ArrayList<Data> ();
organizedData = new ArrayList<ArrayList<String>>();
mData.add("2016-05-3");
mData.add("2016-04-3");
mData.add("2016-02-3");
mData.add("2015-12-3");
for (int i = 0 ; i<mData.size() && i<mData.size() ; i++ ){
//spilting the data
String string = mData.get(i);
String[] parts = string.split("-");
String part1 = parts[0];
String part2 = parts[1];
String part3 = parts[2];
RawmDataSection.add(part1);
}
Set<String> uniqueSet = new HashSet<String>(RawmDataSection);
mDataSection.addAll(uniqueSet);
final List<Data> data = new ArrayList<>();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "ReFreshing", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
Data dataObject = new Data();
for (int i = 0; i < mData.size(); i++) {
dataObject.RowMonth = mData.get(i);
Log.d("row :" + mData.get(i), " Section : ");
data.add(dataObject);
}
for (int i = 0; i < mDataSection.size(); i++) {
dataObject.SectionYear = mDataSection.get(i);
Log.d("row :", " Section : " + mDataSection.get(i));
data.add(dataObject);
}
recyclerView = (RecyclerView) findViewById(R.id.List);
MainAdapter adapter = new MainAdapter(MainActivity.this, mData, mDataSection);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
recyclerView.setHasFixedSize(true);
organizedData.add(mDataSection);
organizedData.add(mData);
}
});
}
}
i even don't know where to start so if any body can give me any hint or guidance than it'll be so helpful and highly appreciated by me , thanks
if my question is not understandable then please let me know i'll fix it
You can achieve that in a simpler way with the library SectionedRecyclerViewAdapter.
First create a Section class to group your items:
class MySection extends StatelessSection {
String title;
List<String> list;
public MySection(String title, List<String> list) {
// call constructor with layout resources for this Section header, footer and items
super(R.layout.section_header, R.layout.section_item);
this.title = title;
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new SimpleHeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;
// bind your header view here
headerHolder.tvItem.setText(title);
}
}
Then you set up the RecyclerView with your Sections:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Create your sections with the list of data for each year
MySection section2015 = new MySection("2015", dataList2015);
MySection section2016 = new MySection("2016", dataList2016);
// Add your Sections to the adapter
sectionAdapter.addSection(section2015);
sectionAdapter.addSection(section2016);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
I have two tabs in my app and there are listviews in this tabs.
I set List data to each listvew.
I want to delete form list, and from listview when I click [x] image.
Item deleted from list In my code , but I dont know how to update listview,I use notifyDataSetChanged() in my customadapter, but not update.
Activity for first tab:
public static List<Product> mCartList;
mCartList = AllProducts.getCartList();
listViewCatalog = (ListView) findViewById(R.id.my_order_list);
mProductAdapter = new CustomListAdapter(MyOrders.this, mCartList, "", true);
listViewCatalog.setAdapter(mProductAdapter);
my Custom List Adapter:
public class CustomListAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
private Context mContext;
private List<Product> mProductList;
private String mType;
private boolean mShowQuantity;
public CustomListAdapter(Context context, List<Product> list, String type, boolean showQuantity) {
layoutInflater = LayoutInflater.from(context);
mContext = context;
mProductList = list;
mShowQuantity = showQuantity;
mType = type;
}
#Override
public int getCount() {
return mProductList.size();
}
#Override
public Object getItem(int position) {
return mProductList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder item;
final int finalMPosition = position;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_row, null);
item = new ViewHolder();
item.imageView = (ImageView) convertView.findViewById(R.id.product_image);
item.name = (TextView) convertView.findViewById(R.id.name);
item.pid = (TextView) convertView.findViewById(R.id.pid);
item.price = (TextView) convertView.findViewById(R.id.price);
item.description = (TextView) convertView.findViewById(R.id.description);
item.removeProduct = (ImageView) convertView.findViewById(R.id.removeProduct);
item.addToCart = (TextView) convertView.findViewById(R.id.addtocard);
item.productQuantity = (TextView) convertView.findViewById(R.id.textViewQuantity);
convertView.setTag(item);
} else {
item = (ViewHolder) convertView.getTag();
}
final Product curProduct = mProductList.get(position);
item.imageView.setImageDrawable(curProduct.imageView);
item.name.setText(curProduct.name);
item.pid.setText(curProduct.pid);
int length = curProduct.description.length();
int start = 0, end = length;
if (length >= 40) {
start = 0;
end = 40;
}
String s = curProduct.description.substring(start, end);
item.description.setText(s + "...");
item.price.setText(curProduct.price + mContext.getString(R.string.currency));
if (mShowQuantity) {
item.addToCart.setVisibility(View.GONE);
// item.productQuantity.setText("Quantity: " + AllProducts.getProductQuantity(curProduct));
item.productQuantity.setVisibility(View.GONE);
} else {
item.productQuantity.setVisibility(View.GONE);
item.removeProduct.setVisibility(View.GONE);
}
item.removeProduct.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
ab.setTitle("Delete ");
ab.setMessage("This product will be deleted from list.").setPositiveButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
curProduct.selected = false;
AllProducts.removeProduct(curProduct);
notifyDataSetChanged();
notifyDataSetInvalidated();
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
ab.create().show();
}
});
item.addToCart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent productDetailsIntent = new Intent(mContext, ProductDetail.class);
productDetailsIntent.putExtra(AllProducts.PRODUCT_INDEX, finalMPosition);
productDetailsIntent.putExtra("type", mType);
mContext.startActivity(productDetailsIntent);
}
});
return convertView;
}
public class ViewHolder {
TextView pid;
TextView addToCart;
TextView name;
TextView price;
TextView description;
ImageView imageView;
ImageView removeProduct;
TextView productQuantity;
}
}
You can introduce notifyDataSetChanged() in the list view or you can reset the setAdapter() which the new list values , but later one will be costly
You have 2 different List, try doing this:
Create a constructor without passing a List i mean:
CustomListAdapter(MyOrders.this, "", true);
Then in the CustomListAdapter create an List Local variable and instatiate it in the constructor:
private List<Product> mProductList;
public CustomListAdapter(Context context, String type, boolean showQuantity) {
layoutInflater = LayoutInflater.from(context);
mContext = context;
mProductList = new ArrayList<Product>();
mShowQuantity = showQuantity;
mType = type;
}
then create an Add or Delete method in the CustomListAdapter:
public void AddItem(Product product){
if(null != product){
mProductList.add(product);
}
}
Then an updateMethod too:
public void update(){
mProductList.notifyDataSetChanged();
}
if notifyDatasetChanged is not working try to use this
yourlist.invalidateviews();
You need to remove that particular item from arraylist by calling
AllProducts.remove(position);
notifyDatasetChanged();
write this methode in your adapter and use it to remove perticular Item from adapter.
public void remove(int position){
mProductList.remove(position);
notifyDatasetChanged();
}
I resolved my problem.
public static void setTab(int i){
if(i==0){
mTabHost.setCurrentTab(i+1);
mTabHost.setCurrentTab(i);
}
else{
mTabHost.setCurrentTab(i-1);
mTabHost.setCurrentTab(i);
}
}
and
#Override
public void onClick(DialogInterface dialog, int which) {
curProduct.selected = false;
AllProducts.removeProduct(curProduct);
MyTabActivity.setTab(0);
}
and for second listview:
MyTabActivity.setTab(1);
The only reason it is not working for you now, is because you have two different lists. You are removing from wrong list.
So first of all, do not delete from your list. Write a method in your adapter where you remove it from list you have stored in that Adapter, and then call notifyDatasetChanged in that method.
Working with tabs in android 2.x, I found a bug in the emulator: The tabs are not correctly refreshed or removed. Alternatives is to use support library v7 which as support for action bar tabs.
Use following code to update ListView.
In order to update ListView you must have to call notifyDataSetChanged(); menthod on Adapter. See Below code.
What you did.
notifyDataSetChanged();
notifyDataSetInvalidated();
What you have to do is,
yourAdapter.notifyDataSetChanged();
Or you may use following code to refresh your list.
private List<Object> mCarlistitem= new ArrayList<Object>();
public void refreshlist()
{
mCarlistitem.clear();
for(int i=0; i< mCartList.size(); i++)
{
Object object = new Object();
mCarlistitem.add(object);
}
mProductAdapter = new CustomListAdapter(MyOrders.this, mCartList, "",true);
listViewCatalog.setAdapter(mProductAdapter);
}