Onboarding screens are a popular way of introducing the app to the user. Although it will last only a few seconds, it is important because the story of the user dealing with your app begins from the first impression through the decision on trying to the actual interaction.

You will find many libraries for onboarding screens. But when u can create an awesome, sleek onboarding screen of a great standard and quality like some of the most popular apps, easily using just a recyclerview, it’s definitely the better way to go.

Layout

Okay, assuming you have created a new project on Android Studio, let’s begin by creating the layout.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/bottomlayout"
        android:orientation="vertical"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btnsignup"
            android:textColor="@android:color/white"
            android:text="GET STARTED"
            android:layout_marginLeft="32dp"
            android:layout_marginRight="32dp"
            android:padding="12dp"
            android:background="@drawable/btn_bg_gradient"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/btnlogin"
            android:textColor="@color/colorPrimary"
            android:text="Got an account? SIGN IN"
            android:padding="12dp"
            android:layout_marginBottom="32dp"
            android:background="@null"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <RelativeLayout
        android:layout_above="@id/bottomlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v7.widget.RecyclerView>

    </RelativeLayout>

</RelativeLayout>

OptionalNotice the “GET STARTED” button, you can use any background, or remove the attribute for default style. But if you would like the same effect as shown in the picture, add this file to drawable folder.

btn_bg_gradient.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient android:angle="0"
        android:startColor="#2ECAD5"
        android:endColor="#2B95CE"/>
    <corners
        android:radius="45dp">
    </corners>
</shape>

That’s all the work for layout. Now let’s move to Java.

RecyclerView: scroll, paging, & indicators

Initialise the RecyclerView in the activity’s java code.

 recyclerView = findViewById(R.id.recyclerView);

And we add a layout manager, which decides the direction of scroll for the RecyclerView. We need horizontal scroll.

 recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));

Now the recyclerview will scroll horizontally, but that’s not all. We also need it to snap to each page/item. So we use PagerSnapHelper class provided by Android.

PagerSnapHelper pagerSnapHelper = new PagerSnapHelper();
pagerSnapHelper.attachToRecyclerView(recyclerView);

We are done with the scroll and paging, now we need to add the indicator dots as item decoration.

Create a new class named CirclePagerIndicatorDecoration.java

public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration {
    private int colorActive = 0xDE000000;
    private int colorInactive = 0x33000000;

    private static final float DP = Resources.getSystem().getDisplayMetrics().density;
    private final int mIndicatorHeight = (int) (DP * 16);
    private final float mIndicatorStrokeWidth = DP * 4;
    private final float mIndicatorItemLength = DP * 4;
    private final float mIndicatorItemPadding = DP * 8;
    private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
    private final Paint mPaint = new Paint();

    public CirclePagerIndicatorDecoration() {
        mPaint.setStrokeWidth(mIndicatorStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        int itemCount = parent.getAdapter().getItemCount();

        // center horizontally, calculate width and subtract half from center
        float totalLength = mIndicatorItemLength * itemCount;
        float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;
        float indicatorTotalWidth = totalLength + paddingBetweenItems;
        float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;

        // center vertically in the allotted space
        float indicatorPosY = parent.getHeight() - mIndicatorHeight * 2F;

        drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);

        // find active page (which should be highlighted)
        LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
        int activePosition = layoutManager.findFirstVisibleItemPosition();
        if (activePosition == RecyclerView.NO_POSITION) {
            return;
        }

        // find offset of active page (if the user is scrolling)
        final View activeChild = layoutManager.findViewByPosition(activePosition);
        int left = activeChild.getLeft();
        int width = activeChild.getWidth();
        int right = activeChild.getRight();

        // on swipe the active item will be positioned from [-width, 0]
        // interpolate offset for smooth animation
        float progress = mInterpolator.getInterpolation(left * -1 / (float) width);

        drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress);
    }

    private void drawInactiveIndicators(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) {
        mPaint.setColor(colorInactive);

        // width of item indicator including padding
        final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;

        float start = indicatorStartX;
        for (int i = 0; i < itemCount; i++) {

            c.drawCircle(start, indicatorPosY, mIndicatorItemLength / 2F, mPaint);
            start += itemWidth;
        }
    }

    private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,
                                int highlightPosition, float progress) {
        mPaint.setColor(colorActive);

        // width of item indicator including padding
        final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;

        if (progress == 0F) {
            // no swipe, draw a normal indicator
            float highlightStart = indicatorStartX + itemWidth * highlightPosition;

            c.drawCircle(highlightStart, indicatorPosY, mIndicatorItemLength / 2F, mPaint);

        } else {
            float highlightStart = indicatorStartX + itemWidth * highlightPosition;
            // calculate partial highlight
            float partialLength = mIndicatorItemLength * progress + mIndicatorItemPadding*progress;

            c.drawCircle(highlightStart + partialLength, indicatorPosY, mIndicatorItemLength / 2F, mPaint);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.bottom = mIndicatorHeight;
    }
}

