Skip to content

Commit f54b463

Browse files
Restore lesson categories in RecyclerView
1 parent 7b90c90 commit f54b463

File tree

2 files changed

+144
-38
lines changed

2 files changed

+144
-38
lines changed

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java

Lines changed: 110 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@
3333
import java.io.IOException;
3434
import java.util.ArrayList;
3535
import java.util.Collections;
36+
import java.util.HashSet;
3637
import java.util.List;
3738
import java.util.Random;
39+
import java.util.Set;
3840

3941
public class AndroidStudioFragment extends Fragment {
4042

4143
private static boolean mobileAdsInitialized = false;
42-
private final List<Lesson> allLessons = new ArrayList<>();
44+
private final List<Object> allItems = new ArrayList<>();
4345
private LessonsAdapter adapter;
4446

4547
@Nullable
@@ -58,9 +60,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
5860
list.setLayoutManager(new LinearLayoutManager(requireContext()));
5961
adapter = new LessonsAdapter();
6062
list.setAdapter(adapter);
61-
allLessons.clear();
62-
allLessons.addAll(loadLessons());
63-
populateAdapter(allLessons);
63+
allItems.clear();
64+
allItems.addAll(loadItems());
65+
populateAdapter(allItems);
6466
}
6567

6668
private void ensureMobileAdsInitialized() {
@@ -70,23 +72,29 @@ private void ensureMobileAdsInitialized() {
7072
}
7173
}
7274

73-
private List<Lesson> loadLessons() {
74-
List<Lesson> lessons = new ArrayList<>();
75+
private List<Object> loadItems() {
76+
List<Object> items = new ArrayList<>();
7577
XmlResourceParser parser = getResources().getXml(R.xml.preferences_android_studio);
7678
try {
7779
int event = parser.getEventType();
78-
Lesson current = null;
80+
Lesson currentLesson = null;
7981
while (event != XmlPullParser.END_DOCUMENT) {
8082
if (event == XmlPullParser.START_TAG) {
8183
String name = parser.getName();
82-
if (name.endsWith("Preference") && !name.endsWith("PreferenceCategory")) {
83-
current = new Lesson();
84+
if (name.endsWith("PreferenceCategory")) {
85+
Category category = new Category();
86+
int titleRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "title", 0);
87+
if (titleRes != 0) category.title = getString(titleRes);
88+
category.iconRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "icon", 0);
89+
items.add(category);
90+
} else if (name.endsWith("Preference") && !name.endsWith("PreferenceCategory")) {
91+
currentLesson = new Lesson();
8492
int titleRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "title", 0);
85-
if (titleRes != 0) current.title = getString(titleRes);
93+
if (titleRes != 0) currentLesson.title = getString(titleRes);
8694
int summaryRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "summary", 0);
87-
if (summaryRes != 0) current.summary = getString(summaryRes);
88-
current.iconRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "icon", 0);
89-
} else if ("intent".equals(name) && current != null) {
95+
if (summaryRes != 0) currentLesson.summary = getString(summaryRes);
96+
currentLesson.iconRes = parser.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "icon", 0);
97+
} else if ("intent".equals(name) && currentLesson != null) {
9098
Intent intent = new Intent();
9199
String action = parser.getAttributeValue("http://schemas.android.com/apk/res/android", "action");
92100
if (action != null) intent.setAction(action);
@@ -97,13 +105,13 @@ private List<Lesson> loadLessons() {
97105
}
98106
String data = parser.getAttributeValue("http://schemas.android.com/apk/res/android", "data");
99107
if (data != null) intent.setData(Uri.parse(data));
100-
current.intent = intent;
108+
currentLesson.intent = intent;
101109
}
102110
} else if (event == XmlPullParser.END_TAG) {
103111
String name = parser.getName();
104-
if (current != null && name.endsWith("Preference") && !name.endsWith("PreferenceCategory")) {
105-
lessons.add(current);
106-
current = null;
112+
if (currentLesson != null && name.endsWith("Preference") && !name.endsWith("PreferenceCategory")) {
113+
items.add(currentLesson);
114+
currentLesson = null;
107115
}
108116
}
109117
event = parser.next();
@@ -112,26 +120,37 @@ private List<Lesson> loadLessons() {
112120
} finally {
113121
parser.close();
114122
}
115-
return lessons;
123+
return items;
116124
}
117125

