Skip to content

Commit 75f0a4e

Browse files
imhappihunterstich
authored andcommitted
[Lists] Add getSwipeState method and demo, and refactored to set necessary swipe elements before drag so they are available for programmatic swipes
PiperOrigin-RevId: 842819821
1 parent f86cedd commit 75f0a4e

File tree

9 files changed

+584
-229
lines changed

9 files changed

+584
-229
lines changed

catalog/java/io/material/catalog/listitem/CustomListItemData.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import android.os.Parcel;
1919
import android.os.Parcelable;
20+
import com.google.android.material.listitem.SwipeableListItem;
2021

2122
/** A sample data class used to represent List Items * */
2223
class CustomListItemData implements Parcelable {
@@ -26,12 +27,14 @@ class CustomListItemData implements Parcelable {
2627
int sectionCount;
2728
String subheading;
2829
boolean expanded;
30+
int swipeState;
2931

3032
public CustomListItemData(String text, int indexInSection, int sectionCount) {
3133
this.text = text;
3234
this.indexInSection = indexInSection;
3335
this.sectionCount = sectionCount;
3436
this.expanded = false;
37+
this.swipeState = SwipeableListItem.STATE_CLOSED;
3538
}
3639

3740
public CustomListItemData(String subheading) {
@@ -45,6 +48,7 @@ protected CustomListItemData(Parcel in) {
4548
sectionCount = in.readInt();
4649
subheading = in.readString();
4750
expanded = in.readByte() != 0;
51+
swipeState = in.readInt();
4852
}
4953

5054
@Override
@@ -55,6 +59,7 @@ public void writeToParcel(Parcel dest, int flags) {
5559
dest.writeInt(sectionCount);
5660
dest.writeString(subheading);
5761
dest.writeByte((byte) (expanded ? 1 : 0));
62+
dest.writeInt(swipeState);
5863
}
5964

6065
@Override

catalog/java/io/material/catalog/listitem/ListsFragment.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ public Fragment createFragment() {
8080
return new ExpandableListDemoFragment();
8181
}
8282
});
83+
additionalDemos.add(
84+
new Demo(R.string.cat_lists_swipe_demo_title) {
85+
@Override
86+
public Fragment createFragment() {
87+
return new SwipeableListDemoFragment();
88+
}
89+
});
8390
return additionalDemos;
8491
}
8592

catalog/java/io/material/catalog/listitem/SegmentedListDemoFragment.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import io.material.catalog.R;
2020

2121
import static android.widget.Adapter.NO_SELECTION;
22-
import static com.google.android.material.listitem.SwipeableListItem.STATE_SWIPE_PRIMARY_ACTION;
2322

2423
import android.os.Bundle;
2524
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -35,10 +34,7 @@
3534
import androidx.annotation.NonNull;
3635
import androidx.annotation.Nullable;
3736
import com.google.android.material.listitem.ListItemCardView;
38-
import com.google.android.material.listitem.ListItemCardView.SwipeCallback;
39-
import com.google.android.material.listitem.ListItemLayout;
4037
import com.google.android.material.listitem.ListItemViewHolder;
41-
import com.google.android.material.listitem.SwipeableListItem;
4238
import java.util.ArrayList;
4339
import java.util.List;
4440

@@ -164,26 +160,6 @@ public void bind(@NonNull CustomListItemData data) {
164160
adapter.notifyItemChanged(previouslySelectedPosition);
165161
}
166162
});
167-
168-
cardView.addSwipeCallback(
169-
new SwipeCallback() {
170-
@Override
171-
public void onSwipe(int swipeOffset) {}
172-
173-
@Override
174-
public void onSwipeStateChanged(int newState) {
175-
if (newState == STATE_SWIPE_PRIMARY_ACTION) {
176-
Toast.makeText(
177-
cardView.getContext(),
178-
R.string.cat_list_item_primary_action,
179-
Toast.LENGTH_SHORT)
180-
.show();
181-
itemView.postDelayed(
182-
() -> ((ListItemLayout) itemView).setSwipeState(SwipeableListItem.STATE_CLOSED),
183-
500);
184-
}
185-
}
186-
});
187163
}
188164
}
189165

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.material.catalog.listitem;
18+
19+
import io.material.catalog.R;
20+
21+
import static com.google.android.material.listitem.SwipeableListItem.STATE_CLOSED;
22+
import static com.google.android.material.listitem.SwipeableListItem.STATE_OPEN;
23+
import static com.google.android.material.listitem.SwipeableListItem.STATE_SWIPE_PRIMARY_ACTION;
24+
25+
import android.graphics.drawable.Drawable;
26+
import android.os.Bundle;
27+
import androidx.recyclerview.widget.LinearLayoutManager;
28+
import androidx.recyclerview.widget.RecyclerView;
29+
import androidx.recyclerview.widget.RecyclerView.Adapter;
30+
import android.view.LayoutInflater;
31+
import android.view.View;
32+
import android.view.ViewGroup;
33+
import android.widget.TextView;
34+
import android.widget.Toast;
35+
import androidx.annotation.NonNull;
36+
import androidx.annotation.Nullable;
37+
import com.google.android.material.button.MaterialButton;
38+
import com.google.android.material.listitem.ListItemCardView;
39+
import com.google.android.material.listitem.ListItemCardView.SwipeCallback;
40+
import com.google.android.material.listitem.ListItemLayout;
41+
import com.google.android.material.listitem.ListItemViewHolder;
42+
import com.google.android.material.listitem.SwipeableListItem;
43+
import java.util.ArrayList;
44+
import java.util.List;
45+
46+
/** A fragment that displays a swipeable List demo for the Catalog app. */
47+
public class SwipeableListDemoFragment extends ListsMainDemoFragment {
48+
49+
private static final String KEY_LIST_DATA = "key_list_data";
50+
51+
private ArrayList<CustomListItemData> listData;
52+
private ListsAdapter adapter;
53+
54+
@NonNull
55+
@Override
56+
public View onCreateDemoView(
57+
@NonNull LayoutInflater layoutInflater,
58+
@Nullable ViewGroup viewGroup,
59+
@Nullable Bundle bundle) {
60+
RecyclerView view =
61+
(RecyclerView)
62+
layoutInflater.inflate(R.layout.cat_lists_bright_background_fragment, viewGroup, false);
63+
64+
view.setLayoutManager(new LinearLayoutManager(getContext()));
65+
if (bundle != null) {
66+
listData = bundle.getParcelableArrayList(KEY_LIST_DATA);
67+
} else {
68+
listData = new ArrayList<>();
69+
for (int i = 0; i < 20; i++) {
70+
listData.add(
71+
new CustomListItemData(
72+
String.format(view.getContext().getString(R.string.cat_list_item_text), i + 1),
73+
i,
74+
20));
75+
}
76+
}
77+
78+
adapter = new ListsAdapter(listData);
79+
view.setAdapter(adapter);
80+
view.addItemDecoration(new MarginItemDecoration(getContext()));
81+
82+
return view;
83+
}
84+
85+
/** An Adapter that shows custom list items */
86+
public static class ListsAdapter extends Adapter<CustomItemViewHolder> {
87+
private final List<CustomListItemData> items;
88+
89+
public ListsAdapter(@NonNull List<CustomListItemData> items) {
90+
this.items = items;
91+
}
92+
93+
@NonNull
94+
@Override
95+
public CustomItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
96+
ViewGroup item =
97+
(ViewGroup)
98+
LayoutInflater.from(parent.getContext())
99+
.inflate(
100+
R.layout.cat_list_item_swipeable_viewholder,
101+
parent,
102+
/* attachToRoot= */ false);
103+
return new CustomItemViewHolder(item);
104+
}
105+
106+
@Override
107+
public void onBindViewHolder(
108+
@NonNull CustomItemViewHolder viewHolder, int position) {
109+
CustomListItemData data = getItemAt(position);
110+
viewHolder.bind(data);
111+
}
112+
113+
@Override
114+
public int getItemCount() {
115+
return items.size();
116+
}
117+
118+
@NonNull
119+
public CustomListItemData getItemAt(int i) {
120+
return items.get(i);
121+
}
122+
}
123+
124+
/** A ViewHolder that shows custom list items */
125+
public static class CustomItemViewHolder extends ListItemViewHolder {
126+
private CustomListItemData data;
127+
private final TextView textView;
128+
private final ListItemCardView cardView;
129+
private final ListItemLayout listItemLayout;
130+
private final MaterialButton endActionsButton;
131+
private final MaterialButton addActionButton;
132+
private final MaterialButton starActionButton;
133+
private final Drawable backArrow;
134+
private final Drawable forwardArrow;
135+
136+
public CustomItemViewHolder(@NonNull View itemView) {
137+
super(itemView);
138+
backArrow = itemView.getResources().getDrawable(R.drawable.ic_arrow_back_24px);
139+
forwardArrow = itemView.getResources().getDrawable(R.drawable.ic_arrow_forward_24px);
140+
listItemLayout = (ListItemLayout) itemView;
141+
textView = itemView.findViewById(R.id.cat_list_item_text);
142+
cardView = itemView.findViewById(R.id.cat_list_item_card_view);
143+
addActionButton = itemView.findViewById(R.id.cat_list_action_add_button);
144+
starActionButton = itemView.findViewById(R.id.cat_list_action_star_button);
145+
endActionsButton = itemView.findViewById(R.id.cat_list_item_end_icon);
146+
147+
addActionButton.setOnClickListener(
148+
v -> Toast.makeText(v.getContext(), R.string.cat_list_item_add_action_clicked, Toast.LENGTH_SHORT)
149+
.show());
150+
starActionButton.setOnClickListener(
151+
v -> Toast.makeText(v.getContext(), R.string.cat_list_item_star_action_clicked, Toast.LENGTH_SHORT)
152+
.show());
153+
endActionsButton.setOnClickListener(
154+
v -> {
155+
if (listItemLayout.getSwipeState() == STATE_CLOSED) {
156+
listItemLayout.setSwipeState(STATE_OPEN);
157+
} else {
158+
listItemLayout.setSwipeState(STATE_CLOSED);
159+
}
160+
});
161+
cardView.setOnClickListener(
162+
v -> {
163+
Toast.makeText(v.getContext(), R.string.mtrl_list_item_clicked, Toast.LENGTH_SHORT)
164+
.show();
165+
});
166+
167+
cardView.addSwipeCallback(
168+
new SwipeCallback() {
169+
@Override
170+
public void onSwipe(int swipeOffset) {}
171+
172+
@Override
173+
public void onSwipeStateChanged(int newState) {
174+
if (data == null) {
175+
return;
176+
}
177+
if (newState == STATE_SWIPE_PRIMARY_ACTION) {
178+
Toast.makeText(
179+
cardView.getContext(),
180+
R.string.cat_list_item_primary_action,
181+
Toast.LENGTH_SHORT)
182+
.show();
183+
itemView.postDelayed(
184+
() -> listItemLayout.setSwipeState(SwipeableListItem.STATE_CLOSED),
185+
500);
186+
}
187+
data.swipeState = newState;
188+
endActionsButton.setIcon(data.swipeState == STATE_CLOSED ? forwardArrow : backArrow);
189+
endActionsButton.setContentDescription(
190+
data.swipeState == STATE_CLOSED
191+
? itemView
192+
.getContext()
193+
.getString(R.string.cat_list_item_show_end_actions_content_description)
194+
: itemView
195+
.getContext()
196+
.getString(R.string.cat_list_item_hide_end_actions_content_description));
197+
}
198+
});
199+
}
200+
201+
public void bind(@NonNull CustomListItemData data) {
202+
super.bind();
203+
this.data = data;
204+
textView.setText(data.text);
205+
endActionsButton.setIcon(data.swipeState == STATE_CLOSED ? forwardArrow : backArrow);
206+
listItemLayout.setSwipeState(data.swipeState, /* animate= */ false);
207+
}
208+
}
209+
210+
@Override
211+
public void onSaveInstanceState(@NonNull Bundle outState) {
212+
super.onSaveInstanceState(outState);
213+
outState.putParcelableArrayList(KEY_LIST_DATA, listData);
214+
}
215+
}

catalog/java/io/material/catalog/listitem/res/layout/cat_list_item_segmented_viewholder.xml

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929
android:clickable="true"
3030
android:focusable="true"
3131
android:layout_height="wrap_content"
32-
android:layout_width="match_parent"
33-
app:swipeToPrimaryActionEnabled="true">
32+
android:layout_width="match_parent">
3433
<LinearLayout
3534
android:gravity="center_vertical"
3635
android:layout_height="wrap_content"
@@ -70,22 +69,4 @@
7069
android:layout_marginStart="12dp"/>
7170
</LinearLayout>
7271
</com.google.android.material.listitem.ListItemCardView>
73-
74-
<com.google.android.material.listitem.ListItemRevealLayout
75-
android:layout_height="match_parent"
76-
android:layout_width="wrap_content">
77-
<com.google.android.material.button.MaterialButton
78-
style="?attr/materialIconButtonFilledTonalStyle"
79-
android:layout_height="match_parent"
80-
android:layout_width="64dp"
81-
android:layout_marginEnd="2dp"
82-
android:layout_marginStart="2dp"
83-
app:icon="@drawable/ic_add_24px" />
84-
<com.google.android.material.button.MaterialButton
85-
style="?attr/materialIconButtonFilledStyle"
86-
android:layout_height="match_parent"
87-
android:layout_width="64dp"
88-
android:layout_marginEnd="2dp"
89-
app:icon="@drawable/ic_star_icon_checkable_24px" />
90-
</com.google.android.material.listitem.ListItemRevealLayout>
9172
</com.google.android.material.listitem.ListItemLayout>

0 commit comments

Comments
 (0)