Skip to content

Commit ae5f161

Browse files
Refactor: Modernize shortcuts screen with RecyclerView
This commit refactors the shortcuts screens to use a `RecyclerView` instead of a static `TableLayout`, improving performance and maintainability. Key changes include: - Replacing the `TableLayout` in all shortcut category layouts with a `RecyclerView`. - Introducing `ShortcutsRepository` to centralize and provide shortcut data for different categories. - Adding `ShortcutsAdapter` and `ShortcutItem` to populate the `RecyclerView` efficiently. - Updating `ShortcutsCategoryActivity` to set up the `RecyclerView` and load data from the repository. - Adding a new `item_shortcut.xml` layout for individual shortcut rows. - Adjusting `fitsSystemWindows` attributes in `activity_main.xml` layouts for consistent UI behavior.
1 parent 8fa611f commit ae5f161

File tree

12 files changed

+454
-1263
lines changed

12 files changed

+454
-1263
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts;
2+
3+
import androidx.annotation.StringRes;
4+
5+
/**
6+
* Represents a keyboard shortcut entry with its key combination and description.
7+
*/
8+
public record ShortcutItem(String key, @StringRes int descriptionResId) { }
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts;
2+
3+
import android.view.LayoutInflater;
4+
import android.view.View;
5+
import android.view.ViewGroup;
6+
import android.widget.TextView;
7+
8+
import androidx.annotation.NonNull;
9+
import androidx.recyclerview.widget.DiffUtil;
10+
import androidx.recyclerview.widget.ListAdapter;
11+
import androidx.recyclerview.widget.RecyclerView;
12+
13+
import com.d4rk.androidtutorials.java.R;
14+
15+
public final class ShortcutsAdapter
16+
extends ListAdapter<ShortcutItem, ShortcutsAdapter.ShortcutViewHolder> {
17+
18+
// DiffUtil: only update what actually changed.
19+
private static final DiffUtil.ItemCallback<ShortcutItem> DIFF = new DiffUtil.ItemCallback<>() {
20+
@Override
21+
public boolean areItemsTheSame(@NonNull ShortcutItem oldItem, @NonNull ShortcutItem newItem) {
22+
// Keys are unique in your dataset (e.g., "Ctrl + S")
23+
return oldItem.key().equals(newItem.key());
24+
}
25+
26+
@Override
27+
public boolean areContentsTheSame(@NonNull ShortcutItem oldItem, @NonNull ShortcutItem newItem) {
28+
// If description resource id is the same, content is the same
29+
return oldItem.descriptionResId() == newItem.descriptionResId();
30+
}
31+
};
32+
33+
public ShortcutsAdapter() {
34+
super(DIFF);
35+
setHasStableIds(false);
36+
}
37+
38+
@NonNull
39+
@Override
40+
public ShortcutViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
41+
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
42+
View view = inflater.inflate(R.layout.item_shortcut, parent, false);
43+
return new ShortcutViewHolder(view);
44+
}
45+
46+
@Override
47+
public void onBindViewHolder(@NonNull ShortcutViewHolder holder, int position) {
48+
holder.bind(getItem(position));
49+
}
50+
51+
// You can keep calling adapter.submitList(list) from outside; ListAdapter handles diff+dispatch.
52+
53+
// Make visibility at least as wide as the adapter’s public API.
54+
public static final class ShortcutViewHolder extends RecyclerView.ViewHolder {
55+
56+
private final TextView keyTextView;
57+
private final TextView descriptionTextView;
58+
59+
public ShortcutViewHolder(@NonNull View itemView) {
60+
super(itemView);
61+
keyTextView = itemView.findViewById(R.id.shortcut_key);
62+
descriptionTextView = itemView.findViewById(R.id.shortcut_description);
63+
}
64+
65+
void bind(ShortcutItem item) {
66+
keyTextView.setText(item.key());
67+
descriptionTextView.setText(item.descriptionResId());
68+
}
69+
}
70+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts;
2+
3+
import android.util.SparseArray;
4+
5+
import androidx.annotation.LayoutRes;
6+
7+
import com.d4rk.androidtutorials.java.R;
8+
9+
import java.util.Collections;
10+
import java.util.List;
11+
12+
public final class ShortcutsRepository {
13+
14+
private ShortcutsRepository() { /* Utility class. */ }
15+
16+
// --- Public API ----------------------------------------------------------
17+
public static List<ShortcutItem> getShortcuts(@LayoutRes int layoutResId) {
18+
List<ShortcutItem> list = SHORTCUTS_BY_LAYOUT.get(layoutResId);
19+
return list != null ? list : Collections.emptyList();
20+
}
21+
22+
// --- Implementation details ---------------------------------------------
23+
private static final SparseArray<List<ShortcutItem>> SHORTCUTS_BY_LAYOUT = new SparseArray<>();
24+
25+
private static final List<ShortcutItem> GENERAL_SHORTCUTS = List.of(
26+
new ShortcutItem("Ctrl + S", R.string.save_all),
27+
new ShortcutItem("Ctrl + ALT + Y", R.string.synchronize),
28+
new ShortcutItem("Ctrl + Shift + F12", R.string.maximize),
29+
new ShortcutItem("Ctrl + Shift + F", R.string.add_to_favorites),
30+
new ShortcutItem("Ctrl + Shift + I", R.string.file_inspect),
31+
new ShortcutItem("Ctrl + `", R.string.quick_switch_theme),
32+
new ShortcutItem("Ctrl + Alt + S", R.string.open_settings),
33+
new ShortcutItem("Ctrl + Alt + Shift + S", R.string.open_project),
34+
new ShortcutItem("Ctrl + Tab", R.string.switch_tabs)
35+
);
36+
37+
private static final List<ShortcutItem> BUILD_SHORTCUTS = List.of(
38+
new ShortcutItem("Ctrl + F9", R.string.build),
39+
new ShortcutItem("Shift + F10", R.string.build_and_run),
40+
new ShortcutItem("Ctrl + F10", R.string.apply_changes)
41+
);
42+
43+
private static final List<ShortcutItem> DEBUGGING_SHORTCUTS = List.of(
44+
new ShortcutItem("Shift + F9", R.string.debug),
45+
new ShortcutItem("F8", R.string.step_over),
46+
new ShortcutItem("F7", R.string.step_into),
47+
new ShortcutItem("Shift + F7", R.string.smart_step_into),
48+
new ShortcutItem("Shift + F8", R.string.step_out),
49+
new ShortcutItem("Alt + F9", R.string.run_to_cursor),
50+
new ShortcutItem("Alt + F8", R.string.evaluate_expression),
51+
new ShortcutItem("F9", R.string.resume_program),
52+
new ShortcutItem("Ctrl + F8", R.string.toggle_breakpoint),
53+
new ShortcutItem("Ctrl + Shift + F8", R.string.view_breakpoints)
54+
);
55+
56+
private static final List<ShortcutItem> CODE_SHORTCUTS = List.of(
57+
new ShortcutItem("Alt + Insert", R.string.generate_code),
58+
new ShortcutItem("Ctrl + O", R.string.override_methods),
59+
new ShortcutItem("Ctrl + I", R.string.implement_methods),
60+
new ShortcutItem("Ctrl + Alt + T", R.string.surround),
61+
new ShortcutItem("Ctrl + Y", R.string.delete_line),
62+
new ShortcutItem("Ctrl +", R.string.collapse_and_expand_code_block),
63+
new ShortcutItem("-/+", R.string.collapse_and_expand_all_code_block),
64+
new ShortcutItem("Ctrl + Shift +", R.string.duplicate_current_line),
65+
new ShortcutItem("-/+", R.string.basic_code_completion),
66+
new ShortcutItem("Ctrl + D", R.string.smart_code_completion)
67+
);
68+
69+
private static final List<ShortcutItem> REFACTORING_SHORTCUTS = List.of(
70+
new ShortcutItem("F5", android.R.string.copy),
71+
new ShortcutItem("F6", R.string.move),
72+
new ShortcutItem("Alt + Delete", R.string.safe_delete),
73+
new ShortcutItem("Shift + F6", R.string.rename),
74+
new ShortcutItem("Ctrl + F6", R.string.change_signature),
75+
new ShortcutItem("Ctrl + Alt + N", R.string.inline),
76+
new ShortcutItem("Ctrl + Alt + M", R.string.extract_method),
77+
new ShortcutItem("Ctrl + Alt + V", R.string.extract_variable),
78+
new ShortcutItem("Ctrl + Alt + F", R.string.extract_field),
79+
new ShortcutItem("Ctrl + Alt + C", R.string.extract_constant)
80+
);
81+
82+
private static final List<ShortcutItem> VERSION_CONTROL_SHORTCUTS = List.of(
83+
new ShortcutItem("Ctrl + K", R.string.commit_project),
84+
new ShortcutItem("Ctrl + T", R.string.update_project),
85+
new ShortcutItem("Alt + Shift + C", R.string.view_recent_changes),
86+
new ShortcutItem("Alt + `", R.string.open_vcs_popup)
87+
);
88+
89+
private static final List<ShortcutItem> NAVIGATION_SHORTCUTS = List.of(
90+
new ShortcutItem("Press Shift twice", R.string.search_everything),
91+
new ShortcutItem("Ctrl + F", R.string.find),
92+
new ShortcutItem("F3", R.string.find_next),
93+
new ShortcutItem("Shift + F3", R.string.find_previous),
94+
new ShortcutItem("Ctrl + R", R.string.replace),
95+
new ShortcutItem("Ctrl + Shift + A", R.string.find_action),
96+
new ShortcutItem("Ctrl + Alt + Shift + N", R.string.search_by_symbol_name),
97+
new ShortcutItem("Ctrl + N", R.string.find_class),
98+
new ShortcutItem("Ctrl + Shift + N", R.string.find_file),
99+
new ShortcutItem("Ctrl + Shift + Alt + N", R.string.find_path),
100+
new ShortcutItem("Ctrl + F12", R.string.open_file_structure),
101+
new ShortcutItem("Ctrl + Tab", R.string.navigate_between_open_tabs),
102+
new ShortcutItem("F4", R.string.jump_to_source),
103+
new ShortcutItem("Shift + F4", R.string.open_current_editor_tab_in_new_window),
104+
new ShortcutItem("Ctrl + E", R.string.recently_opened_files),
105+
new ShortcutItem("Ctrl + Shift + E", R.string.recently_edited_files),
106+
new ShortcutItem("Ctrl + Shift + Backspace", R.string.go_to_last_edit_location),
107+
new ShortcutItem("Ctrl + F4", R.string.close_active_editor_tabs),
108+
new ShortcutItem("Esc", R.string.return_to_editor_window),
109+
new ShortcutItem("Shift + Esc", R.string.hide_active_window),
110+
new ShortcutItem("Ctrl + G", R.string.go_to_line),
111+
new ShortcutItem("Ctrl + H", R.string.open_type_hierarchy),
112+
new ShortcutItem("Ctrl + Shift + H", R.string.open_v_hierarchy),
113+
new ShortcutItem("Ctrl + Alt + H", R.string.open_call_hierarchy)
114+
);
115+
116+
static {
117+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_general, GENERAL_SHORTCUTS);
118+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_build, BUILD_SHORTCUTS);
119+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_debugging, DEBUGGING_SHORTCUTS);
120+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_code, CODE_SHORTCUTS);
121+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_refractoring, REFACTORING_SHORTCUTS);
122+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_version_control, VERSION_CONTROL_SHORTCUTS);
123+
SHORTCUTS_BY_LAYOUT.put(R.layout.activity_shortcuts_navigation_and_searching, NAVIGATION_SHORTCUTS);
124+
}
125+
}

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/ShortcutsCategoryActivity.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@
77

