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.
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>
itemView.getLayoutParams().width = itemView.getWidth() + translation;
, the width will increase to the right, instead of growing to the left? Is that true? – Midtown