Skip to content

Commit ba89599

Browse files
style onboarding with swipe pager and dots
1 parent 851d6eb commit ba89599

13 files changed

+180
-70
lines changed

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/BottomLabelsFragment.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
2929
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
3030
super.onViewCreated(view, savedInstanceState);
3131
viewModel = new ViewModelProvider(requireActivity()).get(OnboardingViewModel.class);
32-
binding.buttonNext.setOnClickListener(v -> {
33-
int checkedId = binding.labelsGroup.getCheckedRadioButtonId();
34-
String[] values = getResources().getStringArray(R.array.preference_bottom_navigation_bar_labels_values);
35-
String value = values[0];
36-
if (checkedId == R.id.radio_selected) {
37-
value = values[1];
38-
} else if (checkedId == R.id.radio_unlabeled) {
39-
value = values[2];
40-
}
41-
viewModel.setBottomNavLabels(value);
42-
((OnboardingActivity) requireActivity()).nextPage();
43-
});
32+
}
33+
34+
public void saveSelection() {
35+
int checkedId = binding.labelsGroup.getCheckedRadioButtonId();
36+
String[] values = getResources().getStringArray(R.array.preference_bottom_navigation_bar_labels_values);
37+
String value = values[0];
38+
if (checkedId == R.id.radio_selected) {
39+
value = values[1];
40+
} else if (checkedId == R.id.radio_unlabeled) {
41+
value = values[2];
42+
}
43+
viewModel.setBottomNavLabels(value);
4444
}
4545

4646
@Override

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/DataFragment.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
2828
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
2929
super.onViewCreated(view, savedInstanceState);
3030
viewModel = new ViewModelProvider(requireActivity()).get(OnboardingViewModel.class);
31-
binding.buttonFinish.setOnClickListener(v -> {
32-
viewModel.markOnboardingComplete();
33-
((OnboardingActivity) requireActivity()).finishOnboarding();
34-
});
3531
}
3632

3733
@Override

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22

33
import android.content.Intent;
44
import android.os.Bundle;
5+
import android.view.View;
6+
import android.widget.ImageView;
57

68
import androidx.annotation.NonNull;
79
import androidx.appcompat.app.AppCompatActivity;
810
import androidx.fragment.app.Fragment;
11+
import androidx.lifecycle.ViewModelProvider;
912
import androidx.viewpager2.adapter.FragmentStateAdapter;
1013

14+
import com.d4rk.androidtutorials.java.R;
1115
import com.d4rk.androidtutorials.java.databinding.ActivityOnboardingBinding;
1216
import com.d4rk.androidtutorials.java.ui.screens.main.MainActivity;
17+
import com.google.android.material.tabs.TabLayout;
18+
import com.google.android.material.tabs.TabLayoutMediator;
1319

1420
import dagger.hilt.android.AndroidEntryPoint;
1521

@@ -18,30 +24,112 @@ public class OnboardingActivity extends AppCompatActivity {
1824

1925
private ActivityOnboardingBinding binding;
2026
private OnboardingPagerAdapter adapter;
27+
private OnboardingViewModel viewModel;
28+
private int currentPosition = 0;
2129

2230
@Override
2331
protected void onCreate(Bundle savedInstanceState) {
2432
super.onCreate(savedInstanceState);
2533
binding = ActivityOnboardingBinding.inflate(getLayoutInflater());
2634
setContentView(binding.getRoot());
2735

36+
viewModel = new ViewModelProvider(this).get(OnboardingViewModel.class);
37+
2838
adapter = new OnboardingPagerAdapter(this);
2939
binding.viewPager.setAdapter(adapter);
30-
binding.viewPager.setUserInputEnabled(false);
31-
}
3240

33-
void nextPage() {
34-
int current = binding.viewPager.getCurrentItem();
35-
if (current < adapter.getItemCount() - 1) {
36-
binding.viewPager.setCurrentItem(current + 1);
41+
binding.viewPager.registerOnPageChangeCallback(new androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback() {
42+
@Override
43+
public void onPageSelected(int position) {
44+
super.onPageSelected(position);
45+
if (position > currentPosition) {
46+
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + currentPosition);
47+
if (fragment instanceof ThemeFragment) {
48+
((ThemeFragment) fragment).saveSelection();
49+
} else if (fragment instanceof StartPageFragment) {
50+
((StartPageFragment) fragment).saveSelection();
51+
} else if (fragment instanceof BottomLabelsFragment) {
52+
((BottomLabelsFragment) fragment).saveSelection();
53+
}
54+
}
55+
currentPosition = position;
56+
}
57+
});
58+
59+
new TabLayoutMediator(binding.tabIndicator, binding.viewPager, (tab, position) -> {
60+
ImageView dot = new ImageView(this);
61+
dot.setImageResource(R.drawable.onboarding_dot_unselected);
62+
tab.setCustomView(dot);
63+
}).attach();
64+
65+
TabLayout.Tab firstTab = binding.tabIndicator.getTabAt(0);
66+
if (firstTab != null && firstTab.getCustomView() instanceof ImageView) {
67+
((ImageView) firstTab.getCustomView()).setImageResource(R.drawable.onboarding_dot_selected);
3768
}
69+
70+
binding.tabIndicator.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
71+
@Override
72+
public void onTabSelected(TabLayout.Tab tab) {
73+
if (tab.getCustomView() instanceof ImageView) {
74+
((ImageView) tab.getCustomView()).setImageResource(R.drawable.onboarding_dot_selected);
75+
}
76+
updateButtons(tab.getPosition());
77+
}
78+
79+
@Override
80+
public void onTabUnselected(TabLayout.Tab tab) {
81+
if (tab.getCustomView() instanceof ImageView) {
82+
((ImageView) tab.getCustomView()).setImageResource(R.drawable.onboarding_dot_unselected);
83+
}
84+
}
85+
86+
@Override
87+
public void onTabReselected(TabLayout.Tab tab) { }
88+
});
89+
90+
binding.buttonBack.setOnClickListener(v -> {
91+
int current = binding.viewPager.getCurrentItem();
92+
if (current > 0) {
93+
binding.viewPager.setCurrentItem(current - 1);
94+
}
95+
});
96+
97+
binding.buttonNext.setOnClickListener(v -> {
98+
int current = binding.viewPager.getCurrentItem();
99+
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + current);
100+
if (fragment instanceof ThemeFragment) {
101+
((ThemeFragment) fragment).saveSelection();
102+
} else if (fragment instanceof StartPageFragment) {
103+
((StartPageFragment) fragment).saveSelection();
104+
} else if (fragment instanceof BottomLabelsFragment) {
105+
((BottomLabelsFragment) fragment).saveSelection();
106+
}
107+
108+
if (current < adapter.getItemCount() - 1) {
109+
binding.viewPager.setCurrentItem(current + 1);
110+
} else {
111+
viewModel.markOnboardingComplete();
112+
finishOnboarding();
113+
}
114+
});
115+
116+
updateButtons(0);
38117
}
39118

40119
void finishOnboarding() {
41120
startActivity(new Intent(this, MainActivity.class));
42121
finish();
43122
}
44123

124+
private void updateButtons(int position) {
125+
binding.buttonBack.setVisibility(position == 0 ? View.INVISIBLE : View.VISIBLE);
126+
if (position == adapter.getItemCount() - 1) {
127+
binding.buttonNext.setText(R.string.finish);
128+
} else {
129+
binding.buttonNext.setText(R.string.next);
130+
}
131+
}
132+
45133
private static class OnboardingPagerAdapter extends FragmentStateAdapter {
46134

47135
OnboardingPagerAdapter(@NonNull AppCompatActivity activity) {

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/StartPageFragment.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
2929
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
3030
super.onViewCreated(view, savedInstanceState);
3131
viewModel = new ViewModelProvider(requireActivity()).get(OnboardingViewModel.class);
32-
binding.buttonNext.setOnClickListener(v -> {
33-
int checkedId = binding.startPageGroup.getCheckedRadioButtonId();
34-
String[] values = getResources().getStringArray(R.array.preference_default_tab_values);
35-
String value = values[0];
36-
if (checkedId == R.id.radio_android_studio) {
37-
value = values[1];
38-
} else if (checkedId == R.id.radio_about) {
39-
value = values[2];
40-
}
41-
viewModel.setDefaultTab(value);
42-
((OnboardingActivity) requireActivity()).nextPage();
43-
});
32+
}
33+
34+
public void saveSelection() {
35+
int checkedId = binding.startPageGroup.getCheckedRadioButtonId();
36+
String[] values = getResources().getStringArray(R.array.preference_default_tab_values);
37+
String value = values[0];
38+
if (checkedId == R.id.radio_android_studio) {
39+
value = values[1];
40+
} else if (checkedId == R.id.radio_about) {
41+
value = values[2];
42+
}
43+
viewModel.setDefaultTab(value);
4444
}
4545

4646
@Override

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/ThemeFragment.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
2929
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
3030
super.onViewCreated(view, savedInstanceState);
3131
viewModel = new ViewModelProvider(requireActivity()).get(OnboardingViewModel.class);
32-
binding.buttonNext.setOnClickListener(v -> {
33-
int checkedId = binding.themeGroup.getCheckedRadioButtonId();
34-
String[] values = getResources().getStringArray(R.array.preference_theme_values);
35-
String value = values[0];
36-
if (checkedId == R.id.radio_light) {
37-
value = values[1];
38-
} else if (checkedId == R.id.radio_dark) {
39-
value = values[2];
40-
} else if (checkedId == R.id.radio_auto_battery) {
41-
value = values[3];
42-
}
43-
viewModel.setTheme(value);
44-
((OnboardingActivity) requireActivity()).nextPage();
45-
});
32+
}
33+
34+
public void saveSelection() {
35+
int checkedId = binding.themeGroup.getCheckedRadioButtonId();
36+
String[] values = getResources().getStringArray(R.array.preference_theme_values);
37+
String value = values[0];
38+
if (checkedId == R.id.radio_light) {
39+
value = values[1];
40+
} else if (checkedId == R.id.radio_dark) {
41+
value = values[2];
42+
} else if (checkedId == R.id.radio_auto_battery) {
43+
value = values[3];
44+
}
45+
viewModel.setTheme(value);
4646
}
4747

4848
@Override
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3+
<size android:width="8dp" android:height="8dp" />
4+
<solid android:color="?attr/colorPrimary" />
5+
</shape>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3+
<size android:width="8dp" android:height="8dp" />
4+
<solid android:color="@android:color/darker_gray" />
5+
</shape>

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,44 @@
99
android:layout_width="0dp"
1010
android:layout_height="0dp"
1111
app:layout_constraintTop_toTopOf="parent"
12-
app:layout_constraintBottom_toBottomOf="parent"
12+
app:layout_constraintBottom_toTopOf="@+id/bottomBar"
1313
app:layout_constraintStart_toStartOf="parent"
1414
app:layout_constraintEnd_toEndOf="parent" />
1515

16+
<LinearLayout
17+
android:id="@+id/bottomBar"
18+
android:layout_width="0dp"
19+
android:layout_height="wrap_content"
20+
android:orientation="horizontal"
21+
android:gravity="center_vertical"
22+
android:padding="16dp"
23+
app:layout_constraintBottom_toBottomOf="parent"
24+
app:layout_constraintStart_toStartOf="parent"
25+
app:layout_constraintEnd_toEndOf="parent">
26+
27+
<com.google.android.material.button.MaterialButton
28+
android:id="@+id/buttonBack"
29+
style="?attr/materialButtonOutlinedStyle"
30+
android:layout_width="wrap_content"
31+
android:layout_height="wrap_content"
32+
android:text="@string/back" />
33+
34+
<com.google.android.material.tabs.TabLayout
35+
android:id="@+id/tabIndicator"
36+
android:layout_width="0dp"
37+
android:layout_height="wrap_content"
38+
android:layout_weight="1"
39+
android:background="@android:color/transparent"
40+
app:tabIndicatorGravity="center"
41+
app:tabIndicatorFullWidth="false"
42+
app:tabRippleColor="@android:color/transparent" />
43+
44+
<com.google.android.material.button.MaterialButton
45+
android:id="@+id/buttonNext"
46+
android:layout_width="wrap_content"
47+
android:layout_height="wrap_content"
48+
android:text="@string/next" />
49+
50+
</LinearLayout>
51+
1652
</androidx.constraintlayout.widget.ConstraintLayout>

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,5 @@
4040
android:text="@string/unlabeled" />
4141
</RadioGroup>
4242

43-
<Button
44-
android:id="@+id/button_next"
45-
android:layout_width="wrap_content"
46-
android:layout_height="wrap_content"
47-
android:text="@string/next" />
4843
</LinearLayout>
4944
</ScrollView>

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,5 @@
1313
android:layout_width="wrap_content"
1414
android:layout_height="wrap_content"
1515
android:text="@string/data_dialog_message" />
16-
17-
<Button
18-
android:id="@+id/button_finish"
19-
android:layout_width="wrap_content"
20-
android:layout_height="wrap_content"
21-
android:text="@string/finish" />
2216
</LinearLayout>
2317
</ScrollView>

0 commit comments

Comments
 (0)