118-
private void populateAdapter(List<Lesson> lessons) {
126+
private void populateAdapter(List<Object> source) {
119127
List<Object> items = new ArrayList<>();
120-
int adCount = lessons.size() / 3;
121-
List<Integer> positions = new ArrayList<>();
122-
for (int i = 0; i < lessons.size(); i++) {
123-
positions.add(i);
128+
int lessonCount = 0;
129+
for (Object item : source) {
130+
if (item instanceof Lesson) {
131+
lessonCount++;
132+
}
133+
}
134+
int adCount = lessonCount / 3;
135+
List<Integer> indices = new ArrayList<>();
136+
for (int i = 0; i < lessonCount; i++) {
137+
indices.add(i);
124138
}
125-
Collections.shuffle(positions, new Random());
126-
positions = positions.subList(0, adCount);
127-
Collections.sort(positions);
128-
int adIndex = 0;
129-
for (int i = 0; i < lessons.size(); i++) {
130-
if (adIndex < positions.size() && i == positions.get(adIndex)) {
131-
items.add(new AdItem());
132-
adIndex++;
139+
Collections.shuffle(indices, new Random());
140+
indices = indices.subList(0, adCount);
141+
Collections.sort(indices);
142+
Set<Integer> adPositions = new HashSet<>(indices);
143+
int lessonIndex = 0;
144+
for (Object item : source) {
145+
if (item instanceof Lesson) {
146+
if (adPositions.contains(lessonIndex)) {
147+
items.add(new AdItem());
148+
}
149+
items.add(item);
150+
lessonIndex++;
151+
} else {
152+
items.add(item);
133153
}
134-
items.add(lessons.get(i));
135154
}
136155
adapter.setItems(items);
137156
}
@@ -172,10 +191,26 @@ public boolean onMenuItemActionCollapse(MenuItem item) {
172191

173192
private void filterLessons(String query) {
174193
String lower = query == null ? "" : query.toLowerCase();
175-
List<Lesson> filtered = new ArrayList<>();
176-
for (Lesson l : allLessons) {
177-
if (l.title != null && l.title.toLowerCase().contains(lower)) {
178-
filtered.add(l);
194+
if (lower.isEmpty()) {
195+
populateAdapter(allItems);
196+
return;
197+
}
198+
List<Object> filtered = new ArrayList<>();
199+
Category lastCategory = null;
200+
boolean categoryAdded = false;
201+
for (Object item : allItems) {
202+
if (item instanceof Category) {
203+
lastCategory = (Category) item;
204+
categoryAdded = false;
205+
} else if (item instanceof Lesson) {
206+
Lesson l = (Lesson) item;
207+
if (l.title != null && l.title.toLowerCase().contains(lower)) {
208+
if (lastCategory != null && !categoryAdded) {
209+
filtered.add(lastCategory);
210+
categoryAdded = true;
211+
}
212+
filtered.add(l);
213+
}
179214
}
180215
}
181216
populateAdapter(filtered);
@@ -190,9 +225,15 @@ private static class Lesson {
190225
Intent intent;
191226
}
192227

228+
private static class Category {
229+
String title;
230+
int iconRes;
231+
}
232+
193233
private static class LessonsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
194234
private static final int TYPE_LESSON = 0;
195235
private static final int TYPE_AD = 1;
236+
private static final int TYPE_CATEGORY = 2;
196237
private final List<Object> items = new ArrayList<>();
197238

198239
void setItems(List<Object> newItems) {
@@ -204,7 +245,9 @@ void setItems(List<Object> newItems) {
204245
@Override
205246
public int getItemViewType(int position) {
206247
Object item = items.get(position);
207-
return item instanceof Lesson ? TYPE_LESSON : TYPE_AD;
248+
if (item instanceof Lesson) return TYPE_LESSON;
249+
if (item instanceof Category) return TYPE_CATEGORY;
250+
return TYPE_AD;
208251
}
209252

210253
@NonNull
@@ -217,6 +260,10 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
217260
ViewGroup.LayoutParams.WRAP_CONTENT));
218261
adView.setNativeAdLayout(R.layout.android_studio_list_native_ad);
219262
return new AdHolder(adView);
263+
} else if (viewType == TYPE_CATEGORY) {
264+
View view = LayoutInflater.from(parent.getContext())
265+
.inflate(R.layout.android_studio_category_item, parent, false);
266+
return new CategoryHolder(view);
220267
} else {
221268
View view = LayoutInflater.from(parent.getContext())
222269
.inflate(R.layout.android_studio_lesson_item, parent, false);
@@ -226,14 +273,18 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
226273

227274
@Override
228275
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
229-
if (getItemViewType(position) == TYPE_AD) {
276+
int type = getItemViewType(position);
277+
if (type == TYPE_AD) {
230278
AdHolder adHolder = (AdHolder) holder;
231279
adHolder.adView.loadAd(new AdRequest.Builder().build(), new AdListener() {
232280
@Override
233281
public void onAdFailedToLoad(@NonNull LoadAdError error) {
234282
adHolder.adView.setVisibility(View.GONE);
235283
}
236284
});
285+
} else if (type == TYPE_CATEGORY) {
286+
Category category = (Category) items.get(position);
287+
((CategoryHolder) holder).bind(category);
237288
} else {
238289
Lesson lesson = (Lesson) items.get(position);
239290
((LessonHolder) holder).bind(lesson);
@@ -286,5 +337,26 @@ void bind(Lesson lesson) {
286337
});
287338
}
288339
}
340+
341+
static class CategoryHolder extends RecyclerView.ViewHolder {
342+
final ImageView icon;
343+
final TextView title;
344+
345+
CategoryHolder(@NonNull View itemView) {
346+
super(itemView);
347+
icon = itemView.findViewById(R.id.category_icon);
348+
title = itemView.findViewById(R.id.category_title);
349+
}
350+
351+
void bind(Category category) {
352+
if (category.iconRes != 0) {
353+
icon.setImageResource(category.iconRes);
354+
icon.setVisibility(View.VISIBLE);
355+
} else {
356+
icon.setVisibility(View.GONE);
357+
}
358+
title.setText(category.title);
359+
}
360+
}
289361
}
290362
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:id="@+id/category_card"
5+
style="@style/Widget.Material3.CardView.Outlined"
6+
android:layout_width="match_parent"
7+
android:layout_height="wrap_content"
8+
android:layout_marginHorizontal="16dp"
9+
android:layout_marginVertical="4dp"
10+
android:clickable="false"
11+
android:focusable="false">
12+
13+
<LinearLayout
14+
android:layout_width="match_parent"
15+
android:layout_height="wrap_content"
16+
android:orientation="horizontal"
17+
android:gravity="center_vertical"
18+
android:padding="16dp">
19+
20+
<ImageView
21+
android:id="@+id/category_icon"
22+
android:layout_width="40dp"
23+
android:layout_height="40dp"
24+
android:layout_marginEnd="16dp"
25+
android:contentDescription="@null" />
26+
27+
<TextView
28+
android:id="@+id/category_title"
29+
android:layout_width="wrap_content"
30+
android:layout_height="wrap_content"
31+
android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />
32+
</LinearLayout>
33+
34+
</com.google.android.material.card.MaterialCardView>

0 commit comments

Comments
 (0)