Now back in MainActivity.java, add this to our RecyclerView

 recyclerView.addItemDecoration(new CirclePagerIndicatorDecoration());

We are done with the configuration of the RecyclerView.

Adapter

The last step is to add items and an adapter.

Create the item XML layout.

view_item_intro.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">

    <LinearLayout
        android:orientation="vertical"
        android:layout_marginLeft="32dp"
        android:layout_marginRight="32dp"
        android:layout_gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/imgview"
            android:layout_gravity="center_horizontal"
            android:adjustViewBounds="true"
            android:layout_width="180dp"
            android:layout_height="180dp" />

        <TextView
            android:layout_marginTop="32dp"
            android:layout_marginBottom="8dp"
            android:textStyle="bold"
            android:textSize="20sp"
            android:textColor="@android:color/black"
            android:textAllCaps="true"
            android:gravity="center"
            android:id="@+id/txttitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <TextView
            android:gravity="center"
            android:textSize="18sp"
            android:id="@+id/txtdesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

</LinearLayout>

Create a simple POJO for our item.

MySimpleObj.java

public class MySimpleObj {

    private String title;
    private String desc;
    private int pic;

    public MySimpleObj(String title, String desc, int pic) {
        this.title = title;
        this.desc = desc;
        this.pic = pic;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getPic() {
        return pic;
    }

    public void setPic(int pic) {
        this.pic = pic;
    }
}

Now create the adapter class.

IntroAdapter.java

public class IntroAdapter extends RecyclerView.Adapter<IntroAdapter.CustomViewHolder> {

    private List<String> items;
    private Context context;

    public IntroAdapter(Context context, List list) {
        this.items = list;
        this.context = context;
    }

    @Override
    public IntroAdapter.CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.view_item_intro, viewGroup,false);
        IntroAdapter.CustomViewHolder viewHolder = new IntroAdapter.CustomViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(IntroAdapter.CustomViewHolder holder, int i) {

        final MySimpleObj result = items.get(i);
        holder.imgview.setImageResource(result.getPic());
        holder.txttitle.setText(result.getTitle());
        holder.txtdesc.setText(result.getDesc());

    }

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

    class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        protected ImageView imgview;
        protected TextView txttitle,txtdesc;

        public CustomViewHolder(View view) {
            super(view);

            this.imgview = view.findViewById(R.id.imgview);
            this.txtdesc = view.findViewById(R.id.txtdesc);
            this.txttitle = view.findViewById(R.id.txttitle);

        }
    }
}

Finally, set the items!

ArrayList<MySimpleObj> items = new ArrayList<>();
items.add(new MySimpleObj("FAST DELIVERY","Lorem ipsum dolor sit amet, consectetur adipiscing elit.",R.drawable.intro1));
items.add(new MySimpleObj("EXCITING OFFERS","Lorem ipsum dolor sit amet, consectetur adipiscing elit",R.drawable.intro4));
items.add(new MySimpleObj("SECURE PAYMENT","Lorem ipsum dolor sit amet, consectetur adipiscing elit",R.drawable.intro5));
IntroAdapter adapter = new IntroAdapter(this,items);
recyclerView.setAdapter(adapter);

The complete code of the activity should look like this:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    Button btnlogin,btnsignup;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.act_intro);
        
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        PagerSnapHelper pagerSnapHelper = new PagerSnapHelper();
        pagerSnapHelper.attachToRecyclerView(recyclerView);
        recyclerView.addItemDecoration(new CirclePagerIndicatorDecoration());
        
        ArrayList<MySimpleObj> items = new ArrayList<>();
        items.add(new MySimpleObj("FAST DELIVERY","Lorem ipsum dolor sit amet, consectetur adipiscing elit.",R.drawable.intro1));
        items.add(new MySimpleObj("EXCITING OFFERS","Lorem ipsum dolor sit amet, consectetur adipiscing elit",R.drawable.intro4));
        items.add(new MySimpleObj("SECURE PAYMENT","Lorem ipsum dolor sit amet, consectetur adipiscing elit",R.drawable.intro5));
        IntroAdapter adapter = new IntroAdapter(this,items);
        recyclerView.setAdapter(adapter);

    }
}

Don’t forget to add images in the drawable folder!