Bài này được dịch từ website androidhive.

Người dịch: Nguyễn Minh Tú - Thực tập sinh sau khoá học Android Techmaster

Chỉnh lý: Nguyễn Bá Thành - Giảng viên lớp Android Techmaster

Source code đã được đưa lên GitHub: https://github.com/thanhnb1101/IntroSlider

Việc thêm các màn hình Welcome/Intro vào trong ứng dụng của bạn là một cách tuyệt vời để giới thiệu những tính năng chính trong ứng dụng của bạn. Và việc đầu tiên bạn sẽ nghĩ ngay đến việc thêm màn hình Splash vào trong ứng của bạn. Tuy nhiên một màn hình Splash có lẽ là không đủ. Trong bài viết này tôi sẽ cùng các bạn xây dựng một sample nhỏ để thêm một Intro Slider tới ứng dụng của bạn nơi người dùng có thê swipe thông qua một vài slide để bắt đầu sử dụng app.

Trong bài viết này tôi sẽ tạo 1 ứng dụng nho nhỏ có chưa vài màn hình intro để người dùng có thể di chuyển qua mỗi màn hình intro với cử chỉ swipe hoặc sử dụng button next. Tôi cũng sẽ giải thích làm thế nào để hiển thị các Intro chỉ khi ứng dụng được chạy lần đầu tiên.

Dưới đây là hình ảnh ứng dụng chúng ta sẽ xây dựng.

Vậy chúng ta hãy bắt đầu tạo mới một project.

1.Tạo project mới

  • Tạo một Project mới trong Android Studio : File -> New Proejct Khi bạn được yêu cầu chọn Activity default thì hãy chọ Empty Activity và tiếp tục.
  • Download res.zip và thêm chúng vào trong project của bạn trong folder res. file zip này có chưa 1 vài drawable image dùng trong project của bạn.

2. Lựa chọn màu sắc

Trong thiết kế của tôi, mỗi màn hình intro sẽ chứa một image ở giữa màn hình, một vài textview bên dưới. Tại cuối của màn hình có một vài dấu chấm, chỉ ra số lượng màn hình intro.

Bên dưới là 1 mẫu,tôi đã lựa chọn để làm thiết kế project của mình.

  • Mở file colors.xml bên dưới res => value và thêm những giá trị màu bên dưới. Bạn có thể chú ý rằng ở đây tôi đã giữ chúng vào trong các array để tôi có thể dễ dàng load chúng trong Activity.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
 
    <!-- Screens background color-->
    <color name="bg_screen1">#f64c73</color>
    <color name="bg_screen2">#20d2bb</color>
    <color name="bg_screen3">#3395ff</color>
    <color name="bg_screen4">#c873f4</color>
 
    <!-- dots inactive colors -->
    <color name="dot_dark_screen1">#d1395c</color>
    <color name="dot_dark_screen2">#14a895</color>
    <color name="dot_dark_screen3">#2278d4</color>
    <color name="dot_dark_screen4">#a854d4</color>
 
    <!-- dots active colors -->
    <color name="dot_light_screen1">#f98da5</color>
    <color name="dot_light_screen2">#8cf9eb</color>
    <color name="dot_light_screen3">#93c6fd</color>
    <color name="dot_light_screen4">#e4b5fc</color>
 
    <array name="array_dot_active">
        <item>@color/dot_light_screen1</item>
        <item>@color/dot_light_screen2</item>
        <item>@color/dot_light_screen3</item>
        <item>@color/dot_light_screen4</item>
    </array>
 
    <array name="array_dot_inactive">
        <item>@color/dot_dark_screen1</item>
        <item>@color/dot_dark_screen2</item>
        <item>@color/dot_dark_screen3</item>
        <item>@color/dot_dark_screen4</item>
    </array>
</resources>
  • Mở strings.xml được đặt dưới res => values và thêm vào những giá trị như bên dưới. Ở đây tôi đang đề cập tới title và description cho mỗi màn hình intro.
