Skip to content
This repository was archived by the owner on Dec 27, 2024. It is now read-only.

Commit 3fcd33a

Browse files
authored
Detail Expansion example (#759)
1 parent e183d67 commit 3fcd33a

File tree

7 files changed

+365
-0
lines changed

7 files changed

+365
-0
lines changed

demoProjects/ExamplesRecyclerView/app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@
5555
<category android:name="android.intent.category.LAUNCHER" />
5656
</intent-filter>
5757
</activity>
58+
<activity android:name="com.example.motionrecycle.RecyclerToDetailView"
59+
android:exported="true">
60+
<intent-filter>
61+
<action android:name="android.intent.action.MAIN" />
62+
<category android:name="android.intent.category.LAUNCHER" />
63+
</intent-filter>
64+
</activity>
5865
<activity
5966
android:name=".MainActivity"
6067
android:exported="true">
@@ -68,6 +75,7 @@
6875
android:name="android.app.lib_name"
6976
android:value="" />
7077
</activity>
78+
7179
</application>
7280

7381
</manifest>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.example.motionrecycle;
2+
3+
import android.graphics.Bitmap;
4+
import android.graphics.Canvas;
5+
import android.graphics.Color;
6+
import android.graphics.Paint;
7+
import android.graphics.Rect;
8+
import android.os.Bundle;
9+
import android.view.LayoutInflater;
10+
import android.view.View;
11+
import android.view.ViewGroup;
12+
import android.widget.ImageView;
13+
14+
import androidx.appcompat.app.AppCompatActivity;
15+
import androidx.constraintlayout.motion.widget.MotionLayout;
16+
import androidx.constraintlayout.utils.widget.ImageFilterButton;
17+
import androidx.constraintlayout.widget.ConstraintSet;
18+
import androidx.recyclerview.widget.LinearLayoutManager;
19+
import androidx.recyclerview.widget.RecyclerView;
20+
21+
import java.util.Random;
22+
23+
public class RecyclerToDetailView extends AppCompatActivity {
24+
int[] mColor = new int[200];
25+
26+
@Override
27+
protected void onCreate(Bundle savedInstanceState) {
28+
super.onCreate(savedInstanceState);
29+
setContentView(R.layout.recycler_to_detail);
30+
RecyclerView recyclerView = findViewById(R.id.recyclerView);
31+
MotionLayout base = findViewById(R.id.base_motionLayout);
32+
Random random = new Random();
33+
for (int i = 0; i < mColor.length; i++) {
34+
mColor[i] = random.nextInt(0xFFFFFF) | 0xFF000000;
35+
}
36+
recyclerView.setAdapter(new CustomAdapter(recyclerView, mColor, base));
37+
recyclerView.setLayoutManager(new LinearLayoutManager(this));
38+
}
39+
40+
static class ColorBitmap {
41+
private final Paint paint = new Paint();
42+
private final float[] hsv = new float[3];
43+
private final Canvas canvas;
44+
private final float y_offset;
45+
private final Rect bounds = new Rect();
46+
public final Bitmap bitmap;
47+
public int color;
48+
49+
ColorBitmap() {
50+
bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
51+
canvas = new Canvas(bitmap);
52+
paint.setTextSize(64);
53+
y_offset = paint.getFontMetrics().descent;
54+
}
55+
56+
void update(int color) {
57+
this.color = color;
58+
paint.setColor(color);
59+
canvas.drawRoundRect(0, 0, 256, 256, 128, 128, paint);
60+
Color.colorToHSV(color, hsv);
61+
hsv[0] = (hsv[0] + 180) % 360;
62+
hsv[1] = 1 - hsv[1];
63+
hsv[2] = 1 - hsv[2];
64+
paint.setColor(Color.HSVToColor(hsv));
65+
String s = Integer.toHexString(color).substring(2);
66+
paint.getTextBounds(s, 0, 6, bounds);
67+
canvas.drawText(s, (256 - bounds.right) / 2f, 128 + y_offset, paint);
68+
}
69+
}
70+
71+
// ========================= The RecyclerView adapter =====================
72+
static class CustomAdapter extends RecyclerView.Adapter<CustomViewHolder> {
73+
private final MotionLayout mBaseMotionLayout;
74+
private final int[] mLocalDataSet;
75+
RecyclerView mRecyclerView;
76+
77+
public CustomAdapter(RecyclerView recyclerView, int[] colors, MotionLayout base) {
78+
mBaseMotionLayout = base;
79+
mRecyclerView = recyclerView;
80+
mLocalDataSet = colors;
81+
}
82+
83+
@Override
84+
public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
85+
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
86+
View view = inflater.inflate(R.layout.recycler_to_detail_item, viewGroup, false);
87+
88+
return new CustomViewHolder(view, mBaseMotionLayout);
89+
}
90+
91+
@Override
92+
public void onBindViewHolder(CustomViewHolder viewHolder, final int position) {
93+
viewHolder.bind(position, mLocalDataSet[position]);
94+
}
95+
96+
@Override
97+
public int getItemCount() {
98+
return mLocalDataSet.length;
99+
}
100+
}
101+
102+
// ========================= The View Holder adapter =====================
103+
public static class CustomViewHolder extends RecyclerView.ViewHolder {
104+
private final ImageFilterButton button;
105+
private final ImageView imageView;
106+
private final MotionLayout mMotionLayout;
107+
private final MotionLayout mBaseMotionLayout;
108+
ColorBitmap colorBitmap = new ColorBitmap();
109+
private int mPosition = -1;
110+
111+
public CustomViewHolder(View view, MotionLayout baseMotionLayout) {
112+
super(view);
113+
mBaseMotionLayout = baseMotionLayout;
114+
mMotionLayout = (MotionLayout) view;
115+
button = view.findViewById(R.id.button);
116+
imageView = view.findViewById(R.id.image);
117+
button.setOnClickListener(this::click);
118+
}
119+
120+
// use CustomViewHolder to bind expand version
121+
private CustomViewHolder(View view, MotionLayout baseMotionLayout, int color) {
122+
super(view);
123+
mBaseMotionLayout = baseMotionLayout;
124+
mMotionLayout = (MotionLayout) view;
125+
button = view.findViewById(R.id.button);
126+
imageView = view.findViewById(R.id.image);
127+
colorBitmap.update(color);
128+
button.setOnClickListener((v) -> mBaseMotionLayout.transitionToStart());
129+
}
130+
131+
public void click(View v) {
132+
CustomViewHolder detailed = (CustomViewHolder) mBaseMotionLayout.getTag(R.id.base_motionLayout);
133+
MotionLayout child = mBaseMotionLayout.findViewById(R.id.detail);
134+
if (detailed == null) {
135+
detailed = new CustomViewHolder(child, mBaseMotionLayout, colorBitmap.color);
136+
mBaseMotionLayout.setTag(R.id.base_motionLayout, detailed);
137+
}
138+
int[] pos = new int[2];
139+
detailed.bind(mPosition, colorBitmap.color);
140+
ConstraintSet cs = mBaseMotionLayout.getConstraintSet(R.id.start);
141+
mBaseMotionLayout.getLocationInWindow(pos);
142+
int x = pos[0], y = pos[1];
143+
mMotionLayout.getLocationInWindow(pos);
144+
pos[0] -= x;
145+
pos[1] -= y;
146+
cs.setMargin(R.id.detail, ConstraintSet.TOP, pos[1]);
147+
mBaseMotionLayout.updateState(R.id.start, cs);
148+
mBaseMotionLayout.transitionToEnd();
149+
}
150+
151+
public void bind(int position, int color) {
152+
mPosition = position;
153+
colorBitmap.update(color);
154+
imageView.setImageBitmap(colorBitmap.bitmap);
155+
}
156+
}
157+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:height="24dp" android:tint="#000000"
2+
android:viewportHeight="24" android:viewportWidth="24"
3+
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="@android:color/white" android:pathData="M21,11l0,-8l-8,0l3.29,3.29l-10,10l-3.29,-3.29l0,8l8,0l-3.29,-3.29l10,-10z"/>
5+
</vector>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
android:background="#1E0A1E"
8+
app:layoutDescription="@xml/recycler_to_detail_scene"
9+
android:id="@+id/base_motionLayout"
10+
tools:context=".MainActivity">
11+
12+
<androidx.recyclerview.widget.RecyclerView
13+
android:id="@+id/recyclerView"
14+
android:layout_width="match_parent"
15+
android:layout_height="match_parent"
16+
android:text="Hello World!"
17+
app:layout_constraintBottom_toBottomOf="parent"
18+
app:layout_constraintEnd_toEndOf="parent"
19+
app:layout_constraintStart_toStartOf="parent"
20+
app:layout_constraintTop_toTopOf="parent" />
21+
22+
<!-- <FrameLayout-->
23+
<!-- android:id="@+id/frameLayout"-->
24+
<!-- android:layout_width="wrap_content"-->
25+
<!-- android:layout_height="wrap_content"-->
26+
27+
<!-- android:visibility="invisible"-->
28+
<!-- app:layout_constraintStart_toStartOf="parent"-->
29+
<!-- app:layout_constraintTop_toTopOf="parent">-->
30+
31+
<include layout="@layout/recycler_to_detail_item" android:id="@+id/detail" android:layout_width="match_parent"
32+
android:layout_height="100dp" />
33+
<!-- </FrameLayout>-->
34+
35+
</androidx.constraintlayout.motion.widget.MotionLayout>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.motion.widget.MotionLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
android:layout_width="match_parent"
6+
android:layout_height="100dp"
7+
android:id="@+id/expand_child"
8+
android:background="#9FF"
9+
10+
app:layoutDescription="@xml/recycler_to_detail_item_scene">
11+
<View
12+
android:id="@+id/backdrop"
13+
android:layout_width="match_parent"
14+
android:layout_height="match_parent"
15+
android:layout_marginStart="1dp"
16+
android:layout_marginTop="2dp"
17+
android:layout_marginBottom="2dp"
18+
android:layout_marginEnd="1dp"
19+
android:background="#256671"
20+
/>
21+
22+
<ImageView
23+
android:id="@+id/image"
24+
android:layout_width="0dp"
25+
android:layout_height="0dp"
26+
app:layout_constraintDimensionRatio="1:1"
27+
android:layout_marginBottom="4dp"
28+
android:layout_marginTop="4dp"
29+
android:layout_marginEnd="4dp"
30+
app:layout_constraintTop_toTopOf="parent"
31+
app:layout_constraintBottom_toBottomOf="parent"
32+
app:layout_constraintEnd_toEndOf="parent"
33+
/>
34+
<androidx.constraintlayout.utils.widget.ImageFilterButton
35+
android:id="@+id/button"
36+
android:layout_width="50dp"
37+
android:layout_height="50dp"
38+
android:src="@drawable/expand_icon"
39+
android:background="#AAA"
40+
app:roundPercent="1"
41+
app:layout_constraintBottom_toBottomOf="parent"
42+
app:layout_constraintEnd_toEndOf="parent"
43+
app:layout_constraintHorizontal_bias="0.1"
44+
app:layout_constraintStart_toStartOf="parent"
45+
app:layout_constraintTop_toTopOf="parent" />
46+
47+
</androidx.constraintlayout.motion.widget.MotionLayout>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:motion="http://schemas.android.com/apk/res-auto">
4+
5+
<Transition
6+
motion:constraintSetEnd="@+id/end"
7+
motion:constraintSetStart="@id/start" >
8+
<KeyFrameSet>
9+
<KeyAttribute
10+
android:rotation="45"
11+
motion:framePosition="20"
12+
motion:motionTarget="@+id/button" />
13+
<KeyAttribute
14+
android:rotation="45"
15+
motion:framePosition="80"
16+
motion:motionTarget="@+id/button" />
17+
18+
</KeyFrameSet>
19+
</Transition>
20+
21+
<ConstraintSet android:id="@+id/start">
22+
23+
<ConstraintOverride android:id="@+id/backdrop">
24+
<CustomAttribute
25+
motion:attributeName="backgroundColor"
26+
motion:customColorValue="#727572" />
27+
</ConstraintOverride>
28+
</ConstraintSet>
29+
30+
<ConstraintSet android:id="@+id/end">
31+
32+
<Constraint
33+
android:id="@+id/backdrop"
34+
android:layout_width="match_parent"
35+
android:layout_height="match_parent">
36+
<CustomAttribute
37+
motion:attributeName="backgroundColor"
38+
motion:customColorValue="#040404" />
39+
</Constraint>
40+
41+
<Constraint
42+
android:id="@+id/button"
43+
android:layout_width="50dp"
44+
android:layout_height="50dp"
45+
motion:layout_constraintBottom_toBottomOf="parent"
46+
motion:layout_constraintEnd_toEndOf="parent"
47+
motion:layout_constraintHorizontal_bias="0.5"
48+
motion:layout_constraintStart_toStartOf="parent"
49+
motion:layout_constraintTop_toTopOf="parent" />
50+
51+
<Constraint
52+
android:id="@+id/image"
53+
android:layout_width="0dp"
54+
android:layout_height="0dp"
55+
motion:layout_constraintDimensionRatio="1:1"
56+
motion:layout_constraintEnd_toEndOf="parent"
57+
motion:layout_constraintStart_toStartOf="parent"
58+
motion:layout_constraintTop_toTopOf="parent" />
59+
</ConstraintSet>
60+
</MotionScene>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<MotionScene
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:motion="http://schemas.android.com/apk/res-auto">
5+
6+
<Transition
7+
motion:constraintSetEnd="@+id/end"
8+
motion:constraintSetStart="@id/start"
9+
motion:duration="2000">
10+
<KeyFrameSet>
11+
<KeyAttribute motion:motionTarget="@+id/detail" android:alpha="1" motion:framePosition="1"/>
12+
<KeyAttribute motion:motionTarget="@+id/detail" motion:motionProgress="0" motion:framePosition="30"/>
13+
</KeyFrameSet>
14+
</Transition>
15+
16+
<ConstraintSet android:id="@+id/start">
17+
<Constraint
18+
android:id="@+id/recyclerView"
19+
motion:layout_constraintEnd_toEndOf="parent"
20+
android:layout_width="match_parent"
21+
android:layout_height="match_parent"
22+
motion:layout_constraintBottom_toBottomOf="parent"
23+
motion:layout_constraintTop_toTopOf="parent"
24+
motion:layout_constraintStart_toStartOf="parent" />
25+
<Constraint
26+
android:id="@+id/detail"
27+
android:layout_width="wrap_content"
28+
android:layout_height="100dp"
29+
android:visibility="invisible"
30+
motion:motionProgress="0"
31+
motion:layout_constraintTop_toTopOf="parent"
32+
motion:layout_constraintStart_toStartOf="parent" />
33+
</ConstraintSet>
34+
35+
<ConstraintSet android:id="@+id/end">
36+
<Constraint
37+
android:id="@+id/recyclerView"
38+
motion:layout_constraintEnd_toEndOf="parent"
39+
android:layout_width="match_parent"
40+
android:layout_height="match_parent"
41+
motion:layout_constraintBottom_toBottomOf="parent"
42+
motion:layout_constraintTop_toTopOf="parent"
43+
motion:layout_constraintStart_toStartOf="parent"
44+
/>
45+
<Constraint
46+
android:id="@+id/detail"
47+
android:layout_width="match_parent"
48+
android:layout_height="match_parent"
49+
motion:motionProgress="1"
50+
motion:layout_constraintTop_toTopOf="parent"
51+
motion:layout_constraintStart_toStartOf="parent" />
52+
</ConstraintSet>
53+
</MotionScene>

0 commit comments

Comments
 (0)