Expand collapse a layout in recycler view - android

I have a recycler view which has header element and child element at starting time the visibility of child element is gone. I want that when I clicked on header element suppose position zero child elements of that position should be visible (mean expand) I achieve this successfully. The only problem that I am facing is that when I clicked the same position again I want to collapse the same view.
My recycler view onBingViewHolder logic is given below:
holder.doc_type.setText(docDatalist.get(position).getDocTypeDesc());
if (position == expandedPosition )
{
holder.recyclerViewImages.setVisibility(View.VISIBLE);
imagerequestserver(docDatalist.get(expandedPosition), vinappserial, holder);
holder.iconexpandCollapse.setImageResource(R.mipmap.expand_4);
}
else {
holder.recyclerViewImages.setVisibility(View.GONE);
holder.iconexpandCollapse.setImageResource(R.mipmap.expand);
}
holder.myDocumentListCollapse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (expandedPosition >= 0) {
int prev = expandedPosition;
holder.recyclerViewImages.setVisibility(View.VISIBLE);
notifyItemChanged(prev);
}else {
expandedPosition = holder.getAdapterPosition();
notifyItemChanged(expandedPosition);
}
}
});

You need to add manually add position array which was clicked and then after check its position in viewHolder;
Provide collapse Position List in your constructor
positionSet = new ArrayList<>();
Add and remove appropriate position on Header Click listener click event:
public void onClick(View view) {
int position = getAdapterPosition();
if(positionSet.contains(position)){
positionSet.remove((Integer)position);
}else {
positionSet.add(position);
}
notifyDataSetChanged();
}
}
Manage to notify in onBindViewHolder
if(positionSet.contains(position)){
//visible
}else {
//gone
}

Related

How to hide/display specific RecyclerView item?

I have a contacts list RecyclerView, where the first item (position 0) holds the user's details. When the search icon in the toolbar is pressed, I want that entry to be hidden from the user.
I've tried using setVisibility(View.GONE) on it, and, although the entry is hidden, the space it occupied was still there, much like setting setVisibility(View.INVISIBLE). How can I toggle the visibility of this specific entry to VISIBLE/GONE in my RecyclerView, or how can I set its height only to 0dp?
EDIT:
My xml is quite simple, it comprises of simply a recyclerView and a FAB. My adapter code is as below.
ContactsAdapter(
Context context,
List<LinphoneContact> contactsList,
ContactViewHolder.ClickListener clickListener,
SelectableHelper helper) {
super(helper);
mContext = context;
updateDataSet(contactsList);
mClickListener = clickListener;
}
#NonNull
#Override
public ContactViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// For the first position only, use the user's own contact card
// For all the rest, use the contact card
switch (viewType) {
case USER_CARD_VIEW:
View userCard =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.contacts_user_card, parent, false);
return new ContactViewHolder(userCard, mClickListener);
case CONTACT_CARD_VIEW:
View contactCard =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.contact_cell, parent, false);
return new ContactViewHolder(contactCard, mClickListener);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull ContactViewHolder holder, int position) {
// Remove the user's card when searching contacts - DOES NOT WORK, MAKES ALL FIRST ITEMS
// INVISIBLE
// if (position == 0) {
// if (mIsSearchMode) holder.itemView.setVisibility(View.GONE);
// else holder.itemView.setVisibility(View.VISIBLE);
// }
if (position != 0) {
LinphoneContact contact = (LinphoneContact) getItem(position - 1);
holder.name.setText(contact.getFullName());
if (!mIsSearchMode) {
String fullName = contact.getFullName();
if (fullName != null && !fullName.isEmpty()) {
holder.separatorText.setText(String.valueOf(fullName.charAt(0)));
}
}
// Separator as in the big capital letter on the left to indicate sections
holder.separator.setVisibility(
mIsSearchMode
|| (getPositionForSection(getSectionForPosition(position))
!= position)
? View.GONE
: View.VISIBLE);
holder.linphoneFriend.setVisibility(
contact.isInFriendList() ? View.VISIBLE : View.GONE);
ContactAvatar.displayAvatar(contact, holder.avatarLayout);
boolean isOrgVisible = LinphonePreferences.instance().isDisplayContactOrganization();
String org = contact.getOrganization();
if (org != null && !org.isEmpty() && isOrgVisible) {
holder.organization.setText(org);
holder.organization.setVisibility(View.VISIBLE);
} else {
holder.organization.setVisibility(View.GONE);
}
holder.delete.setVisibility(isEditionEnabled() ? View.VISIBLE : View.INVISIBLE);
holder.delete.setChecked(isSelected(position));
// } else {
// // TODO - user's card should have whole and voicemail onClick listeners
}
// Log.d("contactsAdapter", "Position: " + position);
// Log.d("contactsAdapter", "Section for position: " +
// getSectionForPosition(position));
// Log.d(
// "contactsAdapter",
// "The other thingy: " +
// getPositionForSection(getSectionForPosition(position)));
}
// +1 item count to account for user's contact card
#Override
public int getItemCount() {
return mContacts.size() + 1;
}
public Object getItem(int position) {
if (position >= getItemCount()) return null;
return mContacts.get(position);
}
public void setIsSearchMode(boolean set) {
mIsSearchMode = set;
}
[...]
#Override
public int getItemViewType(int position) {
return position == 0 ? USER_CARD_VIEW : CONTACT_CARD_VIEW;
}
Essentially, I don't add the first item to my list of items, but instead I add it to the recyclerView, and shift my list of items by one position up.
although the entry is hidden, the space it occupied was still there
may be because you are hiding the child but its parent is still there with its height or padding given
How can I toggle the visibility of this specific entry to VISIBLE/GONE in my RecyclerView
Toggle the visibility of parent
In your onBindViewHolder
#Override
public void onBindViewHolder(#NonNull MyViewHolder viewHolder, int i) {
if (isButtonPressed) {
viewHolder.parentViewId.setVisibility(View.GONE);
}else {
viewHolder.parentViewId.setVisibility(View.VISIBLE);
}
}
Create a public boolean isButtonPressed; and when you press the button make it true and call adapter.notifyDataSetChanged();

How to get control to other item in recyclerview?

I have a horizontal recyclerView,when I clicked on each of the element,a LinearLayout inside it setVisibility(VISIBLE) ,this work fine.
So what I want now is,when I clicked in current item,LinearLayout setVisibilty(VISIBLE)(as usual) at the same time,if I clicked another item before,the LinearLayout inside it setVisibility(GONE).
What I tried so far
viewHolder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(viewHolder.linearLayout.getVisibility() == View.VISIBLE){
//hide
viewHolder.linearLayout.setVisibility(View.GONE);
}else{
int nowClickedId = 0;
//hide LinearLayout of other position that clicked before
if(nowClickedId != position){
viewHolder.linearLayout.setVisibility(View.GONE);
}
//show for current clicked item
viewHolder.linearLayout.setVisibility(View.VISIBLE);
//get current item position store at the array
nowClickedId = viewHolder.getAdapterPosition();
}
}
});
So far I just have control at the item in current position,how can get the control of the item of clicked previously?
Create a variable to store the position of the item clicked and declare it globally.
private int nowclicked=-1;
Now inside your viewholder whenever position is clicked store it to the global variable and call notifyDataSetChanged() which notifies the adapter that data is changed.
private class Sample extends RecyclerView.ViewHolder{
public Sample(View itemView) {
super(itemView);
viewHolder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
nowclicked = getAdapterPosition();
notifyDataSetChanged();
}
});
}
}
In the onBindViewHolder if the posistion is equal show linearLayout else hide it.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(nowclicked==position){
viewHolder.linearLayout.setVisibility(View.VISIBLE);
}else {
viewHolder.linearLayout.setVisibility(View.GONE);
}
}
Try to write this code inside the onBindViewHolder() method and ensure to write
holder.viewHolder.container.setOnClickListener();
And according the position you can do whatever you want.

Hilighted/Selected items colour changes when not in view in RecyclerView

I have a RecyclerView with a large list of items. This RecyclerView has OnScrollListener for endless scrolling.
When an item is selected I highlight it with a specific colour and
when unselected the colour changes to normal/white.
The issue that I am facing is after selecting a few visible items in my view when I scroll up or down to select a few more items the colour of already selected items changes to white.
I tried adding a boolean variable (isSelected) in the model class and highlight the selected item but still I am facing the same issue as earlier. Currently the recyclerView allows to select just one item from the view and after some research I figured some of the concepts were taken from this article to implement single item selection. I wonder how do I modify/tweak this code to be able to select multiple items.
I am not presenting the code as it is quite huge and is confidential but if there is any general fix for this scenario then what would it be?
Background: the application is a chat app and I am showing the sent and received texts. The user should be able to select a few specific texts and should be able to share it with someone else.
Edit: I am putting the code in my onBindViewHolder:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final PostDataColumns mPostDataColumns = data.get(position);
holder.textCardView.setBackgroundColor(mPostDataColumns.isSelected() ? getResources().getColor(R.color.long_press):
getResources().getColor(android.R.color.white));
holder.textCardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
mPostDataColumns.setSelected(!mPostDataColumns.isSelected());
if(mPostDataColumns.isSelected()) {
holder.textCardView.setBackgroundResource(R.color.long_press);
multipleSelectedPositions.add(holder.getLayoutPosition());
} else if(!mPostDataColumns.isSelected()) {
holder.textCardView.setBackgroundResource(android.R.color.white);
multipleSelectedPositions.remove(holder.getAdapterPosition());
}
//Adapter.this.onLongClick(holder, position);
return true;
}
});
holder.textCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.textCardView.setBackgroundResource(android.R.color.white);
/* clearLongSelection(holder, position, alignParentRight,
data.get(position).getReceiverUserId().length() > 5); */
}
});
}
The code which I have commented in onCLick and onLongClick were used when the requirement was to select a single item.
these are the methods which were called in onClick and onLOngClick:
public boolean clearLongSelection(ViewHolder holder, int position) {
if (selectedPosition >= 0) {
if (selectedPosition == position) {
holder.parentLayout.setBackgroundResource(android.R.color.transparent);
if (alignParentRight) {
holder.mediaCardView.setBackgroundResource(android.R.color.white);
holder.assessmentCardView.setBackgroundResource(android.R.color.white);
holder.surveyCardView.setBackgroundResource(android.R.color.white);
holder.documentCardView.setBackgroundResource(android.R.color.white);
holder.textCardView.setBackgroundResource(android.R.color.white);
} else {
holder.mediaCardView.setBackgroundResource(R.color.long_press);
holder.assessmentCardView.setBackgroundResource(R.color.long_press);
holder.surveyCardView.setBackgroundResource(R.color.long_press);
holder.documentCardView.setBackgroundResource(R.color.long_press);
holder.textCardView.setBackgroundResource(R.color.long_press);
}
selectedPosition = -1;
invalidateOptionsMenu();
getSupportActionBar().setTitle(intentData.getName());
}
return true;
}
return false;
}
public void onLongClick(ViewHolder holder, int position) {
if (selectedPosition < 0) {
holder.parentLayout.setBackgroundResource(R.color.long_press);
holder.mediaCardView.setBackgroundResource(R.color.long_press);
holder.assessmentCardView.setBackgroundResource(R.color.long_press);
holder.surveyCardView.setBackgroundResource(R.color.long_press);
holder.documentCardView.setBackgroundResource(R.color.long_press);
holder.textCardView.setBackgroundResource(R.color.long_press);
selectedPosition = position;
invalidateOptionsMenu();
getSupportActionBar().setTitle("1 Selected");
} else {
}
}
The variable selectedPosition in onClick and clearLongSelection is initialised in the class as instance variable- selectedPosition = -1.
Use SparseBooleanArray to keep track of selected items in recycler view adapter
Initialize the SparseBooleanArray as private memeber variable
private SparseBooleanArray mClickedItems=new SparseBooleanArray();
Then inside your click function while clicking any item,store the clicked item position as true.
mClickedItems.put(getAdapterPosition(),true);
notifyDataSetChanged();
Then in the onBindViewHolder check if the position is already selected or not like this
if(mClickedItems.get(position)==true){
//Show selected color
}else {
//show unselected color
}

Expand all list items in custom ListView (android)

I am using a normal ListView (mItemsList) with expandable animation (from this tutorial). It works, when I click on list item it expands and shows details for this item.
mItemsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
View details = view.findViewById(R.id.details);
// Creating the expand animation for the item
ExpandAnimation expandAni = new ExpandAnimation(details, 500);
// Start the animation on the toolbar
details.startAnimation(expandAni);
}
});
I created a showDetails button and I want to expand all list items after clicking the button, but I am completely lost. Code below doesn't work
mShowDetailsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < mItemsList.getAdapter().getCount(); i++) {
View details= v.findViewById(R.id.details);
details.startAnimation(new ExpandAnimation(details, 500));
}
}
});
Could you help me?
Here's my list_item xml file
Maybe do the similar approach like the following to automatically click item one by one to open all listciew.
mShowDetailsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < mItemsList.getAdapter().getCount(); i++) {
listView.performItemClick(
getViewByPosition(i),
i,
listView.getAdapter().getItemId(i));
}
}
});
//the listview getChildAt only return the view(item) that is visible,
//therefore add a function to get invisible view together
public View getViewByPosition(int position) {
int firstItemPosition = listView.getFirstVisiblePosition();
int lastItemPosition = firstItemPosition + listView.getChildCount() - 1;
if (position < firstItemPosition || position > lastItemPosition ) {//is invisible
return listView.getAdapter().getView(position, null, listView);
} else {
int childIndex = position - firstItemPosition;//is visible
return listView.getChildAt(childIndex);
}
}
The one you do is not work as the view is refering the mShowDetailsButton ,but not mItemsList. Thus, you cannot findViewof id R.id.details.
mShowDetailsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {//<-- this view not referring to mItemsList
}
});

How to highlight the first row of RecyclerView by default and then remove highlight as and when other rows are selected?

I am trying to load a list in RecyclerView and show the first row of the list as selected. I have achieved it using the following code:
#Override
public void onBindViewHolder(NavigationDrawerAdapter.ViewHolder holder, final int position) {
if (!mNavClassrooms.get(position).equals("")) {
holder.mTextViewClassroom.setText(mNavClassrooms.get(position)); // Setting the Text with the array of our Titles
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, false));
/*
The following code was written to make the first item in the Classroom list as selected.
It leads to the item always being selected and hence has been commented out.
*/
if(position == 0 && intOldSelectedItem == -1){
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, true));
intOldSelectedItem = 0;
mSelectedView = holder.mRelLayClassroom.getChildAt(position);
mSelectedItems.put(position, true);
}
else{
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, false));
}
} else {
holder.mTextViewClassroom.setText("No classes found");
holder.mTextViewClassroom.setPadding(40, 0, 0, 0);
}
holder.mRelLayClassroom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mSharedPreferences = mContext.getSharedPreferences(Constants.AAPREFERENCES, Context.MODE_PRIVATE);
String strClassroomValue = mNavClassrooms.get(position);
int strClassroomName = mNavClassroomNames.get(position);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.CLASSROOM_VALUE, strClassroomValue);
editor.putInt(Constants.CLASSROOM_NAME, strClassroomName);
editor.commit();
/*
We are storing the position of the selected row in the SparseBooleanArray.
We delete it in case another row has been selected.
*/
if (mSelectedItems.get(position, false)) {
/*
Do nothing
*/
} else {
mSelectedItems.put(position, true);
/*
Making sure that the delete code is called only if some view is selected
*/
if (mSelectedView != null) {
mSelectedView.setSelected(false);
mSelectedItems.delete(intOldSelectedItem);
view.setSelected(false);
}
mSelectedView = view;
intOldSelectedItem = position;
view.setSelected(true);
}
}
However, now the first row stays selected always. I am unable to deselect it. I cannot seem to get this working.
I referred to the following answer to achieve most of this functionlaity.
https://stackoverflow.com/a/29984220/2186220
Any help will be appreciated.
I'm not answering your question by posting a fixed version of your onBindViewHolder method since it's kinda hard to understand and we don't know how the rest of your adapter looks like. So following a RecyclerView Adapter which does what you want: Selecting the first row by default and deselecting it once a other row is selected.
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
// ... other fields
// default selection position is the first one
private int selectedPosition = 0;
// ... constructor etc.
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
if(position == selectedPosition){
holder.itemView.setSelected(true);
} else {
holder.itemView.setSelected(false);
}
// Actual selection / deselection logic
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int currentPosition = holder.getLayoutPosition();
if(selectedPosition != currentPosition){
// Temporarily save the last selected position
int lastSelectedPosition = selectedPosition;
// Save the new selected position
selectedPosition = currentPosition;
// update the previous selected row
notifyItemChanged(lastSelectedPosition);
// select the clicked row
holder.itemView.setSelected(true);
}
}
});
// other adapter code
}
// other adapter stuff like onCreateViewHolder, getItemCount, ViewHolder etc.
}
Note: I guess there's no need to use a SparseBooleanArray so simply remove it and replace it with the int field used in the example above.
Initialize your
int intOldSelectedItem=0 and keep one boolean isVisible= false;
And do it as below:
if (holder.getPosition() == intOldSelectedItem) {
if (isVisible) {
//background for selected item
} else {
//background for unselected item
}
} else {
//background for unselected item
}
holder.mRelLayClassroom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (intOldSelectedItem== holder.getPosition()) {
isVisible = !isVisible;
} else {
if (intOldSelectedItem!= 0) {
isVisible = false;
notifyItemChanged(intOldSelectedItem);
}
isVisible = true;
}
intOldSelectedItem= holder.getPosition();
notifyItemChanged(intOldSelectedItem);
}
});
I hope it might help you.
Add background selector to your ViewHolder layout.
Create your selector handler something like this:
public class SingleSelector {
private View oldVIew;
public void setSelection(View newView) {
if (oldVIew == null) {
newView.setSelected(true);
oldVIew = newView;
} else {
oldVIew.setSelected(false);
newView.setSelected(true);
oldVIew = newView;
}
}
}
Set default selection when you need it:
#Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
if (position == 0) {
singleSelector.setSelection(holder.itemView);
}
}
In your ViewHolder add listener to itemView and pass it to the handler:
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
singleSelector.setSelection(itemView);
}
});

Categories

Resources