<resources>
    <string name="app_name">Intro Slider</string>
    <string name="title_activity_welcome">Home Screen</string>
    <string name="next">NEXT</string>
    <string name="skip">SKIP</string>
    <string name="start">GOT IT</string>
 
    <string name="slide_1_title">Hello Food!</string>
    <string name="slide_1_desc">The easiest way to order food from your favourite restaurant!</string>
 
    <string name="slide_2_title">Movie Tickets</string>
    <string name="slide_2_desc">Book movie tickets for your family and friends!</string>
 
    <string name="slide_3_title">Great Discounts</string>
    <string name="slide_3_desc">Best discounts on every single service we offer!</string>
 
    <string name="slide_4_title">World Travel</string>
    <string name="slide_4_desc">Book tickets of any transportation and travel the world!</string>
 
    <string name="play_again_desc">To see the welcome slider again, goto Settings -> apps -> welcome slider -> clear data</string>
    <string name="play_again">Play Again</string>
</resources>
  • Mở file dimens.xml bên dưới res => values và thêm những giá trị như bên dưới:
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="dots_height">30dp</dimen>
    <dimen name="dots_margin_bottom">20dp</dimen>
    <dimen name="img_width_height">120dp</dimen>
    <dimen name="slide_title">30dp</dimen>
    <dimen name="slide_desc">16dp</dimen>
    <dimen name="desc_padding">40dp</dimen>
</resources>
  • Tiếp theo trong file styles.xml bạn thêm như bên dưới:
<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>
 
    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
 
    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
 
</resources>
  • Màn hình Welcome/Intro chỉ lên hiển thị duy nhất một lần khi bạn lần đầu tiên chạy ứng dụng, nếu người dùng khởi động app lần thứ 2 thì họ nên vào luôn màn hình Main của app. Để có được điều này tôi sử dụngSharedPreferences dể lưu trữ một giá trị boolean để chỉ ra lần đầu tiên người dụng khởi động app.
  • Tạo một class PrefManager.class với những thay đổi như bên dưới. isFirstTimeLaunch() trả về Truenếu app khởi động lần đầu tiên.
PrefManager.java

import android.content.Context;
import android.content.SharedPreferences;
 
public class PrefManager {
    SharedPreferences pref;
    SharedPreferences.Editor editor;
    Context _context;
 
    // shared pref mode
    int PRIVATE_MODE = 0;
 
    // Shared preferences file name
    private static final String PREF_NAME = "androidhive-welcome";
 
    private static final String IS_FIRST_TIME_LAUNCH = "IsFirstTimeLaunch";
 
    public PrefManager(Context context) {
        this._context = context;
        pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
        editor = pref.edit();
    }
 
    public void setFirstTimeLaunch(boolean isFirstTime) {
        editor.putBoolean(IS_FIRST_TIME_LAUNCH, isFirstTime);
        editor.commit();
    }
 
    public boolean isFirstTimeLaunch() {
        return pref.getBoolean(IS_FIRST_TIME_LAUNCH, true);
    }
 
}

3. Tạo các màn hình Intro

Bây giờ là lúc chúng ta sẽ tạo các mành hình Intro cho ứng dụng của chúng ta. Tôi sẽ tạo 4 màn hình Intro . Do đó chúng ta sẽ cần 4 file layout tương ứng cho mỗi màn hình. Layout của mỗi màn hình có bố cục giống nhau chỉ khác nhau về image, text và color. Vậy chúng ta sẽ tạo nhanh 4 file layout với tên tương ứng welcome_side1.xml, welcome_side2.xml, welcome_side3.xmlwelcome_side4.xml bên trong res layouts.

a. welcome_slide1.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_screen1">
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center_horizontal"
        android:orientation="vertical">
 
        <ImageView
            android:layout_width="@dimen/img_width_height"
            android:layout_height="@dimen/img_width_height"
            android:src="@drawable/ic_food" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/slide_1_title"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_title"
            android:textStyle="bold" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:paddingLeft="@dimen/desc_padding"
            android:paddingRight="@dimen/desc_padding"
            android:text="@string/slide_1_desc"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_desc" />
 
    </LinearLayout>
