Skip to content

Commit 7bf9adc

Browse files
Replace findViewById usages with view binding
1 parent 8933a43 commit 7bf9adc

File tree

24 files changed

+307
-105
lines changed

24 files changed

+307
-105
lines changed

app/src/main/java/com/d4rk/androidtutorials/java/ads/managers/NativeAdLoader.java

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.d4rk.androidtutorials.java.ads.managers;
22

33
import android.content.Context;
4+
import android.content.res.Resources;
45
import android.text.TextUtils;
56
import android.util.Log;
67
import android.view.LayoutInflater;
@@ -12,6 +13,7 @@
1213

1314
import androidx.annotation.LayoutRes;
1415
import androidx.annotation.NonNull;
16+
import androidx.viewbinding.ViewBinding;
1517

1618
import com.d4rk.androidtutorials.java.R;
1719
import com.google.android.gms.ads.AdListener;
@@ -23,6 +25,10 @@
2325
import com.google.android.gms.ads.nativead.NativeAd;
2426
import com.google.android.gms.ads.nativead.NativeAdView;
2527

28+
import java.lang.reflect.Field;
29+
import java.lang.reflect.InvocationTargetException;
30+
import java.lang.reflect.Method;
31+
2632
/**
2733
* Helper class to load AdMob native ads into a container.
2834
*/
@@ -73,7 +79,7 @@ public static void load(@NonNull Context context,
7379
adView.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
7480
container.getPaddingRight(), container.getPaddingBottom());
7581
container.setPadding(0, 0, 0, 0);
76-
populateNativeAdView(nativeAd, adView);
82+
populateNativeAdView(nativeAd, adView, layoutRes);
7783
container.removeAllViews();
7884
container.addView(adView);
7985
container.requestLayout();
@@ -92,14 +98,22 @@ public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
9298
adLoader.loadAd(adRequest);
9399
}
94100

