I have the following Adapter :
public class TeambuilderAdapter extends RecyclerView.Adapter<TeambuilderAdapter.ViewHolder> implements ItemTouchHelperAdapter {
private List<PokemonTeam> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
private Context mContext;
private PokemonFavoritesAdapter pokemonFavoritesAdapter;
private GridLayoutManager gridLayoutManager;
// data is passed into the constructor
public TeambuilderAdapter(Context context, List<PokemonTeam> data) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
mContext = context;
setHasStableIds(true);
}
#Override
public long getItemId(int position) {
PokemonTeam pokemonTeam = mData.get(position);
return (pokemonTeam.getName()).hashCode();
}
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.list_item_teambuilder, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
PokemonTeam pokemonTeam = mData.get(position);
gridLayoutManager = new GridLayoutManager(mContext, 6);
pokemonFavoritesAdapter = new PokemonFavoritesAdapter(mContext, pokemonTeam.getPokemonList());
holder.teamSpritesRecyclerView.setAdapter(pokemonFavoritesAdapter);
holder.teamSpritesRecyclerView.setLayoutManager(gridLayoutManager);
String pokemonTeamName = pokemonTeam.getName();
if (pokemonTeamName != null && !pokemonTeamName.isEmpty()) {
holder.teamTitleTextView.setText(pokemonTeam.getName());
}
}
// total number of rows
#Override
public int getItemCount() {
int size = 0;
if (Objects.nonNull(mData)) {
size = mData.size();
}
return size;
}
#Override
public void onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mData, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mData, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
((PokemonTeambuilderActivity) mContext).refreshTeamList();
}
#Override
public void onItemDismiss(int position) {
mData.remove(position);
notifyItemRemoved(position);
((PokemonTeambuilderActivity) mContext).refreshTeamList();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView teamTitleTextView;
RecyclerView teamSpritesRecyclerView;
CardView pokemonTeambuilderContainer;
ViewHolder(View itemView) {
super(itemView);
teamTitleTextView = itemView.findViewById(R.id.pokemonTeambuilderTitleTextView);
teamSpritesRecyclerView = itemView.findViewById(R.id.pokemonTeambuilderSpritesLayout);
pokemonTeambuilderContainer = itemView.findViewById(R.id.pokemonTeambuilderContainer);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) {
mClickListener.onItemClick(view, getAdapterPosition());
}
}
}
// convenience method for getting data at click position
public PokemonTeam getItem(int id) {
return mData.get(id);
}
// allows clicks events to be caught
public void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}
In onBindViewHolder method I call another adapter inside this adapter which follows the same OnClickListener structure :
public class PokemonFavoritesAdapter extends RecyclerView.Adapter<PokemonFavoritesAdapter.ViewHolder> {
private Context mContext;
private List<Pokemon> favoritePokemon;
private LayoutInflater mInflater;
private PokemonFavoritesAdapter.ItemClickListener mClickListener;
public PokemonFavoritesAdapter(Context context, List<Pokemon> list) {
this.mInflater = LayoutInflater.from(context);
mContext = context;
favoritePokemon = list;
}
#NonNull
#Override
public PokemonFavoritesAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.list_item_pokemon_favorite, parent, false);
return new PokemonFavoritesAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull PokemonFavoritesAdapter.ViewHolder holder, int position) {
Pokemon pokemon = favoritePokemon.get(position);
String pokemonId = pokemon.get_id();
int dominantColor = PokemonUtils.getDominantColorFromPokemon(pokemonId, mContext);
int lighterDominantColor = PokemonUtils.lighterColor(dominantColor, GenericConstants.DARK_FACTOR);
int pokemonImageId = PokemonUtils.getPokemonSugimoriImageById(pokemonId, mContext);
holder.circularImageView.setBorderColor(dominantColor);
holder.circularImageView.setBorderWidth(4);
holder.circularImageView.setCircleColor(lighterDominantColor);
holder.circularImageView.setShadowRadius(6);
Picasso.get().load(pokemonImageId).fit().into(holder.circularImageView);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
int size = 0;
if (Objects.nonNull(favoritePokemon) && !favoritePokemon.isEmpty()) {
size = favoritePokemon.size();
}
return size;
}
// convenience method for getting data at click position
public Pokemon getItem(int id) {
return favoritePokemon.get(id);
}
// allows clicks events to be caught
public void setClickListener(PokemonFavoritesAdapter.ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
CircularImageView circularImageView;
ViewHolder(View view) {
super(view);
circularImageView = view.findViewById(R.id.civ_pokemon_favorite);
view.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) {
mClickListener.onItemClick(view, getAdapterPosition());
}
}
}
}
However I have a problem , my activity can't listen the click events that happens inside my nested adapter :
public class PokemonTeambuilderActivity extends AppCompatActivity implements TeambuilderAdapter.ItemClickListener {
#Override
public void onItemClick(View view, int position) {
}
I don't know how to reach the events that happens within my PokemonFavoritesAdapter in my TeambuilderAdapter , I supose that I must implement the PokemonFavoritesAdapter.OnClickListener interface in my TeambuilderAdapter but I don't know how to do it . Any hint ?
I have a RecyclerView adapter with multiple ViewHolders. Each ViewHolder has a header TextView and a nested RecyclerView which was working fine. But I wanted to implement an expand/collapse function so that nested RecyclerView is hidden until the header is clicked. I used this method RecyclerView expand/collapse items. It works but when I click the header to expand the nested recylerview, the recyclerview doesn't populate any data. To be clear, it retrieves data but it's not visible. Any ideas why this might be?
This is my onBindViewMethod:
public class EligibilityAdapter extends RecyclerView.Adapter<EligibilityAdapter.ViewHolder> {
private Context mContext;
private List<EligibilityDetails> mEligsList;
private List<Items> mItemslist;
private LayoutInflater inflater;
private int mExpandedPosition = -1;
public EligibilityAdapter(Context context, List<EligibilityDetails> eligsList) {
mContext = context;
mEligsList = eligsList;
inflater = LayoutInflater.from(context);
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return R.layout.rv_eligs_item_domestic;
case 1:
return R.layout.rv_eligs_item_overseas;
default:
return R.layout.rv_eligs_item_military;
}
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
inflater = LayoutInflater.from(viewGroup.getContext());
View view = inflater.inflate(i, viewGroup, false);
ViewHolder holder = null;
switch (i) {
case R.layout.rv_eligs_item_domestic:
holder = new DomesticViewHolder(view);
break;
case R.layout.rv_eligs_item_overseas:
holder = new OverseasViewHolder(view);
break;
case R.layout.rv_eligs_item_military:
holder = new MilitaryViewHolder(view);
break;
}
return holder;
}
#Override
public int getItemCount() {
return mEligsList.size();
}
public abstract class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView itemsRv;
TextView mHeader;
ItemsAdapter adapter;
public ViewHolder(View itemView) {
super(itemView);
mHeader = (TextView) itemView.findViewById(R.id.header_tv);
itemsRv = itemView.findViewById(R.id.recyclerViewItems);
}
public void setData(List<Items> list) {
adapter.updateList(list);
}
abstract void bind(EligibilityDetails item);
}
public class DomesticViewHolder extends ViewHolder {
TextView mHeader;
RecyclerView itemsRv;
ItemsAdapter adapter;
public DomesticViewHolder(View itemView) {
super(itemView);
mHeader = (TextView) itemView.findViewById(R.id.header_tv);
itemsRv = itemView.findViewById(R.id.recyclerViewItems);
}
public void setData(List<Items> list) {
adapter.updateList(list);
}
#Override
void bind(EligibilityDetails eligibilityDetails) {
mHeader.setText(eligibilityDetails.getRequirementHeader());
mItemslist = eligibilityDetails.getItemsList();
ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
itemsRv.setHasFixedSize(true);
itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
itemsRv.setAdapter(itemsAdapter);
itemsRv.setNestedScrollingEnabled(false);
}
}
public class OverseasViewHolder extends ViewHolder {
TextView mHeader;
RecyclerView itemsRv;
ItemsAdapter adapter;
public OverseasViewHolder(View itemView) {
super(itemView);
mHeader = (TextView) itemView.findViewById(R.id.header_tv);
itemsRv = itemView.findViewById(R.id.recyclerViewItems);
}
public void setData(List<Items> list) {
adapter.updateList(list);
}
#Override
void bind(EligibilityDetails eligibilityDetails) {
mHeader.setText(eligibilityDetails.getRequirementHeader());
mItemslist = eligibilityDetails.getItemsList();
ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
itemsRv.setHasFixedSize(true);
itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
itemsRv.setAdapter(itemsAdapter);
itemsRv.setNestedScrollingEnabled(false);
}
}
public class MilitaryViewHolder extends ViewHolder {
TextView mHeader;
RecyclerView itemsRv;
ItemsAdapter adapter;
public MilitaryViewHolder(View itemView) {
super(itemView);
mHeader = (TextView) itemView.findViewById(R.id.header_tv);
itemsRv = itemView.findViewById(R.id.recyclerViewItems);
}
public void setData(List<Items> list) {
adapter.updateList(list);
}
#Override
void bind(EligibilityDetails eligibilityDetails) {
mHeader.setText(eligibilityDetails.getRequirementHeader());
mItemslist = eligibilityDetails.getItemsList();
final ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
itemsRv.setHasFixedSize(true);
itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
itemsRv.setAdapter(itemsAdapter);
itemsRv.setNestedScrollingEnabled(false);
}
}
#Override
public void onBindViewHolder(#NonNull final EligibilityAdapter.ViewHolder viewHolder, int i) {
viewHolder.bind(mEligsList.get(i));
final boolean isExpanded = i == mExpandedPosition;
viewHolder.itemsRv.setVisibility(isExpanded?View.VISIBLE:View.GONE);
viewHolder.itemView.setActivated(isExpanded);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? -1:viewHolder.getAdapterPosition();
ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
viewHolder.itemsRv.setAdapter(itemsAdapter);
//TransitionManager.beginDelayedTransition(recyclerView);
notifyDataSetChanged();
}
});
}
where itemsRv is the nested RecyclerView.
I've tried moving this logic to the individual viewholders and moving the recyclerview logic here like setting the adapter inside of the onClick method. Each time it comes up blank.
Thanks in advance.
Instead of having a new sub Recyclerview for every header you can create a multi view-type adapter that will have a view-type for your header and a view-type for your child-item.
And instead of using a just the header data-item for your list of data, use a generic type that will allow casting each data-type to its own view-type.
To do that we need to create an empty interface that all of our data-types will implement that way they are all generic.
public interface GenericDataType {}
so then your data-type will look like this
class HeaderItem implements GenericDataType {
//All of your pojo data
List<ChildrenItem> childrens; //ChildrenItem will also implement the GenericDataType that way both of the items are acceptable
}
So ones we did that we can replace the current items from
private List<EligibilityDetails> mEligsList;
private List<Items> mItemslist;
To
private List<GenericDataType> adapterItems;
Now we need to make sure that whenever we have a certain ViewHolder the item of that position will be of the right type. In order to do that we need to change our getItemViewType
#Override
public int getItemViewType(int position) {
GenericItem currentItem = adapterItems.get(position);
if (currentItem instanceOf HeaderItem) { //We have to use if else becasue java's switch does not supprt checking instancesOf
return R.layout.your_header_item_layout_id;
} else if(currentItem instanceOf ChildrenItem) {
return R.layout.your_children_item_layout_id;
} else if(repeat for any other types you have) {
return R.layout.your_other_item_layout_id;
} else {
throw new Throwable("Unsupported type"); //This should never happen but we add it to make the compiler compile
}
}
Now we can finally change our onBind method to support both types
#Override
public void onBindViewHolder(#NonNull final EligibilityAdapter.ViewHolder viewHolder, int i) {
if (viewHolder instanceOf HeaderViewHolder) {
HeaderViewHolder holder = (HeaderViewHolder) viewHolder;
headerItem = (HeaderItem) items.get(i);
//Do rest of binding here
holder.viewToAddMoreItems.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!headerItem.isEpanded()) { //Add to your header type a Boolean to check if it is expanded or not
adapterItems.addAll(headerItem.getChildrens()); //They are the same generic type but the getItemViewType will handle it for us
notifyItemRangeInserted(); //Provied item start index and size of the list
} else {
adapterItems.removeAll(headerItem.getChildrens());
notifyItemRangeRemoved(); I don't remember what it require but you'll figure that out
}
headerItem.setExpanded(!headerItem.isExpanded()); //Flipping the value
}
});
} else if (viewHolder instanceOf ChildrenViewHolder) {
ChildrenViewHolder holder = (ChildrenViewHolder) viewHolder;
childrenItem = (ChildrenItem) items.get(i);
//Do your binding here
} else if(repeate for other view-types) {}
}
I've implemented a recyclerview with staggered gridLayout containing about 31 items in the arrayList, recyclerview is working correctly, but I faced issue relating to single item selection.
When I select the value till "26" as shown in figure, its working fine
But, when I select the value after "26", the values from top most item are also selected, as shown in this next figure.
I require to only select one item at a time.
I've implemented the following code in my adapter class
public class DialogAdapter extends
RecyclerView.Adapter<DialogAdapter.DialogHolder>
{
// components
public Context context;
public ArrayList<AlertDialogModel> dialogArrayList = new
ArrayList<AlertDialogModel>();
private final ArrayList<Integer> selected = new ArrayList<>();
private int lastCheckedPosition = -1;
public Interface interface;
// parameterized constructor
public DialogAdapter(Context context, ArrayList<AlertDialogModel>
dialogArrayList,Interface interface)
{
this.context = context;
this.dialogArrayList = dialogArrayList;
this.interface = interface;
}
#NonNull
#Override
public DialogHolder onCreateViewHolder(#NonNull ViewGroup parent, int
viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_cardview,parent,false);
DialogHolder dialogHolder = new DialogHolder(view);
return dialogHolder;
}
#Override
public void onBindViewHolder(#NonNull final DialogHolder holder, final int position)
{
final AlertDialogModel alertDialogModel = dialogArrayList.get(position);
holder.textView.setText(alertDialogModel.getDisplayValue());
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
}
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
}
});
}
#Override
public int getItemCount()
{
return dialogArrayList.size();
}
public static class DialogHolder extends RecyclerView.ViewHolder
{
public TextView textView;
public DialogHolder(View itemView)
{
super(itemView);
textView = (TextView)itemView.findViewById(R.id.textView);
}
}
}
Can anyone relate my code and identify the issue ?
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
//below line is important to remove previous selected position from the variable
lastCheckedPosition = -1;
}
});
you should put the text view to the original state:
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
holder.textView.setTextColor(context.getResources().getColor(R.color.transparent));
holder.textView.setBackground(null));
}
I have a RecyclerView and each CardView contains a TextView and an ImageView. Whenever I click an item, I want to set the image visibility to VISIBLE and to set the previous clicked item image's visibility to INVISIBLE.
This is my Adapter class :
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.ViewHolder>{
private Context context;
private List<Category> lista;
private LayoutInflater layoutInflater;
private IncomeCategoryActivity activity;
private static final int CATEGORY_REQUEST=6;
private static final int ITEM_EDIT=1;
private static final int ITEM_DELETE=2;
private static final int EDIT_REQUEST=7;
private int current_pos=-1;
public CategoryAdapter(List<Category> lista, Context context, IncomeCategoryActivity activity) {
this.context = context;
this.lista = lista;
this.activity=activity;
layoutInflater=LayoutInflater.from(context);
}
#Override
public CategoryAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=layoutInflater.inflate(R.layout.category_layout, parent, false);
ViewHolder viewHolder=new ViewHolder(view, activity);
return viewHolder;
}
#Override
public void onBindViewHolder(CategoryAdapter.ViewHolder holder, int position) {
holder.imageView.setImageURI(lista.get(position).getUri());
holder.textView.setText(lista.get(position).getCategory());
holder.position = position;
holder.category=lista.get(position);
if(holder.category.isChecked()==true){
holder.imageViewCheck.setVisibility(View.VISIBLE);
current_pos=position;
} else {
holder.imageViewCheck.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return lista.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener{
public ImageView imageView;
public TextView textView;
public ImageView imageViewCheck;
public int position;
public Category category;
public IncomeCategoryActivity activity;
public ViewHolder(View itemView, IncomeCategoryActivity activity) {
super(itemView);
this.activity=activity;
imageView=itemView.findViewById(R.id.customCategoryImageView);
textView=itemView.findViewById(R.id.customCategoryTextView);
imageViewCheck=itemView.findViewById(R.id.customCheckImageView);
itemView.setOnClickListener(this);
itemView.setOnCreateContextMenuListener(this);
}
#Override
public void onClick(View v) {
String aux=textView.getText().toString();
if(aux=="CATEGORIE NOUĂ"){
Intent intent=new Intent(context, CustomIncomeActivity.class);
activity.startActivityForResult(intent, CATEGORY_REQUEST);
}
else{
imageViewCheck.setVisibility(View.VISIBLE);
int pozitie_check=getLayoutPosition();
Intent intent=new Intent(context, AddIncomeActivity.class);
intent.putExtra("categorie_venit", aux);
intent.putExtra("position_check", pozitie_check);
activity.setResult(Activity.RESULT_OK, intent);
activity.finish();
}
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle("Selectează acțiunea");
MenuItem edit=menu.add(0, ITEM_EDIT, 0, "Modifică");
MenuItem delete=menu.add(0, ITEM_DELETE, 0, "Șterge");
edit.setOnMenuItemClickListener(this);
delete.setOnMenuItemClickListener(this);
}
#Override
public boolean onMenuItemClick(MenuItem item) {
int position=getLayoutPosition();
if (item.getGroupId() == 0) {
if(item.getItemId()==ITEM_EDIT){
Category category=lista.get(position);
Intent intent=new Intent(activity, CustomIncomeActivity.class);
intent.putExtra("edit_icon", category.getUri());
intent.putExtra("edit_category", category.getCategory());
intent.putExtra("list_position", position);
activity.startActivityForResult(intent, EDIT_REQUEST);
}
else if(item.getItemId()==ITEM_DELETE){
lista.remove(position);
notifyDataSetChanged();
}
}
return true;
}
At this moment, whenever I click an item, there are two images VISIBLE on the RecyclerView: the clicked item's image and the previous clicked item's image. I think I need to get the previous View by its position and to manually set the visibility to INVISIBLE.
recycleView.getChildCount() and recycleView_parent.getChildAt() only gives the Adapter items which is shows only screen .
that means if your list has 200 items and the only 5 items shows on screen so we can find only 5 item with the help of recycleView
i am using one simple trick to solve the issue.
You can define Hashmap which hold your holder objects
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
ArrayList<Model> dataList;
Context context;
HashMap<Integer,ViewHolder> holderlist;
public MyAdapter(ArrayList<Model> dataList, Context context) {
this.context = context;
this.dataList = dataList;
holderlist = new HashMap<>();
}
And after that to save the holder in Hashmap
public void onBindViewHolder(final ViewHolder holder, final int position) {
if(!holderlist.containsKey(position)){
holderlist.put(position,holder);
}
Create a method in Adapter.
public MyListAdapter.ViewHolder getViewByPosition(int position) {
return holderlist.get(position);
}
Call this method from your Activity or whenever you want.
for (int i = 0; i < datalList.size(); i++) {
MyAdapter.ViewHolder holder = ((MyAdapter)recycleView.getAdapter()).getViewByPosition(i);
View view = holder.itemView;
TextView tv = view.findViewById(R.id.tv);
}
I have created the demo you can refer it and implement for single selection
recyclerView.setAdapter(new RecyclerView.Adapter() {
int selected_position = -1;
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(
R.layout.row_color_list,parent,false)) {
#Override
public String toString() {
return super.toString();
}
};
}
#Override
public void onBindViewHolder(#NonNull final RecyclerView.ViewHolder holder, int position) {
ImageView imageView = holder.itemView.findViewById(R.id.image);
if(selected_position == position){
imageView.setVisibility(View.VISIBLE);
}else {
imageView.setVisibility(View.GONE);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(selected_position != holder.getAdapterPosition()){
selected_position = holder.getAdapterPosition();
notifyDataSetChanged();
}
}
});
}
#Override
public int getItemCount() {
return 20;
}
});
You can do as following:
1) Declare a global variable:
private int selectedPos = -100; // Put any irrelevant number you want
2) Set selected position onClick() :
#Override
public void onClick(View v) {
selectedPos = getAdapterPosition();
}
3) Check selected postion and assign visibility inside onBindViewHolder():
#Override
public void onBindViewHolder(#NonNull final RecyclerView.ViewHolder holder, int position) {
if(position == selectedPos){
holder.imageViewCheck.setVisibility(View.VISIBLE);
} else {
holder.imageViewCheck.setVisibility(View.INVISIBLE);
}
}
Try this code..
add this code into adapter class for handling click event..
OnItemClick onItemClick;
public void setOnItemClick(OnItemClick onItemClick) {
this.onItemClick = onItemClick;
}
public interface OnItemClick {
void getPosition(String data); //pass any data to shared it.
}
after bind method..
#Override
public void onBindViewHolder(final ItemViewHolder holder, final int position) {
// below code handle click event on recycler view item.
final String str=mStringList.get(position); // here your boject
if(holder.category.isChecked()==true){
holder.imageViewCheck.setVisibility(View.VISIBLE);
current_pos=position;
} else {
holder.imageViewCheck.setVisibility(View.INVISIBLE);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClick.getPosition(str); // pass your data.
}
});
}
after bind adapter into recycler view it means adapter not null then called below code..
adpater.setOnItemClick(new RecyclerViewAdpater.OnItemClick() {
#Override
public void getPosition(String data) {
// hear update your value for check into if condition.
data="sdfsdf";
adpater.notifyDataSetChanged();
}
});
and also read comments and also try to make custon class and access that object value and update after click it..
this code only how to handle click event into recycler view.
Here is what I have achieved ? 3 different sections, 10 different items in each section.
Here is the tutorial link I am following and below is the Screenshot:
Trying to show different Views for each and every Section. Like:
For Section 1 (layout_1.xml)
For Section 2 (layout_2.xml)
For Section 3 (layout_3.xml)
But showing layout view of layout_1.xml in every Section... (Section 1, 2, 3)
May I know where I am doing mistake in my code, what I have missed ?
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context mContext;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.mContext = context;
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
switch (i) {
case 0:
View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
return rowONE;
case 1:
View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
SingleItemRowHolder rowTWO = new SingleItemRowHolder(viewTWO);
return rowTWO;
case 2:
View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
SingleItemRowHolder rowTHREE = new SingleItemRowHolder(viewTHREE);
return rowTHREE;
}
return null;
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
SingleItemModel singleItem = itemsList.get(i);
holder.tvTitle.setText(singleItem.getName());
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
public class SingleItemRowHolder extends RecyclerView.ViewHolder {
protected TextView tvTitle;
protected ImageView itemImage;
public SingleItemRowHolder(View view) {
super(view);
this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
this.itemImage = (ImageView) view.findViewById(R.id.itemImage);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
Use this inside adapter's getItemViewType:
#Override
public int getItemViewType(int position) {
if (position == 0) {
return 0;
} else if(position == 1) {
return 1;
} else {
return 2;
}
}
to use multiple layouts according to position in recyclerview you have to override the getItemViewType(int position) method inside the adapter :-
#Override
public int getItemViewType(int position) {
if(position==0)
return 0;
else if(position==1)
return 1;
else
return 2;
}
FYI
RecyclerView can also be used to inflate multiple view types .
It will be easiest for you that create different Holder.
Create Different Adapter is best Solutions
Try with
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
switch (i) {
case 0:
View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
return rowONE;
case 1:
View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
SingleItemRowHolderTwo rowTWO = new SingleItemRowHolderTwo (viewTWO);
return rowTWO;
case 2:
View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
SingleItemRowHolderThree rowTHREE = new SingleItemRowHolderThree(viewTHREE);
return rowTHREE;
}
return null;
}
Read RecyclerView can also be used to inflate multiple view types
As it was mention already, in order to getItemViewType method of RecyclerView.Adapter class, because if you will see in implementation of that method, you will see that it just return 0 all the time.
public int getItemViewType(int position) {
return 0;
}
And here adjusted code of your adapter which should solve your problem.
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private static final int ITEM_TYPE_ROW_1 = 0;
private static final int ITEM_TYPE_ROW_2 = 1;
private static final int ITEM_TYPE_ROW_3 = 2;
private ArrayList<SingleItemModel> itemsList;
private Context context;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.context = context;
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return ITEM_TYPE_ROW_1;
case 1:
return ITEM_TYPE_ROW_2;
case 2:
return ITEM_TYPE_ROW_3;
}
throw new RuntimeException(String.format("unexpected position - %d", position));
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType) {
case ITEM_TYPE_ROW_1:
View viewOne = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
return new SingleItemRowHolder(viewOne);
case ITEM_TYPE_ROW_2:
View viewTwo = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
return new SingleItemRowHolder(viewTwo);
case ITEM_TYPE_ROW_3:
View viewThree = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
return new SingleItemRowHolder(viewThree);
}
throw new RuntimeException(String.format("unexpected viewType - %d", viewType));
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
SingleItemModel singleItem = itemsList.get(i);
holder.tvTitle.setText(singleItem.getName());
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
class SingleItemRowHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
ImageView itemImage;
public SingleItemRowHolder(View view) {
super(view);
this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
this.itemImage = (ImageView) view.findViewById(R.id.itemImage);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
You can do it in a more simple way. Pass any flag while initializing adapter.
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context context;
private int view;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, int layoutFlag) {
this.itemsList = itemsList;
this.context = context;
switch(layoutFlag) {
case 0:
view = R.layout.layout_1;
break;
case 1:
view = R.layout.layout_2;
break;
case 2:
view = R.layout.layout_3;
break;
}
}
...
...
...
}
Use this view for layout reference. You just have to tell which layout to be inflated at the time of setting adapter.
You need to override the method
int getItemViewType (int position)
It receives the row number and you need to return the "type" of row i.e. 1 2 or 3.
The result will then be passed to onCreateViewHolder.
If for example you want to show this list of views:
type1
type2
type3
type1
type2
type3
then that should do the work:
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private static final int ITEM_TYPE_ROW_1 = 0;
private static final int ITEM_TYPE_ROW_2 = 1;
private static final int ITEM_TYPE_ROW_3 = 2;
private ArrayList<SingleItemModel> itemsList;
private Context context;
private ArrayList<Integer> viewTypes = new ArrayList<>();
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.context = context;
viewTypes.add(ITEM_TYPE_ROW_1);
viewTypes.add(ITEM_TYPE_ROW_2);
viewTypes.add(ITEM_TYPE_ROW_3);
viewTypes.add(ITEM_TYPE_ROW_1);
viewTypes.add(ITEM_TYPE_ROW_2);
viewTypes.add(ITEM_TYPE_ROW_3);
}
#Override
public int getItemViewType(int position) {
return viewTypes.get(position);
}
#Override
public int getItemCount() {
return viewTypes.size();
}
.......
........
If you want to add/remove rows then it can be done inserting/removing viewTypes in the viewTypes Array and then calling RecyclerView notifyItemInserted or notifyItemRemoved methods the list will be updated with the new order and type of views.
Simply use frame layout with your fragment and add this fragment in your framelayout that will add like you want for that. So its also easy to handle. hope this will help you
Yes you need to override getItemViewType(int position) method which helps to inflate different views in recyclerview.
I am posting a sample code which may help you.
public class TransactionHistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int TYPE_HEADER = 1;
private final int TYPE_CHILD = 2;
private final Context mContext;
private final List<TransactionResultEntity> mTransactionList;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
View headerView = LayoutInflater.from(mContext)
.inflate(R.layout.row_transaction_header, parent, false);
return new ParentTypeDataObjectHolder(headerView);
case TYPE_CHILD:
View childView = LayoutInflater.from(mContext)
.inflate(R.layout.row_transaction_child, parent, false);
return new ChildTypeDataObjectHolder(childView);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case TYPE_HEADER:
ParentTypeDataObjectHolder parentTypeDataObjectHolder = (ParentTypeDataObjectHolder) holder;
parentTypeDataObjectHolder.headerYearMonthTv.setText(mTransactionList.get(holder.getAdapterPosition()).getRowLabel());
break;
case TYPE_CHILD:
ChildTypeDataObjectHolder childTypeDataObjectHolder = (ChildTypeDataObjectHolder) holder;
childTypeDataObjectHolder.txnAmountTv.setText(mTransactionList.get(holder.getAdapterPosition()).getTransactionAmount());
break;
}
}
#Override
public int getItemCount() {
return mTransactionList.size();
}
#Override
public int getItemViewType(int position) {
if (mTransactionList.get(position).getDataType() == TYPE_HEADER)
return TYPE_HEADER;
else
return TYPE_CHILD;
}
class ParentTypeDataObjectHolder extends RecyclerView.ViewHolder {
private final TextView headerYearMonthTv;
public ParentTypeDataObjectHolder(View itemView) {
super(itemView);
headerYearMonthTv = (TextView) itemView.findViewById(R.id.row_transaction_header_tv);
}
}
class ChildTypeDataObjectHolder extends RecyclerView.ViewHolder {
TextView txnAmountTv;
public ChildTypeDataObjectHolder(View itemView) {
super(itemView);
txnAmountTv = (TextView) itemView.findViewById(R.id.transaction_child_txn_amount_tv);
}
}
}
All you need to do is override the method getItemViewType() inside your adapter.
You can write it as:
#Override
public int getItemViewType(int position) {
if (position < 0) {
return 0;
} else if(position < 20) {
return 1;
} else {
return 2;
}
}
Now the above logic works if your itemsList ArrayList have first 10 items of section 1, next 10 items of section 2, and last 10 items of section 3.
If that is not the case then you can have an integer field sectionNumber in your SingleItemModel class which specifies the section number in which that model belongs to. Now you can modify the method getItemViewType() as
#Override
public int getItemViewType(int position) {
SingleItemModel singleItemModel = itemsList.get(position);
if (singleItemModel.getSection() == 1) {
return 0;
} else if(singleItemModel.getSection() == 2) {
return 1;
} else {
return 2;
}
}
Okay, If I got it right, you want to make the second adapter, the one providing the row lists, variable, so it supports different layouts, based not on its position, but on some data from the main adapter (the one providing the sections). Therefore, overriding getItemViewType won't work, because the section data is contained in the main adapter, it does not even get there. So, the best, and cleanest course, would be to use... abstraction. Forget about multiple viewholders. Use one, and into it, bind a custom view. The custom views will provide both the specific layout files and will set the controls included in them. The holder will do just what it is intended to do: save ram by reusing views. the adavantage of this, is that you can have a clean hierarchy, which can grow in complexity in time, instead of a big, fat adapter which will become too hard to maintain.Here it is:
Since this is a lot of code to put it in here, I took your example project and modified to provide what I understood you were trying to do. Here it is:
https://github.com/fcopardo/exampleCustomViewsInHolder/tree/master
The highlights:
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context context;
private String section;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, String sectionName) {
this.itemsList = itemsList;
this.context = context;
this.section = sectionName;
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
return new SingleItemRowHolder(RowFactory.getRow(context, section));
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
holder.setData(itemsList.get(i));
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
public class SingleItemRowHolder extends RecyclerView.ViewHolder {
protected AbstractRowElement rowElement;
public SingleItemRowHolder(AbstractRowElement view) {
super(view);
this.rowElement = view;
}
public void setData(SingleItemModel singleItemModel){
rowElement.setItem(singleItemModel);
}
}
}
this is the variable layout adapter. As you can see, it uses only one ViewHolder, and a factory to provide the view instances you need.
public class RowFactory {
public static AbstractRowElement getRow(Context context, String name){
switch (name){
case "Section 1": return new FullRowElement(context);
case "Section 2": return new TextRowElement(context);
case "Section 3": return new ImageRowElement(context);
default:
Log.e("inflate", name);
return new FullRowElement(context);
}
}
}
this provides the custom views, each one using a different layout, but working with the same dataset, based on the section title.
public abstract class AbstractRowElement extends CardView{
protected int layout = 0;
protected SingleItemModel singleItemModel;
public AbstractRowElement(Context context) {
super(context);
inflateBaseLayout();
}
public AbstractRowElement(Context context, AttributeSet attrs) {
super(context, attrs);
inflateBaseLayout();
}
public AbstractRowElement(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflateBaseLayout();
}
protected void inflateBaseLayout() {
this.setContainer();
if(this.layout != 0) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(layout, this, true);
this.inflateComponents();
}
}
protected abstract void setContainer();
protected abstract void inflateComponents();
public void setItem(SingleItemModel itemModel){
this.singleItemModel = itemModel;
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), singleItemModel.getName()+"\n"+singleItemModel.getDescription(), Toast.LENGTH_SHORT).show();
}
});
setData(singleItemModel);
}
public abstract void setData(SingleItemModel itemModel);
}
Finally, this is the base view class for the adapter. Subclasses define the layout file to use, and put the desired data in the controls. The rest is pretty straightforward.
It would be totally possible to make this without custom views. You could just make something like :
int layoutFile = getLayoutForSection(section);
View v = LayoutInflater.from(viewGroup.getContext()).inflate(layoutFile, null);
But since I don't know how complex is the view you intend to create, it is best to keep things nicely separated. Have fun!