</RelativeLayout>

b. welcome_slide2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_screen2">
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center_horizontal"
        android:orientation="vertical">
 
        <ImageView
            android:layout_width="@dimen/img_width_height"
            android:layout_height="@dimen/img_width_height"
            android:src="@drawable/ic_movie" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/slide_2_title"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_title"
            android:textStyle="bold" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:paddingLeft="@dimen/desc_padding"
            android:paddingRight="@dimen/desc_padding"
            android:text="@string/slide_2_desc"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_desc" />
    </LinearLayout>
</RelativeLayout>

c. welcome_slide3.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_screen3">
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center_horizontal"
        android:orientation="vertical">
 
        <ImageView
            android:layout_width="@dimen/img_width_height"
            android:layout_height="@dimen/img_width_height"
            android:src="@drawable/ic_discount" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/slide_3_title"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_title"
            android:textStyle="bold" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:paddingLeft="@dimen/desc_padding"
            android:paddingRight="@dimen/desc_padding"
            android:text="@string/slide_3_desc"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_desc" />
 
    </LinearLayout>
 
</RelativeLayout>

d. welcome_slide4.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_screen4">
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center_horizontal"
        android:orientation="vertical">
 
        <ImageView
            android:layout_width="@dimen/img_width_height"
            android:layout_height="@dimen/img_width_height"
            android:src="@drawable/ic_travel" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/slide_4_title"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_title"
            android:textStyle="bold" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:paddingLeft="@dimen/desc_padding"
            android:paddingRight="@dimen/desc_padding"
            android:text="@string/slide_4_desc"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="@dimen/slide_desc" />
 
    </LinearLayout>
 
</RelativeLayout>
  • Một khi các file Layout đã sẵn sàng, tạo một Activity mới tên là WelcomeActivity.java
  • Mở activity_welcome.xml và chỉnh sửa code như bên dưới. Ở đây tôi thêm ViewPager cho màn hình intro,LinearLayout cho những dấu chấm(bottom dots) và 2 button Skip và Next.

activity_welcome.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:showIn="@layout/activity_welcome">
 
 
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
    <LinearLayout
        android:id="@+id/layoutDots"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dots_height"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="@dimen/dots_margin_bottom"
        android:gravity="center"
        android:orientation="horizontal"></LinearLayout>
 
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:alpha=".5"
        android:layout_above="@id/layoutDots"
        android:background="@android:color/white" />
 
    <Button
        android:id="@+id/btn_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:background="@null"
        android:text="@string/next"
        android:textColor="@android:color/white" />
 
    <Button
        android:id="@+id/btn_skip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:background="@null"
        android:text="@string/skip"
        android:textColor="@android:color/white" />
 
</RelativeLayout>

Mở WelcomeActivity.java và chỉnh sửa code như bên dưới. Ở đây chúng ta sẽ chú ý đển một số chi tiết.

  • Kiểm tra lần đầu tiên app khởi động bằng cách sử dụng phương thứcPrefManager.getInstance(this).isFirstTimeLaunch()
  • Làm notification bar thành transparent để màu background có thể nhìn thấy
  • Tạo IntroViewPagerAdapter cho ViewPager và inflated các layout chúng ta đã tạo trước đây.
  • Thêm sự kiện clickListenner cho các button Next và Skip. Nều Button Next được click thì màn hinh InTro tiếp tiếp theo sẽ được hiển thị, con Button Skip được click thì sẽ vào thẳng trong màn hình Main của APP.

WelcomeActivity.java

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
 
public class WelcomeActivity extends AppCompatActivity {
 
    private ViewPager viewPager;
    private MyViewPagerAdapter myViewPagerAdapter;
    private LinearLayout dotsLayout;
    private TextView[] dots;
    private int[] layouts;
    private Button btnSkip, btnNext;
    private PrefManager prefManager;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // Checking for first time launch - before calling setContentView()
        prefManager = new PrefManager(this);
        if (!prefManager.isFirstTimeLaunch()) {
            launchHomeScreen();
            finish();
        }
 
