Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collapsing all other Recyclerview items when one is expanded

Tags:

java

android

I have a well-setup expandable recyclerview. When clicked on any item, it gets expanded to show more details. But I want a change here. When one item is clicked to expand, all other items should be collapsed. Here's my adapter code:

public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
    private ArrayList<ListItem> android_versions;
    private List<ListItem> listItems;
    private LayoutInflater layoutInflater;
    private Animation animationUp, animationDown;
    private Context context;
    private final int COUNTDOWN_RUNNING_TIME = 500;
    int i =0;

    public DataAdapter(Context context, java.util.List<ListItem> android_versions,
                       Animation animationUp, Animation animationDown) {
        this.context = context;
        this.listItems = android_versions;
        this.animationDown = animationDown;
        this.animationUp = animationUp;
    }

    @Override
    public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_layout, viewGroup, false);
        return new ViewHolder(view);
    }

    public String getRandomColor(int a){
        String colors[] = {"#ef5350","#f44336","#ef5350"};
        return colors[a];
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int i) {
        ListItem listItem = listItems.get(i);
        if (i%2==2)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(0)));
        else if (i%2==1)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(1)));
        else if (i%2==0)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(2)));

        holder.tv_android.setText(listItem.getSubName());
        holder.contentLayout.setText(listItem.getContent());
        holder.expandit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (holder.contentLayout.isShown()) {
                    holder.contentLayout.startAnimation(animationUp);
                    CountDownTimer countDownTimerStatic = new CountDownTimer(COUNTDOWN_RUNNING_TIME, 16) {
                        @Override
                        public void onTick(long millisUntilFinished) {
                        }

                        @Override
                        public void onFinish() {
                            holder.contentLayout.setVisibility(View.GONE);
                        }
                    };
                    countDownTimerStatic.start();
                } else {
                    holder.contentLayout.setVisibility(View.VISIBLE);
                    holder.contentLayout.startAnimation(animationDown);
                }
            }
        });
    }
    @Override
    public int getItemCount() {
        return listItems.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView tv_android;
        private TextView contentLayout;
        RelativeLayout expandit;
        public ViewHolder(View view) {
            super(view);
            tv_android = (TextView)view.findViewById(R.id.tv_android);
            contentLayout = (TextView) view.findViewById(R.id.content);
            expandit = (RelativeLayout)view.findViewById(R.id.expandit);
        }
    }
}

I think it's not possible to change viewHolder's content by position. So how can I hide all other items' content by position. I don't think other code is required so I haven't included it.

Edit: Code after Ashish's suggestion:

  private void changeStateOfItemsInLayout(ListItem listItem) {
        for (int i = 0; i < listItems.size(); i++) {
            if (listItems.get(i) == listItem)
                listItem.setShouldBeExpanded(true);
            else
                listItem.setShouldBeExpanded(false);
        }
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int i) {
        ListItem listItem = listItems.get(i);
        if (i%2==2)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(0)));
        else if (i%2==1)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(1)));
        else if (i%2==0)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(2)));

        holder.tv_android.setText(listItem.getSubName());
        holder.contentLayout.setText(listItem.getContent());
        if(listItem.getShouldBeExpanded()){
            holder.contentLayout.setVisibility(View.VISIBLE);
            holder.contentLayout.startAnimation(animationDown);
        }else{
            holder.contentLayout.startAnimation(animationUp);
            CountDownTimer countDownTimerStatic = new CountDownTimer(COUNTDOWN_RUNNING_TIME, 16) {
                @Override
                public void onTick(long millisUntilFinished) {
                }

                @Override
                public void onFinish() {
                    holder.contentLayout.setVisibility(View.GONE);
                }
            };
            countDownTimerStatic.start();
        }

        holder.expandit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (holder.contentLayout.isShown()) {
                    holder.contentLayout.startAnimation(animationUp);
                    CountDownTimer countDownTimerStatic = new CountDownTimer(COUNTDOWN_RUNNING_TIME, 16) {
                        @Override
                        public void onTick(long millisUntilFinished) {
                        }

                        @Override
                        public void onFinish() {
                            holder.contentLayout.setVisibility(View.GONE);
                        }
                    };
                    countDownTimerStatic.start();
                } else {
                    holder.contentLayout.setVisibility(View.VISIBLE);
                    holder.contentLayout.startAnimation(animationDown);
                }
                listItems.get(holder.getLayoutPosition()).setShouldBeExpanded(true);
                changeStateOfItemsInLayout(listItems.get(holder.getLayoutPosition()));
                notifyDataSetChanged();
            }
        });
    }

Although in the onClick, I still have the code to expand/collapse layout, even if I remove it, it doesn't work.

Here's my modal class if required:

public class ListItem {

    private String subName;
    private String content;
    private String param;
    private Boolean shouldBeExpanded=false;

    public Boolean getShouldBeExpanded() {
        return shouldBeExpanded;
    }

    public void setShouldBeExpanded(Boolean shouldBeExpanded) {
        this.shouldBeExpanded = shouldBeExpanded;
    }

    public ListItem(String subName, String content) {
        this.subName = subName;
        this.content = content;
        this.param = "Nothing";
    }

    public String getParam() {
        return param;
    }

    public ListItem(String subName, String content, String param) {

        this.subName = subName;
        this.content = content;
        this.param = param;
    }

