Designing Onboarding Screens Using XML

Onboarding screens are a crucial part of any mobile application. They provide users with an initial introduction to the app’s features and guide them through the basic functionalities. In this post, we’ll explore how to design effective onboarding screens using XML layouts in Android, combining simplicity with usability.

What are Onboarding Screens?

Onboarding screens are the first set of screens a user encounters when opening an app for the first time. Their primary purpose is to educate the user on the app’s main features, benefits, and how to use it effectively. A well-designed onboarding process can significantly improve user retention and engagement.

Why Use XML for Designing Onboarding Screens?

  • Simplicity: XML is straightforward and easy to understand for defining UI layouts.
  • Control: Offers precise control over UI elements and their attributes.
  • Compatibility: Ensures broad compatibility across various Android devices and versions.

How to Design Effective Onboarding Screens Using XML

To create compelling onboarding screens, follow these steps:

Step 1: Set Up the Project

Create a new Android project in Android Studio or open an existing one.

Step 2: Add Dependencies

Include any necessary dependencies in your build.gradle file. For this example, we’ll assume you have the standard AndroidX dependencies:

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    // Add any additional dependencies if needed
}

Step 3: Create the Onboarding Activity

Create a new activity for the onboarding screens. Let’s call it OnboardingActivity.java or OnboardingActivity.kt for Kotlin users.

Java version:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class OnboardingActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_onboarding);
    }
}

Kotlin version:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class OnboardingActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_onboarding)
    }
}

Step 4: Create the XML Layout for Onboarding Screens

Create the layout file activity_onboarding.xml in the res/layout directory.

<?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=".OnboardingActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPagerOnboarding"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/tabLayoutIndicator"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayoutIndicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="16dp"
        app:tabBackground="@drawable/tab_selector"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

Here we are using a ViewPager2 to swipe between different onboarding screens and a TabLayout to indicate the current screen. A tab_selector.xml file will be used for defining the tab indicator’s appearance.

Step 5: Create Individual Slide Layouts

Each onboarding screen will be a separate layout. Let’s create three slides:

slide_screen1.xml

<?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:orientation="vertical"
    android:gravity="center"
    android:padding="24dp">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/onboarding_image1"
        android:layout_marginBottom="16dp"
        android:contentDescription="First onboarding screen image"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Welcome to Our App!"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Discover amazing features and enhance your experience with our app."
        android:textSize="16sp"
        android:gravity="center"/>
</LinearLayout>

slide_screen2.xml

<?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:orientation="vertical"
    android:gravity="center"
    android:padding="24dp">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/onboarding_image2"
        android:layout_marginBottom="16dp"
        android:contentDescription="Second onboarding screen image"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Explore New Features"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Unlock advanced capabilities and improve your workflow."
        android:textSize="16sp"
        android:gravity="center"/>
</LinearLayout>

slide_screen3.xml

<?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:orientation="vertical"
    android:gravity="center"
    android:padding="24dp">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/onboarding_image3"
        android:layout_marginBottom="16dp"
        android:contentDescription="Third onboarding screen image"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Started Today!"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ready to dive in? Create an account or log in to start using our app."
        android:textSize="16sp"
        android:gravity="center"
        android:layout_marginBottom="24dp"/>

    <Button
        android:id="@+id/buttonGetStarted"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Started"
        android:padding="12dp"/>
</LinearLayout>

Make sure to add appropriate images to the drawable folder and replace @drawable/onboarding_image1, @drawable/onboarding_image2, and @drawable/onboarding_image3 with your image resources.

Step 6: Create tab_selector.xml

Create a tab_selector.xml file inside the res/drawable directory.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true" android:drawable="@drawable/indicator_selected" />
    <item android:drawable="@drawable/indicator_unselected" />
</selector>

Add two new drawables inside the res/drawable folder named indicator_selected.xml and indicator_unselected.xml

indicator_selected.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/design_default_color_primary" />
    <size
        android:width="8dp"
        android:height="8dp" />
</shape>

indicator_unselected.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#BBBBBB" />
    <size
        android:width="8dp"
        android:height="8dp" />
</shape>

Step 7: Set Up ViewPager2 Adapter

Create an adapter to handle the slides in the ViewPager2. Create a class named OnboardingViewPagerAdapter.java (or OnboardingViewPagerAdapter.kt for Kotlin) for this.