        // Making notification bar transparent
        if (Build.VERSION.SDK_INT >= 21) {
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        }
 
        setContentView(R.layout.activity_welcome);
 
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        dotsLayout = (LinearLayout) findViewById(R.id.layoutDots);
        btnSkip = (Button) findViewById(R.id.btn_skip);
        btnNext = (Button) findViewById(R.id.btn_next);
 
 
        // layouts of all welcome sliders
        // add few more layouts if you want
        layouts = new int[]{
                R.layout.welcome_slide1,
                R.layout.welcome_slide2,
                R.layout.welcome_slide3,
                R.layout.welcome_slide4};
 
        // adding bottom dots
        addBottomDots(0);
 
        // making notification bar transparent
        changeStatusBarColor();
 
        myViewPagerAdapter = new MyViewPagerAdapter();
        viewPager.setAdapter(myViewPagerAdapter);
        viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
 
        btnSkip.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                launchHomeScreen();
            }
        });
 
        btnNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // checking for last page
                // if last page home screen will be launched
                int current = getItem(+1);
                if (current < layouts.length) {
                    // move to next screen
                    viewPager.setCurrentItem(current);
                } else {
                    launchHomeScreen();
                }
            }
        });
    }
 
    private void addBottomDots(int currentPage) {
        dots = new TextView[layouts.length];
 
        int[] colorsActive = getResources().getIntArray(R.array.array_dot_active);
        int[] colorsInactive = getResources().getIntArray(R.array.array_dot_inactive);
 
        dotsLayout.removeAllViews();
        for (int i = 0; i < dots.length; i++) {
            dots[i] = new TextView(this);
            dots[i].setText(Html.fromHtml("&#8226;"));
            dots[i].setTextSize(35);
            dots[i].setTextColor(colorsInactive[currentPage]);
            dotsLayout.addView(dots[i]);
        }
 
        if (dots.length > 0)
            dots[currentPage].setTextColor(colorsActive[currentPage]);
    }
 
    private int getItem(int i) {
        return viewPager.getCurrentItem() + i;
    }
 
    private void launchHomeScreen() {
        prefManager.setFirstTimeLaunch(false);
        startActivity(new Intent(WelcomeActivity.this, MainActivity.class));
        finish();
    }
 
    //  viewpager change listener
    ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
 
        @Override
        public void onPageSelected(int position) {
            addBottomDots(position);
 
            // changing the next button text 'NEXT' / 'GOT IT'
            if (position == layouts.length - 1) {
                // last page. make button text to GOT IT
                btnNext.setText(getString(R.string.start));
                btnSkip.setVisibility(View.GONE);
            } else {
                // still pages are left
                btnNext.setText(getString(R.string.next));
                btnSkip.setVisibility(View.VISIBLE);
            }
        }
 
        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
 
        }
 
        @Override
        public void onPageScrollStateChanged(int arg0) {
 
        }
    };
 
    /**
     * Making notification bar transparent
     */
    private void changeStatusBarColor() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        }
    }
 
    /**
     * View pager adapter
     */
    public class MyViewPagerAdapter extends PagerAdapter {
        private LayoutInflater layoutInflater;
 
        public MyViewPagerAdapter() {
        }
 
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
            View view = layoutInflater.inflate(layouts[position], container, false);
            container.addView(view);
 
            return view;
        }
 
        @Override
        public int getCount() {
            return layouts.length;
        }
 
        @Override
        public boolean isViewFromObject(View view, Object obj) {
            return view == obj;
        }
 
 
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            View view = (View) object;
            container.removeView(view);
        }
    }
}
    • Cuối cùng mởi file AndroidManifest.xml và làm WelcomeActivity.java như là launcher Activity như bên dưới
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="info.androidhive.introslider">
     
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name="info.androidhive.introslider.WelcomeActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
     
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name="info.androidhive.introslider.MainActivity"
                android:label="@string/title_activity_welcome"
                android:theme="@style/AppTheme.NoActionBar"></activity>
        </application>
     
    </manifest>

     Bây giờ bạn chạy ứng dụng và xem thành quả mình đã làm.