OnBoarding biasanya digunakan developers sebagai halaman awal bagi pengguna yang baru saja menginstall aplikasi, biasanya dipergunakan untuk menyambut kedatangan pengguna baru dan memberikan sedikit informasi awal mengenai aplikasi. Itu biasanya, namun banyak juga yang tidak seperti biasanya.
Disini kita akan menggunakan ViewPager2 dengan adapter RecycleView. Baik tanpa perlu basa-basi mari kita segera mulai.
Tentunya dengan membuat sebuah project terlebih dahulu atau project yang sudah ada. Karena dalam contoh ini menggunakan kelas RippleDrawable sehingga minimum target SDK nya adalah 5.0 atau Lolipop. Kegunaan RippleDrawable disini adalah untuk memberikan efek warna pada sebuah view clicklistener.
Kemudian, jika Anda masih menggunakan Android Studio versi lama (mungkin dibawah 4.1), silahkan cek terlebih dahulu build.gradle(app) dan pastikan sudah terdapat library Google Material di dalamnya. Untuk versi diatas 4.1 library tersebut sudah di input otomatis saat membuat project baru. Berikut library nya :
implementation 'com.google.android.material:material:1.3.0'
Background Drawable :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#FFFFFF"
android:shape="rectangle">
<item>
<shape android:shape="rectangle">
<corners android:radius="10dp" />
<solid android:color="#C0DCC1" />
</shape>
</item>
</ripple>
bg_button_next.xml :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0CDCC7"
android:shape="rectangle">
<item>
<shape android:shape="oval">
<stroke
android:width="1dp"
android:color="#068F8E" />
<solid android:color="#FFFFFF"/>
</shape>
</item>
</ripple>
bg_timer.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="15dp" />
<solid android:color="#FFFFFF" />
</shape>
bg_view.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#5E5858"/>
</shape>
bg_viewpager.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#DFDCE3"/>
<corners android:bottomLeftRadius="40dp"
android:bottomRightRadius="40dp"/>
</shape>
indicator_active.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#068786"/>
<size android:width="20dp"
android:height="15dp"/>
<corners android:radius="20dp"/>
</shape>
indicator_inactive.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FFFFFF"/>
<size android:width="15dp"
android:height="15dp"/>
</shape>
Vector Drawable :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:autoMirrored="true"
android:tint="#5175FF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M13.49,5.48c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM9.89,19.38l1,-4.4 2.1,2v6h2v-7.5l-2.1,-2 0.6,-3c1.3,1.5 3.3,2.5 5.5,2.5v-2c-1.9,0 -3.5,-1 -4.3,-2.4l-1,-1.6c-0.4,-0.6 -1,-1 -1.7,-1 -0.3,0 -0.5,0.1 -0.8,0.1l-5.2,2.2v4.7h2v-3.4l1.8,-0.7 -1.6,8.1 -4.9,-1 -0.4,2 7,1.4z" />
</vector>
ic_highfive.xml :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:tint="#5175FF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M23,5.5V20c0,2.2 -1.8,4 -4,4h-7.3c-1.08,0 -2.1,-0.43 -2.85,-1.19L1,14.83c0,0 1.26,-1.23 1.3,-1.25c0.22,-0.19 0.49,-0.29 0.79,-0.29c0.22,0 0.42,0.06 0.6,0.16C3.73,13.46 8,15.91 8,15.91V4c0,-0.83 0.67,-1.5 1.5,-1.5S11,3.17 11,4v7h1V1.5C12,0.67 12.67,0 13.5,0S15,0.67 15,1.5V11h1V2.5C16,1.67 16.67,1 17.5,1S19,1.67 19,2.5V11h1V5.5C20,4.67 20.67,4 21.5,4S23,4.67 23,5.5z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M5.88,4.12L13.76,12l-7.88,7.88L8,22l10,-10L8,2z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:tint="#5175FF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M16,1L8,1C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3L19,4c0,-1.66 -1.34,-3 -3,-3zM14,21h-4v-1h4v1zM17.25,18L6.75,18L6.75,4h10.5v14z" />
</vector>
Secara default saat membuat project, kita akan memiliki activity_main.xml dengan java class MainActivity.java. Activity launcher dalam contoh ini masih MainActivity.java, disini kita hanya mendeteksi apakah pengguna baru atau bukan menggunakan SharedPreferences. Jika pengguna baru, maka pengguna akan diarahkan ke BoardingActivity.java (activity ini akan kita buat nanti).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/imageOnBoarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="@string/app_name"/>
<TextView
android:id="@+id/textTitleBoarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:textColor="@color/black"
android:textStyle="bold"
android:gravity="center"/>
<TextView
android:id="@+id/textDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:textSize="18sp"
android:textColor="@color/black"
android:gravity="center"/>
</LinearLayout>
package com.gwnbs.onboarding;
public class BoardingItemModel {
private final int imageBoarding;
private final String title, description;
public BoardingItemModel(int imageBoarding, String title, String description) {
this.imageBoarding = imageBoarding;
this.title = title;
this.description = description;
}
public int getImageBoarding() {
return imageBoarding;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
}
Nah sekarang mari kita buat activity baru yakni dengan layout activity_boarding.xml dan java class BoardingActivity.java. Setelah dibuat, kita tinggalkan sejenak dan beralih ke launcher activity terlebih dahulu. Untuk activity_main.xml, dalam contoh ini hanya memiliki sebuah view TextView untuk mereset data nantinya. Berikut isinya :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textResetData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_button_main"
android:padding="10dp"
android:text="Resed data"
android:textColor="@color/black"
android:textStyle="bold"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.gwnbs.onboarding;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
//Kunci string boolean pengguna baru/bukan
private static final String FIRST_USER = "firstTime";
private SharedPreferences preferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
preferences = getSharedPreferences("BOARDING", MODE_PRIVATE);
//Cek pengguna baru atau bukan menggunakan boolean dengan nilai default true,
//jika pengguna baru kita arahkan ke BoardingActivity
if (preferences.getBoolean(FIRST_USER, true)) {
finish();
startActivity(new Intent(this, BoardingActivity.class));
//Boolean kita simpan dengan nilai false
//Supaya saat membuka kembali aplikasi pengguna tidak diarahkan lagi ke BoardingActivity
preferences.edit().putBoolean(FIRST_USER, false).apply();
}
//Mereset data SharedPreferences ke awal
findViewById(R.id.textResetData).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
preferences.edit().clear().apply();
finish();
startActivity(getIntent());
}
});
}
}
package com.gwnbs.onboarding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class OnboardAdapter extends RecyclerView.Adapter<OnboardAdapter.OnboardViewHolder> {
private final List<BoardingItemModel> boardingItemModels;
public OnboardAdapter(List<BoardingItemModel> boardingItemModels) {
this.boardingItemModels = boardingItemModels;
}
@NonNull
@Override
public OnboardAdapter.OnboardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new OnboardViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_onboard, parent, false));
}
@Override
public void onBindViewHolder(@NonNull OnboardAdapter.OnboardViewHolder holder, int position) {
holder.bindOnboard(boardingItemModels.get(position));
}
@Override
public int getItemCount() {
return boardingItemModels.size();
}
static class OnboardViewHolder extends RecyclerView.ViewHolder {
private final TextView textTitleBoarding, textDescription;
private final ImageView imageOnboarding;
OnboardViewHolder(@NonNull View itemView) {
super(itemView);
textTitleBoarding = itemView.findViewById(R.id.textTitleBoarding);
textDescription = itemView.findViewById(R.id.textDescription);
imageOnboarding = itemView.findViewById(R.id.imageOnBoarding);
}
void bindOnboard(BoardingItemModel boardingItemModel) {
imageOnboarding.setImageResource(boardingItemModel.getImageBoarding());
textTitleBoarding.setText(boardingItemModel.getTitle());
textDescription.setText(boardingItemModel.getDescription());
}
}
}
Nah sekarang kita menuju ke topik inti yaitu BoardingActivity, pertama-tama tentunya dengan mengatur terlebih dahulu layout nya. Terdapat cukup banyak view didalam layout ini, sebenarnya sedikit jika bukan project tutorial.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BoardingActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/onBoardViewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@drawable/bg_viewpager"
app:layout_constraintBottom_toTopOf="@id/viewCenterNext"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/indicatorsContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textSkip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:text="LEWAT"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/bg_view"
app:layout_constraintBottom_toBottomOf="@id/imageNext"
app:layout_constraintEnd_toEndOf="@id/imageNext"
app:layout_constraintStart_toStartOf="@id/imageNext"
app:layout_constraintTop_toTopOf="@id/imageNext" />
<View
android:id="@+id/viewCenterNext"
android:layout_width="1dp"
android:layout_height="1dp"
app:layout_constraintBottom_toBottomOf="@id/imageNext"
app:layout_constraintEnd_toEndOf="@id/imageNext"
app:layout_constraintStart_toStartOf="@id/imageNext"
app:layout_constraintTop_toTopOf="@id/imageNext" />
<ImageView
android:id="@+id/imageNext"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="50dp"
android:background="@drawable/bg_button_next"
android:contentDescription="@string/app_name"
android:padding="3dp"
android:src="@drawable/ic_next"
app:layout_constraintBottom_toTopOf="@id/textGetStarted"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:tint="@color/teal_700" />
<TextView
android:id="@+id/textGetStarted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:background="@drawable/bg_button_main"
android:padding="10dp"
android:text="Mulai Sekarang!"
android:textColor="@color/black"
android:textSize="22sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/textCountDownTimer"
android:layout_width="150dp"
android:layout_height="150dp"
android:textColor="@color/black"
android:textSize="100sp"
android:textStyle="bold"
android:gravity="center"
android:visibility="gone"
android:background="@drawable/bg_timer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.gwnbs.onboarding;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import java.util.ArrayList;
import java.util.List;
public class BoardingActivity extends AppCompatActivity {
//Variable views
private ViewPager2 onBoardViewPager;
private LinearLayout indicatorsContainer;
private TextView textCountDownTimer;
private ImageView imageNext;
private CountDownTimer countDownTimer;
private OnboardAdapter onboardAdapter;
private final List<BoardingItemModel> boardingItemModels = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_boarding);
//Inisialisai id dari views
onBoardViewPager = findViewById(R.id.onBoardViewPager);
indicatorsContainer = findViewById(R.id.indicatorsContainer);
textCountDownTimer = findViewById(R.id.textCountDownTimer);
imageNext = findViewById(R.id.imageNext);
//Memanggil metode void ke onCreate
setItemsOnboarding();
setupIndicators();
setCurrentIndicators(0);
findViewById(R.id.textSkip).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoMainActivity();
}
});
findViewById(R.id.textGetStarted).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoMainActivity();
}
});
}
private void setItemsOnboarding() {
//Menentukan nilai-nilai ImageView dan TextViews untuk item-item
boardingItemModels.add(new BoardingItemModel(R.drawable.ic_highfive,
"Selamat Datang!", "Hi, pengguna " + Build.MODEL
+ ". Selamat datang! Terima kasih telah menggunakan aplikasi kami."));
boardingItemModels.add(new BoardingItemModel(R.drawable.ic_phone, "Khusus Untuk Smartphone Anda",
"Aplikasi kami dirancang khusus untuk smartphone Anda, dijamin Anda tidak akan kecewa."));
boardingItemModels.add(new BoardingItemModel(R.drawable.ic_go, "Sudahkah Anda Siap?",
"Jika Anda tidak mengklik tombol, dalam hitungan mundur selama 20 detik," +
" Anda akan kami bawa ke halaman utama aplikasi."));
//Menghubungkan viewPager ke adapter Recycleview
onboardAdapter = new OnboardAdapter(boardingItemModels);
onBoardViewPager.setAdapter(onboardAdapter);
//Menyinkronkan halaman viewpager dengan indicators
onBoardViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
setCurrentIndicators(position);
//Jika halaman viewpager mencapai halaman terakhir
//Kita setel penghitung waktu mundur untuk ke MainActivity
if (position == 2) {
textCountDownTimer.setVisibility(View.VISIBLE);
textCountDownTimer.animate().rotation(360).setDuration(2000).withEndAction(new Runnable() {
@Override
public void run() {
textCountDownTimer.setRotation(0);
countDownTimer = new CountDownTimer(21000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
int detik = (int) (millisUntilFinished / 1000);
textCountDownTimer.setText(String.valueOf(detik));
}
@Override
public void onFinish() {
gotoMainActivity();
}
}.start();
}
}).start();
} else {
//Jika halaman di geser kehalaman sebelumnya sebelum waktu penghitung selesai
//penghitung waktu mundur kita batalkan dan textviewnya kita hilangkan
if (countDownTimer !=null) {
countDownTimer.cancel();
countDownTimer = null;
}
textCountDownTimer.setText("");
textCountDownTimer.setVisibility(View.GONE);
}
}
});
//Mode scrolling viewpager
RecyclerView recyclerView = (RecyclerView) onBoardViewPager.getChildAt(0);
recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
//Menyinkronkan icon next dengan viewPager
imageNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int items = onBoardViewPager.getCurrentItem();
if (items + 1 < onboardAdapter.getItemCount()) {
onBoardViewPager.setCurrentItem(items + 1);
} else {
gotoMainActivity();
}
}
});
}
//Menyetel indikator, dipanggil di dalam onCreate
private void setupIndicators() {
//Membuat image array sebanyak jumlah halaman viewpager
ImageView[] indicators = new ImageView[onboardAdapter.getItemCount()];
//Layoutparams untuk menyetel margin
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(0, 8, 5, 8);
//java loop untuk menentukan indikator dan menyetel nilai default background indikatornya
for (int i = 0; i < indicators.length; i++) {
indicators[i] = new ImageView(this);
indicators[i].setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),
R.drawable.indicator_inactive));
indicators[i].setLayoutParams(layoutParams);
indicatorsContainer.addView(indicators[i]);
}
}
//Menyetel indikator saat halaman di scroll
private void setCurrentIndicators(int position) {
for (int i = 0; i < indicatorsContainer.getChildCount(); i++) {
ImageView imageView = (ImageView) indicatorsContainer.getChildAt(i);
if (i == position) {
imageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),
R.drawable.indicator_active));
} else {
imageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),
R.drawable.indicator_inactive));
}
}
}
//Intent ke MainActivity
private void gotoMainActivity() {
finish();
startActivity(new Intent(this, MainActivity.class));
if (countDownTimer!=null) {
countDownTimer.cancel();
countDownTimer = null;
}
}
}
Sekian dan terima kasih atas waktu dan kunjungannya.
0 comments:
Post a Comment