App引导界面,可以这么玩

时间:2021-05-07 15:13:07

什么是ViewPager,刚一听到这个词,我们可能感觉很奇怪,但是我相信我们大部分人都曾见到过这些界面的。其实它就是我们在安装好一个app之后第一次使用时的那些引导界面的效果。这就是通过ViewPager来完成滴。今天,就让我们一起走进ViewPager的世界吧。


理论基础


  • 其实说是理论基础,也只不过是一些小知识点罢了,所以不要紧张咯。ViewPager在ADT开发时使用到了android.support.v4.view.ViewPager控件,我们需要知道这点就可以了。
  • 我们可以把ViewPager看作是一个ListView样式的控件,然后按照对待ListView的眼光来看待ViewPager,就会很轻松了。
  • ListView需要一个适配器,而ViewPager同样也是如此;ListView需要注册Item的事件侦听,ViewPager同样如此。至于底层的细节我们稍后再说。

了解了上面的这些,相信我们心中都有了一个大致的了解了吧。那我们就正式开始咯。

ViewPager怎么使用?


首先我们需要在XML文件中进行声明,注意我们引用的是android.support.v4.view.ViewPager控件哦,如下:

 <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:background="#00000000"
        >
</android.support.v4.view.ViewPager>

然后是在Java代码中findViewById一下,免得出现NullPointerExcetion.

了解到了ViewPager是一个View的容器,那么我们当然要有View才行了,所以我们在layout文件夹下创建几个用于呈现的view。由于比较相似,我这里就仅仅写出一个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" >

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/guide_3" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/start_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="进入" />
    </LinearLayout>
    <!--由于这是第三个布局,也就是引导的最后一个,我们提供了一个进入主界面的按钮-->
</RelativeLayout>

现在万事俱备,只欠东风了。所以我们就需要创建一个适配器了,这里需要注意的是适配器要继承自PagerAdapter ,具体的代码如下:

package com.mark.viewpagerdemo;

import java.util.List;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;

/**
 * 这个类的作用就是对于ViewPager的Activity(View)的切换的
 * 时候需要的一个适配器的作用,其实ViewPager的原理就可以看作是一个ListView就行了
 * @author lhdn
 *
 */
public class ViewPageAdapter extends PagerAdapter{

    //list是为了存储容器中的view,context的作用就是上下文
    private List<View> views;
    private Context context;

    public ViewPageAdapter(List<View > mViews , Context mContext) {
        this.context = mContext;
        this.views = mViews;
    }

    /**
     * 将不需要呈现的view及时的销毁
     */
    @Override
    public void destroyItem(View container, int position, Object object) {
        ((ViewPager) container).removeView(views.get(position));
    }

    /**
     * 该方法的作用类似于ListViewAdapter的那个getView的作用
     */
    @Override
    public Object instantiateItem(View container, int position) {
        ((ViewPager) container).addView(views.get(position));
        return views.get(position);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return views.size();
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        // 判断当前获得的view是不是我们想要得到的view
        return (arg0 == arg1);
    }

}

其中方法:

@Override
    public Object instantiateItem(View container, int position) {
        ((ViewPager) container).addView(views.get(position));
        return views.get(position);
    }

就好比我们的ListViewAdapter的getView方法。功能就是获得一个view,即可。

如果不需要在图片上添加效果的话,这样其实就已经是搞定了,我们可以测试一下效果,发现这个小案例已经是可行的了。


ViewPager添加一些效果


所谓添加效果,就是添加上几个小圆点,比如我们滑动View的时候下面会出现的一些小亮点和小暗点等,其目的是为了显示我们确实滑动了ViewPager,也为了美观嘛。那么我们要怎么实现呢?答案就是使用ImageView就可以了,但是切记只需要在ViewPager所在的界面进行添加就可以了,而不需要为其他的XML文件进行添加。然后我们在ViewPager的OnPageChangeListener处理事件中进行相关的设置就可以了。如下:

<?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.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:background="#00000000"
        >
    </android.support.v4.view.ViewPager>

    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+id/iv1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/login_point_selected" />

        <ImageView
            android:id="@+id/iv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/login_point" />

        <ImageView
            android:id="@+id/iv3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/login_point" />
    </LinearLayout>

</RelativeLayout>
package com.mark.viewpagerdemo;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class Guide extends Activity implements OnPageChangeListener {

    private ViewPager vp;
    // 因为viewpager的使用要街注意一个适配器,所以要创建一个适配器的对象
    private ViewPageAdapter vpAdapter;
    // 创建一些视图View,用于放置到我们的ViewPager容器中
    private List<View> views;
    // 存储图像的小点
    private ImageView[] dots;
    // 图片小点的ID值
    int[] ids = new int[] { R.id.iv1, R.id.iv2, R.id.iv3 };

    private Button enter ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.guide);
        initViews();
        initDots();
    }

    /**
     * 初始化ViewPager机器内部的view视图
     */
    public void initViews() {
        views = new ArrayList<View>();
        LayoutInflater inflater = LayoutInflater.from(this);
        // 向ViewPager中添加view
        views.add(inflater.inflate(R.layout.one, null));
        views.add(inflater.inflate(R.layout.two, null));
        views.add(inflater.inflate(R.layout.three, null));

        // 为进入主界面的按钮申请资源,由于button的值在第三个view中,而这里是Guide的
        // 所以,要先得到第三个view之后,才能使用findViewById()方法
        enter = (Button) views.get(2).findViewById(R.id.start_btn);
        enter.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent intent = new Intent(Guide.this, MainActivity.class);
                startActivity(intent);
                finish();
            }
        });

        // 创建ViewPager以及添加适配器
        vp = (ViewPager) findViewById(R.id.viewpager);
        vpAdapter = new ViewPageAdapter(views, this);
        vp.setAdapter(vpAdapter);
        // 为ViewPager 注册监听事件
        vp.setOnPageChangeListener(this);

    }

    /**
     * 初始化需要的点的资源
     */
    public void initDots() {
        dots = new ImageView[views.size()];
        for (int i = 0; i < dots.length; i++) {
            dots[i] = (ImageView) findViewById(ids[i]);
        }
    }

    // ///////////////////////////////////////////onViewPagerChangeListener接口的方法开始

    @Override
    public void onPageScrollStateChanged(int arg0) {
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
    }

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

    }
    // ///////////////////////////////////////////onViewPagerChangeListener接口方法结束

}

测试后发现,成了!这样就已经是完整的实现了我们需要的功能了。


深入的思考


虽然我们已经完成了ViewPager的使用了,但是仔细的想一想,我们真的完成了吗?

其实并不是的,因为这样的话,我们每一次打开这个APP,就会出现一个引导界面,这样是不符合事实的,因为我们并不希望每一次都看到这个引导界面,所以我们现在要完成的就是如何实现只在第一次的时候才显示引导界面。

实现的思路:使用一个变量,放置到文件的存储中,每次打开APP的时候检验这个值,如果是第一次使用,就跳转到引导界面,如果不是,就跳转到主界面,所以我们现在就要实现这个中间层的界面了。实现的效果就是打开APP的时候显示这个中间层的界面,然后在跳转到其他的引导界面或者主界面。


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

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/welcome_android"
        />
</LinearLayout>
package com.mark.viewpagerdemo;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Handler;

public class Welcome extends Activity {

    //设置一个延迟时间,为界面的跳转来争取时间
    private static final int TIME = 2000;
    private static final int GO_HOME = 1000;
    private static final int GO_GUIDE = 1001;

    //设置一个参数,实现只为第一次进入的时候显示guide界面,这个参数要存储进本地的一个参数,
    //见下面的initValue方法
    private boolean isFirstIn = true;
    private void initValue(){
        SharedPreferences sp = getSharedPreferences("ViewPagerDemo",MODE_PRIVATE);
        isFirstIn = sp.getBoolean("isFirstIn", true);
        if(isFirstIn) {
            mHandler.sendEmptyMessageDelayed(GO_GUIDE,TIME);
            Editor editor = sp.edit();
            editor.putBoolean("isFirstIn", false);
            editor.commit();
        }else{
            mHandler.sendEmptyMessageDelayed(GO_HOME,TIME);
        }
    }

    //等待时间不能在主进程,所以使用handler
    private Handler mHandler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch(msg.what){
            case GO_HOME:
                goHome();
                break;
            case GO_GUIDE:
                goGuide();
                break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        setContentView(R.layout.welcome);
        initValue();
    }

    private void goHome(){
        Intent i = new Intent(Welcome.this,MainActivity.class);
        startActivity(i);
        finish();
    }
    private void goGuide(){
        Intent i = new Intent(Welcome.this,Guide.class);
        startActivity(i);
        finish();
    }

}

Welcome.java 深入剖析:


由于我们设置了在欢迎界面的等待时间,所以就最好是使用handler来实现界面的更新,这样符合“在主线程更新UI”原则。于是使用了SharedPreferences来实现了值得记录。

优化思路,其实我觉得在initValue方法中初始化的方式也不是特别的好,如果在静态代码中进行的话,效果会更好。

handler的使用是至关重要的一个知识点,我们要记得在主线程中进行UI界面的更新,所以要在主线程中对其他线程传递过来的,msg.what进行处理。


测试结果


点击运行完程序之后,我们会发现只有在第一次使用APP的时候才会出现引导界面,一旦我们进入过了主界面,引导界面就不会再出现了。除非你重新安装了,(^__^) 嘻嘻……


知识点回顾


这个小项目的主要的流程是这样的,

* 先是了解了什么是ViewPager,

* 然后学会了ViewPagerAdapter适配器的使用,

* 再然后就是对PageChange事件的处理

* 学会了如何使用handler配合UI的更新

* 再就是添加了常量值的配合实现了“一次性计划”

* 接下来就真的完成了,虽然界面不好看,但是核心的思想却是都在这里了。

项目源代码下载地址