    public String getSubName() {
        return subName;
    }

    public String getContent() {
        return content;
    }
}

EDIT: As requested, here's the code for animationUp file and animationDown file:

animationUp.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true" >

    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/linear_interpolator"
        android:toXScale="1.0"
        android:toYScale="0.0" />

</set>

animationDown.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="0.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXScale="1.0"
        android:toYScale="1.0" />

</set>
like image 235
Paras Sidhu Avatar asked Jun 27 '17 16:06

Paras Sidhu


2 Answers

Thanks to Ashish, I solved the problem with a simple logic. Here's my redefined changeStateOfItemsInLayout function code:

private void changeStateOfItemsInLayout(ListItem listItem, int p) {
        for (int x = 0; x < listItems.size(); x++) {
            if (x == p) {
                listItem.setShouldBeExpanded(true);
                //Since this is the tapped item, we will skip
                //the rest of loop for this item and set it expanded
                continue;
            }
            listItems.get(x).setShouldBeExpanded(false);
        }
    }

This function is responsible for collapsing all other items. It checks which option is tapped and expands it (If already expanded, we won't call this function from OnBindViewHolder function) and collapses all others.

Here's my full adapter code:

public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
    private ArrayList<ListItem> android_versions;
    private List<ListItem> listItems;
    private LayoutInflater layoutInflater;
    private Animation animationUp, animationDown;
    private Context context;
    private final int COUNTDOWN_RUNNING_TIME = 300;

    public DataAdapter(Context context, java.util.List<ListItem> android_versions,
                       Animation animationUp, Animation animationDown) {
        this.context = context;
        this.listItems = android_versions;
        this.animationDown = animationDown;
        this.animationUp = animationUp;
    }

    @Override
    public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_layout, viewGroup, false);
        return new ViewHolder(view);
    }

    public String getRandomColor(int a){
        String colors[] = {"#ef5350","#f44336","#ef5350"};
        return colors[a];
    }

    private void changeStateOfItemsInLayout(ListItem listItem, int p) {
        for (int x = 0; x < listItems.size(); x++) {
            if (x == p) {
                listItem.setShouldBeExpanded(true);
                //Since this is the tapped item, we will skip
                //the rest of loop for this item and set it expanded
                continue;
            }
            listItems.get(x).setShouldBeExpanded(false);
        }
    }


    @Override
    public void onBindViewHolder(final ViewHolder holder, int i) {
        ListItem listItem = listItems.get(i);
        if (i%2==2)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(0)));
        else if (i%2==1)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(1)));
        else if (i%2==0)
            holder.itemView.setBackgroundColor(Color.parseColor(getRandomColor(2)));

        holder.tv_android.setText(listItem.getSubName());
        holder.contentLayout.setText(listItem.getContent());
        if(listItem.getShouldBeExpanded()){
            holder.contentLayout.setVisibility(View.VISIBLE);
            holder.contentLayout.startAnimation(animationDown);
        }else{
            holder.contentLayout.startAnimation(animationUp);
            CountDownTimer countDownTimerStatic = new CountDownTimer(COUNTDOWN_RUNNING_TIME, 16) {
                @Override
                public void onTick(long millisUntilFinished) {}
                @Override
                public void onFinish() {
                    holder.contentLayout.setVisibility(View.GONE);
                }
            };
            countDownTimerStatic.start();
        }

        holder.expandit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (holder.contentLayout.isShown()) {
                    listItems.get(holder.getLayoutPosition()).setShouldBeExpanded(false);
                } else {
                    listItems.get(holder.getLayoutPosition()).setShouldBeExpanded(true);
                    changeStateOfItemsInLayout(listItems.get(holder.getLayoutPosition()),holder.getLayoutPosition());
                }
                notifyDataSetChanged();
            }
        });
    }
    @Override
    public int getItemCount() {
        return listItems.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView tv_android;
        private TextView contentLayout;
        RelativeLayout expandit;
        public ViewHolder(View view) {
            super(view);
            tv_android = (TextView)view.findViewById(R.id.tv_android);
            contentLayout = (TextView) view.findViewById(R.id.content);
            expandit = (RelativeLayout)view.findViewById(R.id.expandit);
        }
    }
}
like image 132
Paras Sidhu Avatar answered Sep 30 '22 08:09

Paras Sidhu


In your ListItem class which i am assuming is the model class, add a new boolean variable shouldBeExpanded and set its default value to false, and write its getter and setter. Now in your adapter, in onBindViewHolder write the following code

if(listItem.shouldBeExpanded()){
//write your code to expand it
}else{
//write the code to collapse
}

Now in the code section, in some onClick which expands the layout, set this variable for this lisItem to be true and call a function which sets all other items in the listItem to false for shouldBeExpanded..somewhat by calling the below function changeStateOfItemsInLayout(listItem). Now called notifiyDataSetChanged().

private void changeStateOfItemsInLayout(ListItem listItem){
for(int i=0;i<listItems.size();i++){
if(listItems.get(i)==listItem)
listItem.get(i).setShouldBeExpanded(true);
}else{
listItem.get(i).setShouldBeExpanded(false);
}
}

This should solve your purpose.

like image 42
Ashish Kumar Avatar answered Sep 30 '22 08:09

Ashish Kumar