Java Version:

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class OnboardingViewPagerAdapter extends RecyclerView.Adapter<OnboardingViewPagerAdapter.OnboardingViewHolder> {

    private List<Integer> slideLayouts;

    public OnboardingViewPagerAdapter(List<Integer> slideLayouts) {
        this.slideLayouts = slideLayouts;
    }

    @NonNull
    @Override
    public OnboardingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(slideLayouts.get(viewType), parent, false);
        return new OnboardingViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull OnboardingViewHolder holder, int position) {
        // You can set up the UI elements in the slides here, if needed
    }

    @Override
    public int getItemCount() {
        return slideLayouts.size();
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    static class OnboardingViewHolder extends RecyclerView.ViewHolder {
        public OnboardingViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

Kotlin Version:

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class OnboardingViewPagerAdapter(private val slideLayouts: List) :
    RecyclerView.Adapter() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OnboardingViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(slideLayouts[viewType], parent, false)
        return OnboardingViewHolder(view)
    }

    override fun onBindViewHolder(holder: OnboardingViewHolder, position: Int) {
        // Set up UI elements if needed
    }

    override fun getItemCount(): Int = slideLayouts.size

    override fun getItemViewType(position: Int): Int = position

    class OnboardingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

Step 8: Connect Adapter to ViewPager2 in Activity

Connect the adapter to the ViewPager2 in your OnboardingActivity.

Java Version:

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;

import java.util.Arrays;
import java.util.List;

public class OnboardingActivity extends AppCompatActivity {

    private ViewPager2 viewPagerOnboarding;
    private TabLayout tabLayoutIndicator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_onboarding);

        viewPagerOnboarding = findViewById(R.id.viewPagerOnboarding);
        tabLayoutIndicator = findViewById(R.id.tabLayoutIndicator);

        List<Integer> slideLayouts = Arrays.asList(R.layout.slide_screen1, R.layout.slide_screen2, R.layout.slide_screen3);

        OnboardingViewPagerAdapter adapter = new OnboardingViewPagerAdapter(slideLayouts);
        viewPagerOnboarding.setAdapter(adapter);

        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab());
        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab());
        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab());

        tabLayoutIndicator.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPagerOnboarding.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });

        viewPagerOnboarding.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                tabLayoutIndicator.selectTab(tabLayoutIndicator.getTabAt(position));
            }
        });
    }
}

Kotlin Version:

import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.widget.ViewPager2
import android.os.Bundle
import com.google.android.material.tabs.TabLayout

class OnboardingActivity : AppCompatActivity() {

    private lateinit var viewPagerOnboarding: ViewPager2
    private lateinit var tabLayoutIndicator: TabLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_onboarding)

        viewPagerOnboarding = findViewById(R.id.viewPagerOnboarding)
        tabLayoutIndicator = findViewById(R.id.tabLayoutIndicator)

        val slideLayouts = listOf(R.layout.slide_screen1, R.layout.slide_screen2, R.layout.slide_screen3)

        val adapter = OnboardingViewPagerAdapter(slideLayouts)
        viewPagerOnboarding.adapter = adapter

        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab())
        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab())
        tabLayoutIndicator.addTab(tabLayoutIndicator.newTab())

        tabLayoutIndicator.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab) {
                viewPagerOnboarding.currentItem = tab.position
            }

            override fun onTabUnselected(tab: TabLayout.Tab) {}
            override fun onTabReselected(tab: TabLayout.Tab) {}
        })

        viewPagerOnboarding.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                tabLayoutIndicator.selectTab(tabLayoutIndicator.getTabAt(position))
            }
        })
    }
}

Step 9: Handle “Get Started” Button (Optional)

If your last onboarding screen has a “Get Started” button, implement its functionality. For example, you can navigate to the main activity and save a flag to prevent showing onboarding again.

Step 10: Create Images

Include necessary images inside the res/drawable folder.

Best Practices for Designing Onboarding Screens

  • Keep It Concise: Use clear and straightforward language.
  • Visual Appeal: Employ high-quality images and animations.
  • Highlight Key Features: Focus on the core benefits of the app.
  • Interactive Elements: Use interactive elements for a better user experience.
  • Progress Indicators: Show users their progress through the onboarding process.

Conclusion

Designing effective onboarding screens using XML in Android offers a balance of control, simplicity, and compatibility. By following these steps and best practices, you can create onboarding experiences that enhance user engagement and improve app retention. Combining straightforward XML layouts with visually appealing elements will ensure a seamless introduction for new users.