Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Increase cell width in a RecyclerView with GridLayoutManager

I have a RecyclerView with a GridLayoutManager with spanCount=2 and a Vertical orientation.

My items are correctly displayed as the image below:

Now I need to add an animation that when a click on one of the items, let's say number "3", that item increases its width and push the item next to it (in this example, number "4") partially outside the parent/screen.

Visually, it would be something like this:

To expand the item I am setting the visibility to VISIBLE to a view inside the item and to collapse it, set the visibility to GONE.

At the moment, I am able to show and hide that view, but it only takes the space of the item, it does increase the width pushing the item next to it.

So my questions are:

  • Is this possible to use the default GridLayoutManager for this?
  • What would be a good approach to achieve this?
like image 823
amp Avatar asked Nov 20 '18 10:11

amp


1 Answers

It is possible to useGridLayoutManager as follows:

  • Define the GridLayoutManager with two spans.
  • Set up an item layout that incorporates the clickable view and the view that will be set to VISIBLE and GONE.
  • In the adapter, when, say, the left view is clicked, set the expandable view to VISIBLE, increase the size of the itemView by the width of the expandable view. Translate the view to the right by the amount of the expansion to accommodate the increased size of the left view.
  • Do some housekeeping to make sure that recycled views are reset.

Here is an example. I assume that only the left view can be clicked. The general process is the same for the right view if if it can also be clicked, but the details differ in what needs to be translated, etc.

This example does not retained information of the status of view that are involved in an expansion, so if you expand a view then scroll down and back up, the view may be reset.

enter image description here

MainActivity.java

public class MainActivity extends AppCompatActivity {  
    private LinearLayoutManager mLayoutManager;  
    private RecyclerViewAdapter mAdapter;  
    private List<String> mItems = new ArrayList<>();  
    private RecyclerView mRecycler;  

    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  

        for (int i = 0; i < 100; i++) {  
            mItems.add(i + 1 + "");  
        }  

        mLayoutManager = new GridLayoutManager(this, 2);  
        mRecycler = findViewById(R.id.recyclerView);  
        mAdapter = new RecyclerViewAdapter(mItems);  
        mRecycler.setLayoutManager(mLayoutManager);  
        mRecycler.setAdapter(mAdapter);  
    }  
}

RecyclerViewAdapter.java
This demo adapter assumes that there are an even number of items to display. For simplicity, null checks have not been coded.

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
    implements View.OnClickListener {
    private final List<String> mItems;
    private RecyclerView mRecyclerView;

    RecyclerViewAdapter(List<String> items) {
        mItems = items;
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
    }

    @Override
    public @NonNull
    RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        ItemViewHolder vh = (ItemViewHolder) holder;

        vh.mItemTextView.setText(mItems.get(position));

        // Only allow clicks on left items which corresponds to even positions.
        vh.mItemTextView.setOnClickListener((position % 2 == 0) ? this : null);

        // Reset translation and expansion if viewholder is reused.
        vh.itemView.setTranslationX(0);
        if (vh.mExpansion != 0) {
            vh.itemView.getLayoutParams().width -= vh.mExpansion;
            vh.mExpansion = 0;
            vh.mGoneView.setVisibility(View.GONE);
        }

        int bgColor = (position % 2 == 0)
            ? android.R.color.holo_blue_light
            : android.R.color.holo_green_light;
        vh.mItemTextView.setBackgroundColor(vh.itemView.getContext().getResources().getColor(bgColor));
    }

    @Override
    public int getItemCount() {
        return (mItems == null) ? 0 : mItems.size();
    }

    @Override
    public int getItemViewType(int position) {
        return TYPE_ITEM;
    }

    @Override
    public void onClick(View v) {
        ItemViewHolder vh = (ItemViewHolder) mRecyclerView.findContainingViewHolder(v);
        View itemView = vh.itemView;

        // Get the child to the right. This child will be translated to the right to make room
        // for the expanded left view.
        View rightChild = mRecyclerView.getChildAt(findRightChildPos(vh.itemView));
        if (vh.mGoneView.getVisibility() == View.GONE) {
            // Reveal the "GONE" view, expand the itemView and translate the right-hand view.
            vh.mGoneView.setVisibility(View.VISIBLE);
            int translation = vh.mGoneView.getLayoutParams().width;
            itemView.getLayoutParams().width = itemView.getWidth() + translation;

            // Works with "GONE" view of fixed width. Make adjustments if width is variable.
            rightChild.setTranslationX(translation);
            vh.mExpansion = translation;
        } else { // View is expanded.
            // Undo the expansion changes.
            vh.mGoneView.setVisibility(View.GONE);
            itemView.getLayoutParams().width = itemView.getWidth() - vh.mExpansion;
            vh.mExpansion = 0;
            rightChild.setTranslationX(0);
        }
    }

    // Find the child to the right of a view within the RecyclerView.
    private int findRightChildPos(View view) {
        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
            if (mRecyclerView.getChildAt(i) == view) {
                return i + 1;
            }
        }
        return RecyclerView.NO_POSITION;
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        final TextView mItemTextView;
        final View mGoneView;
        int mExpansion;

        ItemViewHolder(View item) {
            super(item);
            mItemTextView = item.findViewById(R.id.textView);
            mGoneView = item.findViewById(R.id.expandingView);
        }
    }

    private final static int TYPE_ITEM = 1;
}

recycler_item.xml

<LinearLayout 
    android:id="@+id/item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    android:clickable="true"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:textSize="48sp"
        tools:background="@android:color/holo_blue_light"
        tools:text="1" />

    <View
        android:id="@+id/expandingView"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:background="@android:color/holo_purple"
        android:visibility="gone" />

    <View
        android:layout_width="10dp"
        android:layout_height="match_parent"
        android:background="@android:color/holo_red_light" />
</LinearLayout>
like image 170
Cheticamp Avatar answered Oct 08 '22 07:10

Cheticamp