95-
private static void populateNativeAdView(@NonNull NativeAd nativeAd, @NonNull NativeAdView adView) {
96-
MediaView mediaView = adView.findViewById(R.id.ad_media);
97-
TextView headlineView = adView.findViewById(R.id.ad_headline);
98-
TextView bodyView = adView.findViewById(R.id.ad_body);
99-
Button callToActionView = adView.findViewById(R.id.ad_call_to_action);
100-
ImageView iconView = adView.findViewById(R.id.ad_app_icon);
101-
TextView attributionView = adView.findViewById(R.id.ad_attribution);
102-
AdChoicesView adChoicesView = adView.findViewById(R.id.ad_choices);
101+
private static void populateNativeAdView(@NonNull NativeAd nativeAd,
102+
@NonNull NativeAdView adView,
103+
@LayoutRes int layoutRes) {
104+
ViewBinding binding = tryBind(adView, layoutRes);
105+
if (binding == null) {
106+
Log.w(TAG, "Could not bind native ad view for layout " + layoutRes);
107+
return;
108+
}
109+
110+
MediaView mediaView = getBindingField(binding, "adMedia", MediaView.class);
111+
TextView headlineView = getBindingField(binding, "adHeadline", TextView.class);
112+
TextView bodyView = getBindingField(binding, "adBody", TextView.class);
113+
Button callToActionView = getBindingField(binding, "adCallToAction", Button.class);
114+
ImageView iconView = getBindingField(binding, "adAppIcon", ImageView.class);
115+
TextView attributionView = getBindingField(binding, "adAttribution", TextView.class);
116+
AdChoicesView adChoicesView = getBindingField(binding, "adChoices", AdChoicesView.class);
103117

104118
if (mediaView != null) {
105119
adView.setMediaView(mediaView);
@@ -166,4 +180,53 @@ private static void populateNativeAdView(@NonNull NativeAd nativeAd, @NonNull Na
166180

167181
adView.setNativeAd(nativeAd);
168182
}
183+
184+
@androidx.annotation.Nullable
185+
private static ViewBinding tryBind(@NonNull NativeAdView adView, @LayoutRes int layoutRes) {
186+
try {
187+
String resourceName = adView.getResources().getResourceEntryName(layoutRes);
188+
String bindingName = toBindingClassName(resourceName);
189+
String fullClassName = adView.getContext().getPackageName() + ".databinding." + bindingName;
190+
Class<?> bindingClass = Class.forName(fullClassName);
191+
Method bindMethod = bindingClass.getMethod("bind", View.class);
192+
return (ViewBinding) bindMethod.invoke(null, adView);
193+
} catch (Resources.NotFoundException | ClassNotFoundException | NoSuchMethodException |
194+
IllegalAccessException | InvocationTargetException e) {
195+
Log.w(TAG, "Failed to create view binding for native ad layout", e);
196+
return null;
197+
}
198+
}
199+
200+
@androidx.annotation.Nullable
201+
private static <T> T getBindingField(@NonNull ViewBinding binding,
202+
@NonNull String fieldName,
203+
@NonNull Class<T> type) {
204+
try {
205+
Field field = binding.getClass().getField(fieldName);
206+
Object value = field.get(binding);
207+
if (type.isInstance(value)) {
208+
return type.cast(value);
209+
}
210+
} catch (NoSuchFieldException | IllegalAccessException e) {
211+
Log.w(TAG, "Unable to access binding field " + fieldName, e);
212+
}
213+
return null;
214+
}
215+
216+
@NonNull
217+
private static String toBindingClassName(@NonNull String resourceName) {
218+
StringBuilder builder = new StringBuilder(resourceName.length());
219+
boolean capitalize = true;
220+
for (int i = 0; i < resourceName.length(); i++) {
221+
char c = resourceName.charAt(i);
222+
if (c == '_') {
223+
capitalize = true;
224+
} else {
225+
builder.append(capitalize ? Character.toUpperCase(c) : c);
226+
capitalize = false;
227+
}
228+
}
229+
builder.append("Binding");
230+
return builder.toString();
231+
}
169232
}

app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ protected void onBindingCreated(@NonNull T binding, @Nullable Bundle savedInstan
4747
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
4848
@Nullable Bundle savedInstanceState) {
4949
binding = inflateBinding(inflater, container);
50-
View adView = binding.getRoot().findViewById(R.id.ad_view);
51-
AdUtils.loadBanner(adView);
50+
AdUtils.loadBanner(getAdView(binding));
5251
onBindingCreated(binding, savedInstanceState);
5352
return binding.getRoot();
5453
}
5554

55+
@NonNull
56+
protected abstract View getAdView(@NonNull T binding);
57+
5658
@Override
5759
public void onDestroyView() {
5860
super.onDestroyView();

app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/BaseActivity.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,17 @@
33
import android.annotation.SuppressLint;
44
import android.os.Bundle;
55
import android.view.Menu;
6-
import android.view.View;
76

87
import androidx.annotation.Nullable;
98
import androidx.appcompat.app.ActionBar;
109
import androidx.appcompat.app.AppCompatActivity;
1110
import androidx.appcompat.view.menu.MenuBuilder;
1211

13-
import com.d4rk.androidtutorials.java.R;
14-
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
15-
1612
public abstract class BaseActivity extends AppCompatActivity {
1713

1814
@Override
1915
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
2016
super.onPostCreate(savedInstanceState);
21-
View container = findViewById(R.id.container);
22-
if (container != null) {
23-
EdgeToEdgeDelegate.apply(this, container);
24-
}
2517
ActionBar actionBar = getSupportActionBar();
2618
if (actionBar != null) {
2719
actionBar.setDisplayHomeAsUpEnabled(true);

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

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@
3030
import com.d4rk.androidtutorials.java.ads.AdUtils;
3131
import com.d4rk.androidtutorials.java.ads.views.NativeAdBannerView;
3232
import com.d4rk.androidtutorials.java.databinding.FragmentAndroidStudioBinding;
33+
import com.d4rk.androidtutorials.java.databinding.ItemPreferenceBinding;
34+
import com.d4rk.androidtutorials.java.databinding.ItemPreferenceCategoryBinding;
35+
import com.d4rk.androidtutorials.java.databinding.ItemPreferenceWidgetOpenInNewBinding;
3336
import com.google.android.gms.ads.AdListener;
3437
import com.google.android.gms.ads.LoadAdError;
3538
import com.google.android.material.button.MaterialButton;
36-
import com.google.android.material.card.MaterialCardView;
37-
import com.google.android.material.imageview.ShapeableImageView;
3839
import com.google.android.material.shape.CornerFamily;
3940
import com.google.android.material.shape.ShapeAppearanceModel;
40-
import com.google.android.material.textview.MaterialTextView;
4141

4242
import org.xmlpull.v1.XmlPullParser;
4343
import org.xmlpull.v1.XmlPullParserException;
@@ -375,13 +375,19 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
375375
adView.setNativeAdUnitId(R.string.native_ad_lessons_list_unit_id);
376376
return new AdHolder(adView);
377377
} else if (viewType == TYPE_CATEGORY) {
378-
View view = LayoutInflater.from(parent.getContext())
379-
.inflate(R.layout.item_preference_category, parent, false);
380-
return new CategoryHolder(view);
378+
ItemPreferenceCategoryBinding binding = ItemPreferenceCategoryBinding.inflate(
379+
LayoutInflater.from(parent.getContext()),
380+
parent,
381+
false
382+
);
383+
return new CategoryHolder(binding);
381384
} else {
382-
View view = LayoutInflater.from(parent.getContext())
383-
.inflate(R.layout.item_preference, parent, false);
384-
return new LessonHolder(view);
385+
ItemPreferenceBinding binding = ItemPreferenceBinding.inflate(
386+
LayoutInflater.from(parent.getContext()),
387+
parent,
388+
false
389+
);
390+
return new LessonHolder(binding);
385391
}
386392
}
387393

@@ -424,40 +430,36 @@ static class AdHolder extends RecyclerView.ViewHolder {
424430
}
425431

426432
static class LessonHolder extends RecyclerView.ViewHolder {
427-
final MaterialCardView card;
428-
final ShapeableImageView icon;
429-
final MaterialTextView title;
430-
final MaterialTextView summary;
431-
final FrameLayout widgetFrame;
432-
final MaterialButton externalButton;
433-
434-
LessonHolder(@NonNull View itemView) {
435-
super(itemView);
436-
card = (MaterialCardView) itemView;
437-
icon = itemView.findViewById(android.R.id.icon);
438-
title = itemView.findViewById(android.R.id.title);
439-
summary = itemView.findViewById(android.R.id.summary);
440-
widgetFrame = itemView.findViewById(android.R.id.widget_frame);
441-
LayoutInflater.from(itemView.getContext())
442-
.inflate(R.layout.item_preference_widget_open_in_new, widgetFrame, true);
443-
externalButton = widgetFrame.findViewById(R.id.open_in_new);
433+
private final ItemPreferenceBinding binding;
434+
private final ItemPreferenceWidgetOpenInNewBinding widgetBinding;
435+
436+
LessonHolder(@NonNull ItemPreferenceBinding binding) {
437+
super(binding.getRoot());
438+
this.binding = binding;
439+
widgetBinding = ItemPreferenceWidgetOpenInNewBinding.inflate(
440+
LayoutInflater.from(binding.getRoot().getContext()),
441+
binding.widgetFrame,
442+
true
443+
);
444444
}
445445

446446
void bind(Lesson lesson, boolean first, boolean last) {
447447
if (lesson.iconRes != 0) {
448-
icon.setImageResource(lesson.iconRes);
449-
icon.setVisibility(View.VISIBLE);
448+
binding.icon.setImageResource(lesson.iconRes);
449+
binding.icon.setVisibility(View.VISIBLE);
450450
} else {
451-
icon.setVisibility(View.GONE);
451+
binding.icon.setVisibility(View.GONE);
452452
}
453-
title.setText(lesson.title);
453+
binding.title.setText(lesson.title);
454454
if (lesson.summary != null) {
455-
summary.setText(lesson.summary);
456-
summary.setVisibility(View.VISIBLE);
455+
binding.summary.setText(lesson.summary);
456+
binding.summary.setVisibility(View.VISIBLE);
457457
} else {
458-
summary.setVisibility(View.GONE);
458+
binding.summary.setVisibility(View.GONE);
459459
}
460460
boolean showExternalButton = lesson.opensInBrowser && lesson.intent != null;
461+
FrameLayout widgetFrame = binding.widgetFrame;
462+
MaterialButton externalButton = widgetBinding.openInNew;
461463
widgetFrame.setVisibility(showExternalButton ? View.VISIBLE : View.GONE);
462464
externalButton.setVisibility(showExternalButton ? View.VISIBLE : View.GONE);
463465
externalButton.setEnabled(showExternalButton);
@@ -486,30 +488,30 @@ private void applyCorners(boolean first, boolean last) {
486488
itemView.getResources().getDisplayMetrics());
487489
float dp24 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24,
488490
itemView.getResources().getDisplayMetrics());
489-
ShapeAppearanceModel.Builder builder = card.getShapeAppearanceModel().toBuilder()
491+
ShapeAppearanceModel.Builder builder = binding.lessonCard.getShapeAppearanceModel().toBuilder()
490492
.setTopLeftCorner(CornerFamily.ROUNDED, first ? dp24 : dp4)
491493
.setTopRightCorner(CornerFamily.ROUNDED, first ? dp24 : dp4)
492494
.setBottomLeftCorner(CornerFamily.ROUNDED, last ? dp24 : dp4)
493495
.setBottomRightCorner(CornerFamily.ROUNDED, last ? dp24 : dp4);
494-
card.setShapeAppearanceModel(builder.build());
496+
binding.lessonCard.setShapeAppearanceModel(builder.build());
495497
}
496498
}
497499

498500
static class CategoryHolder extends RecyclerView.ViewHolder {
499-
final MaterialTextView title;
501+
private final ItemPreferenceCategoryBinding binding;
500502

501-
CategoryHolder(@NonNull View itemView) {
502-
super(itemView);
503-
title = itemView.findViewById(android.R.id.title);
503+
CategoryHolder(@NonNull ItemPreferenceCategoryBinding binding) {
504+
super(binding.getRoot());
505+
this.binding = binding;
504506
}
505507

506508
void bind(Category category) {
507509
if (category.iconRes != 0) {
508-
title.setCompoundDrawablesRelativeWithIntrinsicBounds(category.iconRes, 0, 0, 0);
510+
binding.title.setCompoundDrawablesRelativeWithIntrinsicBounds(category.iconRes, 0, 0, 0);
509511
} else {
510-
title.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
512+
binding.title.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
511513
}
512-
title.setText(category.title);
514+
binding.title.setText(category.title);
513515
}
514516
}
515517
}

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public class AndroidSDK extends UpNavigationActivity {
6464
protected void onCreate(Bundle savedInstanceState) {
6565
super.onCreate(savedInstanceState);
6666
binding = ActivityAndroidSdkBinding.inflate(getLayoutInflater());
67-
setContentView(binding.getRoot()); EdgeToEdgeDelegate.apply(this, binding.scrollView);
67+
setContentView(binding.getRoot());
68+
EdgeToEdgeDelegate.apply(this, binding.scrollView);
6869

6970
AdUtils.loadBanner(binding.adViewBottom);
7071
AdUtils.loadBanner(binding.adView);
@@ -74,7 +75,7 @@ protected void onCreate(Bundle savedInstanceState) {
7475
}
7576

7677
private void createDynamicTable() {
77-
TableLayout tableLayout = binding.cardViewTableLayout.findViewById(R.id.table_layout);
78+
TableLayout tableLayout = binding.tableLayout;
7879
for (AndroidVersion version : androidVersions) {
7980
TableRow row = new TableRow(this);
8081

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@
22

33
import android.os.Bundle;
44
import android.view.LayoutInflater;
5-
import android.view.View;
65
import android.view.ViewGroup;
7-
import android.widget.TextView;
86

97
import androidx.annotation.NonNull;
108
import androidx.recyclerview.widget.LinearLayoutManager;
119
import androidx.recyclerview.widget.RecyclerView;
1210

13-
import com.d4rk.androidtutorials.java.R;
1411
import com.d4rk.androidtutorials.java.ads.AdUtils;
1512
import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsNavigationAndSearchingBinding;
13+
import com.d4rk.androidtutorials.java.databinding.ItemShortcutBinding;
1614
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
1715
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
1816

@@ -72,15 +70,14 @@ private static class ShortcutsAdapter extends RecyclerView.Adapter<ShortcutsAdap
7270
@NonNull
7371
@Override
7472
public ShortcutHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
75-
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_shortcut, parent, false);
76-
return new ShortcutHolder(view);
73+
ItemShortcutBinding binding = ItemShortcutBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
74+
return new ShortcutHolder(binding);
7775
}
7876

7977
@Override
8078
public void onBindViewHolder(@NonNull ShortcutHolder holder, int position) {
8179
Shortcut item = items.get(position);
82-
holder.key.setText(item.key);
83-
holder.description.setText(item.description);
80+
holder.bind(item);
8481
}
8582

8683
@Override
@@ -89,13 +86,16 @@ public int getItemCount() {
8986
}
9087

9188
static class ShortcutHolder extends RecyclerView.ViewHolder {
92-
final TextView key;
93-
final TextView description;
89+
private final ItemShortcutBinding binding;
9490

95-
ShortcutHolder(@NonNull View itemView) {
96-
super(itemView);
97-
key = itemView.findViewById(R.id.shortcut_key);
98-
description = itemView.findViewById(R.id.shortcut_description);
91+
ShortcutHolder(@NonNull ItemShortcutBinding binding) {
92+
super(binding.getRoot());
93+
this.binding = binding;
94+
}
95+
96+
void bind(@NonNull Shortcut shortcut) {
97+
binding.shortcutKey.setText(shortcut.key());
98+
binding.shortcutDescription.setText(shortcut.description());
9999
}
100100
}
101101
}

0 commit comments

Comments
 (0)