App下载下来第一次进入时,一般情况都会有一个欢迎界面,滑动过几个界面后才会真正进入到应用中去。今天主要是利用ViewPager来制作欢迎界面。主要思路是:点击app时进入启动界面,然后判断是否第一次打开app,是则转到欢迎界面,否则直接转到应用主界面。换言之,我们重点关注的就是启动界面和欢迎界面。
一般而言,启动跟欢迎界面大部分是全屏显示的,所以我们需要通过setSystemUiVisibility来实现全屏的功能。下面我们一步一步来讲解如果使用ViewPager实现App欢迎界面。

启动界面

我们打开App的时候,有时看到有一个Logo显示的界面,比如打开微博或者赤兔时,会看到app先在Logo页停留一段时间,然后才会进入到主界面中,如果是第一次打开app时,还会先进入到一个欢迎界面,之后再跳转。看起来有点绕口,还是直接看代码吧。

// WelcomeActivity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
        imageView = findViewById(R.id.imgWelcome);

        // 下面的代码实现全屏
        imageView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LOW_PROFILE
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        );
        // 对显示的图片进行压缩处理,否则容易造成OOM问题
        bitmap = GuideActivity.readBitMap(this, R.drawable.img_welcome);
        ((ImageView)imageView).setImageBitmap(bitmap);
        // 本地记录是否第一次打开app
        SharedPreferences sharedPreferences = getSharedPreferences("config", Context.MODE_PRIVATE);
        isFirstIn = sharedPreferences.getBoolean("isFirstIn", true);
        if (isFirstIn){
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putBoolean("isFirstIn", false);
            editor.apply();
            // 第一次打开,跳转到欢迎界面
            // 这里利用handler来实现延迟跳转
            handler.sendEmptyMessageDelayed(GO_GUIDE, TRANSLATE_DELAY);
        } else {
            // 非第一次打开,跳转到主界面
            handler.sendEmptyMessageDelayed(GO_MAIN, TRANSLATE_DELAY);
        }
    }
    // 通过msg.what接受消息,判断跳转到那个页面
    // goMain跟goGuide两个函数很简单,就是利用intent和startActivity
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == GO_MAIN){
                goMain();
            } else {
                goGuide();
            }
        }
    };

启动界面到这里就完成了,调用setSystemUiVisibility实现全屏效果,其他都很简单。还有一点要注意的就是,为了全屏效果看起来不会太过生硬(启动白屏问题),我们需要设置一下主题,然后在AndroidManifest里面修改对应的theme属性。这里直接贴出style的代码

<style name="AppLoad" parent="AppTheme.NoActionBar">
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

接下我们看看欢迎界面,这也是本文的重点。主要是ViewPager的使用

欢迎界面

欢迎界面显示三张图片,滑动到最后一张图片后,点击按钮,进入主界面。滑动的功能通过ViewPager来实现,layout的代码这里就补贴出来了,比较简单,跟之前blog介绍过的很相似。滑动时底部显示的点可以通过shape来实现

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

下面是GuideActivity的主要代码

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_guide);
        textView = findViewById(R.id.textView);
        textView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LOW_PROFILE
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        initView();
        initDots();
    }

    public void initView(){
        viewPager = (ViewPager) findViewById(R.id.viewPager);
        // adapter里面是需要显示的内容
        viewPager.setAdapter(new MyViewPagerAdapter(this));
        // 滑动时触发
        viewPager.setOnPageChangeListener(this);
    }

    // 初始化点图案,在滑动的时候,底部显示点点,表明现在所处位置
    // 这里的点不是图片资源,而是通过shape生成的  
    public void initDots(){
        dots = new ImageView[ids.length];
        for (int i = 0; i < ids.length; i++){
            dots[i] = (ImageView) findViewById(ids[i]);
        }

    @Override
    public void onPageSelected(int position) {
        for (int i = 0; i < ids.length; i++){
            if (i == position){
                dots[i].setImageResource(R.drawable.dot_red);
            }else {
                dots[i].setImageResource(R.drawable.dot_white);
            }
        }
    }

MyViewPagerAdapter继承至PagerAdapter需要复写instantiateItemdestroyItemgetCountisViewFromObject 这几个方法,还要有一个构造函数

public MyViewPagerAdapter(Context context) {
        this.context = context;
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // 把布局文件加载rootView中
        LayoutInflater inflater = LayoutInflater.from(context);
        rootView = inflater.inflate(R.layout.three,container,false);
        Button button = (Button) rootView.findViewById(R.id.btnStart);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context, MainActivity.class);
                context.startActivity(intent);
            }
        });
        if (position == imgDrawableResource.length - 1){
            button.setVisibility(View.VISIBLE);
        } else {
            button.setVisibility(View.GONE);
        }
        imageView = (ImageView) rootView.
        findViewById(R.id.imgThree);
        bitmap = GuideActivity.readBitMap(context, imgDrawableResource[position]);
        imageView.setImageBitmap(bitmap);
        // 把rootView添加到主容器中,即GuideActivity中
        container.addView(rootView);
        return rootView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
        Bitmap bm = ((BitmapDrawable)imageView.getDrawable()).getBitmap();
        if (bm !=null && !bm.isRecycled()){
            bm.recycle();
            bm = null;
            Log.e("GuideActivity", "destroyItem");
        }
    }

    @Override
    public int getCount() {
        // 返回需要显示的图片的张数
        return imgDrawableResource.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        // 没有这个,图片无法显示
        return view == object;
    }

利用ViewPager简单地实现了欢迎界面,但还有很多地方可以改进,比如图片的加载、缓存,如何解决OOM问题,启动界面时,通过线程加载本地数据等等,这些以后碰到的时候再来讨论。