88
import com.d4rk.androidtutorials.java.R;
99
import com.d4rk.androidtutorials.java.ads.AdUtils;
10+
import com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts.ShortcutItem;
11+
import com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts.ShortcutsAdapter;
12+
import com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts.ShortcutsRepository;
1013
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
1114
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeHelper;
1215
import com.google.android.gms.ads.AdView;
1316

17+
import java.util.List;
18+
19+
import androidx.recyclerview.widget.LinearLayoutManager;
20+
import androidx.recyclerview.widget.RecyclerView;
21+
1422
import me.zhanghai.android.fastscroll.FastScrollScrollView;
1523
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
1624

@@ -37,6 +45,8 @@ protected void onCreate(Bundle savedInstanceState) {
3745
setContentView(layoutResId);
3846
setTitle(titleResId);
3947

48+
RecyclerView recyclerView = setupShortcuts(layoutResId);
49+
4050
View contentView = findViewById(android.R.id.content);
4151
if (contentView instanceof ViewGroup) {
4252
View root = ((ViewGroup) contentView).getChildAt(0);
@@ -50,9 +60,28 @@ protected void onCreate(Bundle savedInstanceState) {
5060
AdUtils.loadBanner(adView);
5161
}
5262

53-
FastScrollScrollView scrollView = findViewById(R.id.scroll_view);
54-
if (scrollView != null) {
55-
new FastScrollerBuilder(scrollView).useMd2Style().build();
63+
if (recyclerView != null) {
64+
new FastScrollerBuilder(recyclerView).useMd2Style().build();
65+
} else {
66+
FastScrollScrollView scrollView = findViewById(R.id.scroll_view);
67+
if (scrollView != null) {
68+
new FastScrollerBuilder(scrollView).useMd2Style().build();
69+
}
5670
}
5771
}
72+
73+
private RecyclerView setupShortcuts(int layoutResId) {
74+
RecyclerView recyclerView = findViewById(R.id.shortcut_list);
75+
if (recyclerView == null) {
76+
return null;
77+
}
78+
79+
recyclerView.setLayoutManager(new LinearLayoutManager(this));
80+
ShortcutsAdapter adapter = new ShortcutsAdapter();
81+
recyclerView.setAdapter(adapter);
82+
83+
List<ShortcutItem> shortcuts = ShortcutsRepository.getShortcuts(layoutResId);
84+
adapter.submitList(shortcuts);
85+
return recyclerView;
86+
}
5887
}

app/src/main/res/layout-sw600dp/activity_main.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
android:id="@+id/app_bar_layout"
1010
android:layout_width="match_parent"
1111
android:layout_height="wrap_content"
12+
android:fitsSystemWindows="true"
1213
app:layout_constraintTop_toTopOf="parent">
1314

1415
<com.google.android.material.appbar.MaterialToolbar

app/src/main/res/layout/activity_main.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
xmlns:app="http://schemas.android.com/apk/res-auto"
33
android:id="@+id/container"
44
android:layout_width="match_parent"
5-
android:layout_height="match_parent"
6-
android:fitsSystemWindows="true">
5+
android:layout_height="match_parent">
76

87
<com.google.android.material.appbar.AppBarLayout
98
android:id="@+id/app_bar_layout"
109
android:layout_width="match_parent"
1110
android:layout_height="wrap_content"
11+
android:fitsSystemWindows="true"
1212
app:layout_constraintTop_toTopOf="parent">
1313

1414
<com.google.android.material.appbar.MaterialToolbar

0 commit comments

Comments
 (0)