Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
📱 Android App (Kotlin + XML)  ·  Lesson 15

Splash और Onboarding Screens

पहली बार vs बाद में खोलना

SplashDataStore पढ़ेंOnboarding?सिर्फ पहली बारLogged in?token saved?Homeया Login screen
MainActivity launcher है; वही तय करके आगे भेजती है
9:41▮▮ 85%🛒KartEasy9:41▮▮ 85%Skip🖼️shopping bag illustrationEverything you needMobiles, fashion, electronics,books and more in one simple app.━ • •Next9:41▮▮ 85%🖼️delivery truck illustrationFast doorstep deliveryPay cash on delivery and trackevery order.• • ━Get started
Splash → onboarding page 1 → page 3 (आख़िरी page पर "Skip" छुप जाता है)

Step 1: SplashScreen API

core-splashscreen library Android 6 से हर version पर Android 12 वाली splash देती है। Launcher activity एक special splash theme से शुरू होती है; splash के बाद normal app theme अपने आप लग जाती है।

XML – splash theme + manifest
<!-- res/values/themes.xml (splash part) -->
<style name="Theme.KartEasy.Splash" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">@color/brand</item>
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
    <item name="postSplashScreenTheme">@style/Theme.KartEasy</item>
</style>

<!-- AndroidManifest.xml -->
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:theme="@style/Theme.KartEasy.Splash"
    android:windowSoftInputMode="adjustResize">
    <!-- intent-filter MAIN / LAUNCHER … -->
</activity>

Step 2: कहाँ जाना है तय करें

setKeepOnScreenCondition { !ready } splash को तब तक दिखाता है जब तक coroutine DataStore से token, onboarding flag और theme पढ़ती है। फिर हम या तो दूसरी activity खोलते हैं (और इसे finish() करते हैं, ताकि Back यहाँ न लौटे) या main UI बनाते हैं।

Kotlin – MainActivity.kt (शुरुआत)
// MainActivity.kt – the start of onCreate (full file in the Drawer & Bottom Nav lesson)
override fun onCreate(savedInstanceState: Bundle?) {
    // 1) Splash screen stays until we know where to go (onboarding / login / home).
    val splash = installSplashScreen()
    splash.setKeepOnScreenCondition { !ready }
    super.onCreate(savedInstanceState)

    lifecycleScope.launch {
        val app = kartApp
        app.session.load()
        applyDarkMode(app.session.darkMode())
        when {
            !app.session.isOnboardingDone() -> openAndFinish(OnboardingActivity::class.java)
            !app.session.isLoggedIn -> openAndFinish(AuthActivity::class.java)
            else -> setupUi()
        }
        ready = true
    }
}

private fun openAndFinish(target: Class<*>) {
    startActivity(Intent(this, target))
    finish()
}
Kotlin – SessionManager.kt (onboarding वाला हिस्सा)
// SessionManager.kt (onboarding part)
private object Keys {
    val ONBOARDING_DONE = booleanPreferencesKey("onboarding_done")
    // …
}

suspend fun isOnboardingDone(): Boolean = context.dataStore.data.first()[Keys.ONBOARDING_DONE] == true

suspend fun setOnboardingDone() {
    context.dataStore.edit { it[Keys.ONBOARDING_DONE] = true }
}

Step 3: App icon और splash icon

App icon एक adaptive icon है: background और foreground layer जिन्हें हर launcher circle, squircle या square में काट सकता है। दोनों layers vectors हैं, इसलिए कोई PNG export नहीं करनी।

XML – mipmap-anydpi-v26/ic_launcher.xml
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
XML – drawable/ic_launcher_foreground.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <group android:scaleX="2" android:scaleY="2" android:translateX="30" android:translateY="30">
        <path android:fillColor="#FFFFFFFF" android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49C21.25,4.16 20.78,3.5 20.1,3.5H5.21l-0.94,-2H1zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
    </group>
    <path android:fillColor="#F97316" android:pathData="M72,34m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0" />
</vector>

Splash icon 288dp canvas पर है; Android बीच का 192dp circle दिखाता है, इसलिए हम white circle के अंदर brand-colour cart बनाते हैं:

XML – drawable/ic_splash.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Splash icon: the cart in the middle of a 288dp canvas (the system shows a 192dp circle) -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="288dp"
    android:height="288dp"
    android:viewportWidth="288"
    android:viewportHeight="288">
    <path android:fillColor="#FFFFFFFF" android:pathData="M144,144m-64,0a64,64 0,1 1,128 0a64,64 0,1 1,-128 0" />
    <group android:scaleX="3.4" android:scaleY="3.4" android:translateX="103.2" android:translateY="103.2">
        <path android:fillColor="#4F46E5" android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49C21.25,4.16 20.78,3.5 20.1,3.5H5.21l-0.94,-2H1zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
    </group>
</vector>

Step 4: Onboarding layouts

ViewPager2 pages रखता है, TabLayout सिर्फ dots के लिए है, और एक button आख़िरी page पर Next से Get started बन जाता है।

XML – layout/activity_onboarding.xml
<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/surface"
    android:padding="24dp">

    <com.google.android.material.button.MaterialButton
        android:id="@+id/skip"
        style="@style/Widget.Material3.Button.TextButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/skip"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/dots"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/skip" />

    <!-- TabLayout used only as page dots -->
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/dots"
        android:layout_width="wrap_content"
        android:layout_height="24dp"
        android:layout_marginBottom="24dp"
        android:background="@android:color/transparent"
        app:layout_constraintBottom_toTopOf="@id/next"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:tabBackground="@drawable/tab_dot"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp"
        app:tabMaxWidth="18dp"
        app:tabMinWidth="18dp"
        app:tabPaddingEnd="0dp"
        app:tabPaddingStart="0dp" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/next"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/next"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
XML – layout/item_onboarding.xml (एक page)
<?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"
    android:padding="8dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="260dp"
        android:layout_height="240dp"
        android:importantForAccessibility="no" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:gravity="center"
        android:textAppearance="?attr/textAppearanceHeadlineSmall"
        android:textColor="@color/text_primary"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:gravity="center"
        android:lineSpacingExtra="4dp"
        android:textAppearance="?attr/textAppearanceBodyLarge"
        android:textColor="@color/text_secondary" />
</LinearLayout>

Dot एक selector है: selected होने पर लंबी brand-colour pill, वरना छोटा grey dot।

XML – drawable/tab_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Page dot: long brand pill when selected, small grey dot otherwise -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item android:gravity="center" android:width="16dp" android:height="6dp">
                <shape android:shape="rectangle">
                    <solid android:color="@color/brand" />
                    <corners android:radius="3dp" />
                </shape>
            </item>
        </layer-list>
    </item>
    <item>
        <layer-list>
            <item android:gravity="center" android:width="6dp" android:height="6dp">
                <shape android:shape="oval">
                    <solid android:color="@color/divider" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

Step 5: OnboardingActivity

Pages data की एक छोटी list हैं। एक छोटा inner adapter हर page bind करता है; TabLayoutMediator dots को pager से जोड़ता है। User finish (या skip) करे तो हम flag save करके login screen खोलते हैं।

Kotlin – ui/onboarding/OnboardingActivity.kt
package com.codingeasily.karteasy.ui.onboarding

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.codingeasily.karteasy.R
import com.codingeasily.karteasy.databinding.ActivityOnboardingBinding
import com.codingeasily.karteasy.databinding.ItemOnboardingBinding
import com.codingeasily.karteasy.ui.auth.AuthActivity
import com.codingeasily.karteasy.util.kartApp
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch

/** Three intro slides shown only on the first launch. */
class OnboardingActivity : AppCompatActivity() {

    private data class Page(val image: Int, val title: Int, val text: Int)

    private val pages = listOf(
        Page(R.drawable.onboard_shop, R.string.onboard_title_1, R.string.onboard_text_1),
        Page(R.drawable.onboard_deals, R.string.onboard_title_2, R.string.onboard_text_2),
        Page(R.drawable.onboard_delivery, R.string.onboard_title_3, R.string.onboard_text_3)
    )

    private lateinit var binding: ActivityOnboardingBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityOnboardingBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.pager.adapter = PageAdapter()
        TabLayoutMediator(binding.dots, binding.pager) { _, _ -> }.attach()

        binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                val last = position == pages.lastIndex
                binding.next.setText(if (last) R.string.get_started else R.string.next)
                binding.skip.visibility = if (last) android.view.View.INVISIBLE else android.view.View.VISIBLE
            }
        })

        binding.next.setOnClickListener {
            val current = binding.pager.currentItem
            if (current < pages.lastIndex) binding.pager.currentItem = current + 1 else finishOnboarding()
        }
        binding.skip.setOnClickListener { finishOnboarding() }
    }

    private fun finishOnboarding() {
        lifecycleScope.launch {
            kartApp.session.setOnboardingDone()
            startActivity(Intent(this@OnboardingActivity, AuthActivity::class.java))
            finish()
        }
    }

    private inner class PageAdapter : RecyclerView.Adapter<PageAdapter.Holder>() {
        inner class Holder(val b: ItemOnboardingBinding) : RecyclerView.ViewHolder(b.root)

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
            Holder(ItemOnboardingBinding.inflate(LayoutInflater.from(parent.context), parent, false))

        override fun getItemCount() = pages.size

        override fun onBindViewHolder(holder: Holder, position: Int) {
            val p = pages[position]
            holder.b.image.setImageResource(p.image)
            holder.b.title.setText(p.title)
            holder.b.text.setText(p.text)
        }
    }
}

Step 6: Vector illustrations

तीनों illustrations vector drawables हैं: एक soft circle, छोटे decoration dots और 5× बड़ा Material icon। हर एक लगभग 1 KB का है और हर screen पर sharp दिखता है।

XML – drawable/onboard_deals.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="260dp"
    android:height="240dp"
    android:viewportWidth="260"
    android:viewportHeight="240">
    <path android:fillColor="@color/brand_light" android:pathData="M130,120m-100,0a100,100 0,1 1,200 0a100,100 0,1 1,-200 0" />
    <path android:fillColor="#4F46E5" android:pathData="M40,40m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" />
    <path android:fillColor="#F97316" android:pathData="M222,62m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0" />
    <path android:fillColor="#4F46E5" android:pathData="M214,196m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0" />
    <group android:scaleX="5" android:scaleY="5" android:translateX="70" android:translateY="60">
        <path android:fillColor="#F97316" android:pathData="M21.41,11.58l-9,-9C12.05,2.22 11.55,2 11,2H4c-1.1,0 -2,0.9 -2,2v7c0,0.55 0.22,1.05 0.59,1.42l9,9c0.36,0.36 0.86,0.58 1.41,0.58 0.55,0 1.05,-0.22 1.41,-0.59l7,-7c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-0.55 -0.23,-1.06 -0.59,-1.42zM5.5,7C4.67,7 4,6.33 4,5.5S4.67,4 5.5,4 7,4.67 7,5.5 6.33,7 5.5,7z" />
    </group>
</vector>

Test करें

  1. 1Fresh install → splash → onboarding page 1
  2. 2Swipe करें और Next दबाएँ → page 3 पर "Get started" और Skip गायब
  3. 3Get started → Login screen
  4. 4बंद करके फिर खोलें → onboarding वापस नहीं आता
  5. 5Login करें, बंद करें, फिर खोलें → सीधे Home (login screen नहीं)
First-launch checklist

अक्सर पूछे जाने वाले प्रश्न (FAQ)

3 second delay वाली SplashActivity क्यों नहीं?
Android 12+ हमेशा system splash दिखाता है, इसलिए दूसरी splash screen होने पर users को दो दिखती हैं। Fixed delay समय भी बर्बाद करता है। SplashScreen API एक ही splash उतनी देर दिखाती है जितनी DataStore पढ़ने में लगे — आमतौर पर कुछ milliseconds।
Testing के लिए onboarding दोबारा कैसे दिखाएँ?
App icon long-press → App info → Storage → Clear storage। DataStore साफ़ हो जाता है, app fresh install जैसी चलती है।
← E-commerce App Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicLogin और Register Screens आपकी प्रगति: 15 / 28 (54%) · Lesson 16 / 28 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें