ViewPager 从入门到带你撸个启动页之实战启动页(三)

转载请注明出处(万分感谢!):
http://blog.csdn.net/javazejian/article/details/52144889

关联文章:

ViewPager 从入门到带你撸个启动页之ViewPager基础入门(一)

ViewPager 从入门到带你撸个启动页之Fragment+ViewPager(二)

ViewPager 从入门到带你撸个启动页之实战启动页(三)

ViewPager 从入门到带你撸个启动页之实战PageTransformer切换动画特效(四)

一、启动页实现概述

  通过前两篇的分享,我们已经基本掌握了ViewPager的基本使用,本篇咱们就来实现一个通用的启动页,先来看看启动页效果:

  看来效果还不错,但实际上只用了ViewPager,并没有用到Fragment,因为移动就是3张图片而已,所以PagerAdapter的实现类代码也是相当简单的。唯一比较棘手的是页面移动过程中底部的3个小点也必须跟着移动,这点需要配合ViewPager的滑动监听动态实时小圆点的移动位置。下面我们就来分析它的实现过程吧。

二、启动页实现过程

2.1 实现整体滑动效果

  首先我们先来实现整体的滑动效果,也就是添加3张图片作为ViewPager的滑动页面,因此我们的activity_guide.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.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </android.support.v4.view.ViewPager>

</RelativeLayout>

接下来我们创建GuideActivity.java:

package com.zejian.commonstartpage;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;

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

/**
 * Created by zejian
 * Time 16/8/7.
 * Description:
 */
public class GuideActivity extends Activity{
    // ViewPager的数据
    private List<ImageView> imageViewList;
    ViewPager mViewPager;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 去标题, 需要在setContentView方法之前调用
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_guide);
        initView();// 初始化控件
    }

    /**
     * 初始化控件
     */
    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.vp_guide);
        GuideAdapter adapter = new GuideAdapter();
        mViewPager.setAdapter(adapter);
}

    /**
     * TODO:初始化ViewPager数据 void
     */
    private void initData() {
        int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
                R.drawable.guide_3};
        imageViewList = new ArrayList<>();

        ImageView iv;// 图片

        for (int i = 0; i < imageResIDs.length; i++) {
            iv = new ImageView(this);
            iv.setBackgroundResource(imageResIDs[i]);
            imageViewList.add(iv);
        }
    }



    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return imageViewList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        /*
         * 删除元素
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView iv = imageViewList.get(position);
            container.addView(iv);// 1. 向ViewPager中添加一个view对象
            return iv; // 2. 返回当前添加的view对象
        }
    }
}

  我们简单说一下代码,首先我们通过initData方法去创建我们所需需要的界面,因为我们的界面都是图片,所以这里我们直接new出3个ImageView控件,并设置图片即可,最后我们把其添加到imageViewList数据集合中,到此数据初始化完成:

 /**
* 初始化ViewPager数据 
*/
private void initData() {
   int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
           R.drawable.guide_3};
   imageViewList = new ArrayList<>();

   ImageView iv;// 图片

   for (int i = 0; i < imageResIDs.length; i++) {
       iv = new ImageView(this);
       iv.setBackgroundResource(imageResIDs[i]);
       imageViewList.add(iv);
   }
}

  接着我们创建GuideAdapter数据适配器继承自PagerAdapter,重写其方法,返回我们所需要的界面,这个前两篇已经说得很清楚了,这里不过多分析了。最后我们把数据适配器GuideAdapter设置给ViewPager即可:

mViewPager = (ViewPager) findViewById(R.id.vp_guide);
GuideAdapter adapter = new GuideAdapter();
mViewPager.setAdapter(adapter);

到此启动页初步实现完成,我们运行一下,看看效果。

不错!预期效果已经开始出现。接下来我们先把最后一页的按钮添加上。

2.2 添加按钮

  需要添加按钮的话我们就必须修改activity_guide.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.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </android.support.v4.view.ViewPager>

    <Button
        android:id="@+id/btn_guide_start_experience"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dip"
        android:background="@drawable/button_bg"
        android:textColor="@drawable/button_textcolor"
        android:text="开始体验"
        android:textSize="20sp" 
        android:paddingLeft="20dip"
        android:paddingRight="20dip"
        android:paddingTop="5dip"
        android:paddingBottom="5dip"
        android:visibility="gone"
        />
</RelativeLayout>

  显然我们添加了一个Button,并将其设置在父布局的底部而且水平居中,注意此时按钮先设置为gone,待需要显示时再设置其可见。然后我们在Activity_Guide.java中添加如下代码:

btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience)
//设置监听器
btnStartExperience.setOnClickListener(this);

  设置完按钮的点击事件后,我们还需要解决一个问题,那就是当需要显示Button时再显示出来,很显然,我们只需在第3页时才需要显示按钮,这时我们就需要借助ViewPager的滑动事件来处理了,因此我们实现ViewPager.OnPageChangeListener接口,并实现其方法如下:

    /**
     * 当页面正在滚动时 position 当前选中的是哪个页面 positionOffset 比例 positionOffsetPixels 偏移像素
     */
    @Override
    public void onPageScrolled(int position, float positionOffset,
                               int positionOffsetPixels) {
    }

    /**
     * 当页面被选中
     */
    @Override
    public void onPageSelected(int position) {

    }

    /**
     * 当页面滚动状态改变
     */
    @Override
    public void onPageScrollStateChanged(int state) {

    }

简单介绍一下三个方法:

  • onPageScrolled(int position, float positionOffset,
    int positionOffsetPixels)

    当页面正在滚动时被回调。
    position 表示当前选中的是第几个页面;
    positionOffset 是当前页面滑动比例,其值得范围是[0,1),如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。
    positionOffsetPixels 是当前页面滑动像素的偏移距离

  • onPageScrollStateChanged(int state)
    当页面滚动状态改变时被回调。有三个值:0(END),1(PRESS) , 2(UP) 。
    当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指擡起时,如果发生了滑动(即使很小),其值会变为2,最后变为0 。总共执行这个方法三次。当当前页面翻页时,会执行这个方法两次,state值分别为2 , 0 。

  • onPageSelected(int position)
    当页面被选中时回调,position表示被选中的页面,position下标从0开始计算。

  很显然,我们应该利用onPageSelected(int position)决定实现我们的Button是否展示,当position为最后一个页面时展示,其他时候都隐藏即可。代码如下:

 /**
 * 当页面被选中
 */
@Override
public void onPageSelected(int position) {
    // 显示体验按钮
    if (position == imageViewList.size() - 1) {
        // 显示
        btnStartExperience.setVisibility(View.VISIBLE);
    } else {
        btnStartExperience.setVisibility(View.GONE);// 隐藏
    }
}

到此按钮也集成完,我们看看整体GuideActivity代码:


package com.zejian.commonstartpage;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

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

/**
 * Created by zejian
 * Time 16/8/7.
 * Description:
 */
public class GuideActivity extends Activity implements ViewPager.OnPageChangeListener, View.OnClickListener {
    // ViewPager的数据
    private List<ImageView> imageViewList;
    // 开始体验按钮
    private Button btnStartExperience;
    // ViewPager
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 去标题, 需要在setContentView方法之前调用
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_guide);
        initView();// 初始化控件
    }

    /**
     * 初始化控件
     */
    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.vp_guide);
        btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience);
        initData();// 初始化ViewPager数据
        GuideAdapter adapter = new GuideAdapter();
        mViewPager.setAdapter(adapter);
        mViewPager.setOnPageChangeListener(this);// 设置监听器
        btnStartExperience.setOnClickListener(this);// 按钮添加监听
    }

    /**
     * TODO:初始化ViewPager数据 void
     */
    private void initData() {
        int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
                R.drawable.guide_3};
        imageViewList = new ArrayList<>();

        ImageView iv;// 图片

        for (int i = 0; i < imageResIDs.length; i++) {
            iv = new ImageView(this);
            iv.setBackgroundResource(imageResIDs[i]);
            imageViewList.add(iv);
        }
    }


    /**
     * 当页面正在滚动时 position 当前选中的是哪个页面 positionOffset 比例 positionOffsetPixels 偏移像素
     */
    @Override
    public void onPageScrolled(int position, float positionOffset,
                               int positionOffsetPixels) {
    }

    /**
     * 当页面被选中
     */
    @Override
    public void onPageSelected(int position) {
        // 显示体验按钮
        if (position == imageViewList.size() - 1) {
            btnStartExperience.setVisibility(View.VISIBLE);// 显示
        } else {
            btnStartExperience.setVisibility(View.GONE);// 隐藏
        }
    }

    /**
     * 当页面滚动状态改变
     */
    @Override
    public void onPageScrollStateChanged(int state) {

    }

    /**
     * 打开新的界面
     */
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "跳转新界面", Toast.LENGTH_SHORT).show();
    }


    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return imageViewList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        /*
         * 删除元素
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView iv = imageViewList.get(position);
            container.addView(iv);// 1. 向ViewPager中添加一个view对象
            return iv; // 2. 返回当前添加的view对象
        }
    }
}

我们运行看看其效果:

2.3 添加指示器

  终于到最后一步了,也是比较麻烦的一步,为每一个页面添加指示器,而且指示器必须跟随页面的移动而移动。那就先从布局开始吧,我们在activity_guide.xml文件最下方添加如下代码:

<RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dip">

        <LinearLayout
            android:id="@+id/ll_guide_point_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"></LinearLayout>

        <View
            android:id="@+id/select_point"
            android:layout_width="10px"
            android:layout_height="10px"
            android:background="@drawable/point_select" />
</RelativeLayout>

  我们在布局中添加了一个相对布局,其中包含了一个id为ll_guide_point_group的LinearLayout,这个线性布局主要用于存放指示器列表也就是3个灰色圆点,而另外一个id为select_point的View则是表示一个被选中的红色圆点。接下来我们就先来初始化这些点列表指示器的数据,因为灰色圆点的数量跟启动页面的数量是一致的,所以有多少个页面就有多少个灰色圆点,因此我们只需在生成图片时同时生成灰色圆点即可,每个点的宽高都为10,相隔距离也为10,代码如下:

private void initData() {
        int[] imageResIDs = { R.drawable.guide_1, R.drawable.guide_2,
                R.drawable.guide_3 };
        imageViewList = new ArrayList<ImageView>();

        ImageView iv;// 图片
        View view;// 点
        LayoutParams params; // 参数类

        for (int i = 0; i < imageResIDs.length; i++) {
            iv = new ImageView(this);
            iv.setBackgroundResource(imageResIDs[i]);
            imageViewList.add(iv);
            // 根据图片的个数, 每循环一次向LinearLayout中添加一个点
            view = new View(this);
            view.setBackgroundResource(R.drawable.point_normal);
            // 设置参数
            params = new LayoutParams(10, 10);
            if (i != 0) {
                params.leftMargin = 10;
            }
            view.setLayoutParams(params);// 添加参数
            llPointGroup.addView(view);
        }
    }

  从代码我们可以看出,根据图片的个数, 每循环一次向LinearLayout(llPointGroup)中添加一个点,最后便可以生成与图片数量相同的灰色圆点。现在我们来运行一下,看看点的效果:

  嗯,小圆点已经出现,接下来就是如何移动的问题了。现在我们需要实现的效果是这样的,当我们滑动页面时,红色小圆点也跟着移动,例如当页面滑动到屏幕的一半时,红色小圆点也应处于灰色圆点的中间位置。如下图:

  因此,我们去动态计算页面滑动的比例,然后按相同比例去计算红色小圆点需要滑动的距离。比如当第1页往第0页滑动到中间时,此时小红点的滑动距离应该如下计算:

  我们假设屏幕宽度为320px,那么当第1页滑动到屏幕中间时,其滑动距离就是160,此时滑动比较就是160/320=0.5,也就是说红色小圆点的滑动比例也是0.5,通过这个滑动比例我们就可以计算出两个点间的滑动距离,我们可以通过以下方式计算出小红点要滑动的距离:
pWidth=llPointGroup.getChildAt(1).getLeft()-llPointGroup.getChildAt(0).getLeft()
  最后乘以滑动比例就是小红点的滑动距离了。当然这只是其中一次滑动的完整计算过程,页面滑动过程中的小红点变化的计算方式也是相同(因为滑动过程中会多次调用onPageScrolled方法后面会说明),现在剩下的问题是我们如何知道页面滑动过程呢?还记得前面我们ViewPager设置的OnPageChangeListener监听器吧?其中有个回调方法onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的参数positionOffset不就是页面的滑动比例吗?确实如此,因此我们可以利用这个滑动比例来动态计算小红点的滑动距离,这样也就可以达到页面滑动的过程中小红点也跟随移动的效果。下面我们给出实现代码:

/**
* 当页面正在滚动时 position 当前选中的是哪个页面 positionOffset 比例 positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset,
                          int positionOffsetPixels) {
   //获取两个点间的距离,获取一次即可
   if(pWidth==0) {
       pWidth = llPointGroup.getChildAt(1).getLeft()
               - llPointGroup.getChildAt(0).getLeft();
   }

   // 获取点要移动的距离
   int leftMargin = (int) (pWidth * (position + positionOffset));
   // 给红点设置参数
   RelativeLayout.LayoutParams params = (android.widget.RelativeLayout.LayoutParams) mSelectPointView
           .getLayoutParams();
   params.leftMargin = leftMargin;
   mSelectPointView.setLayoutParams(params);
}

这里我们再来简单分析一下计算移动距离的代码:

// 获取点要移动的距离
int leftMargin = (int) (pWidth * (position + positionOffset));

  由于position表示当前页面的下标,取值范围在[0,2],而positionOffset则表示当前界面的滑动比例,取值范围在[0,1),当页面向右滑动,positionOffset值不断变大,最后在趋近1的情况后突变为0。如果页面向左滑动,这个值不断变小,最后变为0,当positionOffset突变为0时则说明页面已处于展示位置。因此当我们把当前页面往左滑动时,当前position=0,而positionOffset则在不断的增大,我们过Log来观察一下

当第0页滑动到左侧,而第1页正好在中间展示时,position=1,而positionOffset=0;

  因此我们可以通过positionOffset的值来计算小红点的滑动距离,当当前页面往左滑动时,positionOffset不断增大,此时由于position=0;所以pWidth * (position + positionOffset)也在不断增大,计算出小红点的移动距离leftMargin后,我们只需动态设置其params.leftMargin参数即可,这也就达到了小红点跟随移动的目的(onPageScrolled函数在移动中多次被触发),最终position=1,positionOffset=0,小红点也刚好落在了第2个灰色小圆点上。往后第3个点原理也是一样的,向右移动也依次类推即可。。
最后我们给出GuideActivity的代码:

package com.zejian.commonstartpage;


import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.Toast;

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

/**
 * Created by zejian
 * Time 16/8/7.
 * Description:
 */
public class GuideActivity extends Activity implements ViewPager.OnPageChangeListener, View.OnClickListener {
    // ViewPager的数据
    private List<ImageView> imageViewList;
    // 点的组
    private LinearLayout llPointGroup;
    // 选中的点view对象
    private View mSelectPointView;
    // 开始体验按钮
    private Button btnStartExperience;
    // ViewPager
    ViewPager mViewPager;
    // 点之间的宽度
    private int pWidth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 去标题, 需要在setContentView方法之前调用
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_guide);
        initView();// 初始化控件
    }

    /**
     * 初始化控件
     */
    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.vp_guide);
        btnStartExperience = (Button) findViewById(R.id.btn_guide_start_experience);
        llPointGroup = (LinearLayout) findViewById(R.id.ll_guide_point_group);
        mSelectPointView = findViewById(R.id.select_point);
        initData();// 初始化ViewPager数据
        GuideAdapter adapter = new GuideAdapter();
        mViewPager.setAdapter(adapter);
        mViewPager.setOnPageChangeListener(this);// 设置监听器
        btnStartExperience.setOnClickListener(this);// 按钮添加监听
    }

    /**
     * TODO:初始化ViewPager数据 void
     */
    private void initData() {
        int[] imageResIDs = {R.drawable.guide_1, R.drawable.guide_2,
                R.drawable.guide_3};
        imageViewList = new ArrayList<>();

        ImageView iv;// 图片
        View view;// 点
        LayoutParams params; // 参数类

        for (int i = 0; i < imageResIDs.length; i++) {
            iv = new ImageView(this);
            iv.setBackgroundResource(imageResIDs[i]);
            imageViewList.add(iv);
            // 根据图片的个数, 每循环一次向LinearLayout中添加一个点
            view = new View(this);
            view.setBackgroundResource(R.drawable.point_normal);
            // 设置参数
            params = new LayoutParams(10, 10);
            if (i != 0) {
                params.leftMargin = 10;
            }
            view.setLayoutParams(params);// 添加参数
            llPointGroup.addView(view);
        }
    }


    /**
     * 当页面正在滚动时 position 当前选中的是哪个页面 positionOffset 比例 positionOffsetPixels 偏移像素
     */
    @Override
    public void onPageScrolled(int position, float positionOffset,
                               int positionOffsetPixels) {

        Log.e("zejian","positionOffset:-->"+positionOffset);
        Log.e("zejian","position:-->"+position);
        //获取两个点间的距离,获取一次即可
        if(pWidth==0) {
            pWidth = llPointGroup.getChildAt(1).getLeft()
                    - llPointGroup.getChildAt(0).getLeft();
        }

        // 获取点要移动的距离
        int leftMargin = (int) (pWidth * (position + positionOffset));
        // 给红点设置参数
        RelativeLayout.LayoutParams params = (android.widget.RelativeLayout.LayoutParams) mSelectPointView
                .getLayoutParams();
        params.leftMargin = leftMargin;
        mSelectPointView.setLayoutParams(params);
    }

    /**
     * 当页面被选中
     */
    @Override
    public void onPageSelected(int position) {
        // 显示体验按钮
        if (position == imageViewList.size() - 1) {
            btnStartExperience.setVisibility(View.VISIBLE);// 显示
        } else {
            btnStartExperience.setVisibility(View.GONE);// 隐藏
        }
    }

    /**
     * 当页面滚动状态改变
     */
    @Override
    public void onPageScrollStateChanged(int state) {

    }

    /**
     * 打开新的界面
     */
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "跳转新界面", Toast.LENGTH_SHORT).show();
    }


    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return imageViewList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        /*
         * 删除元素
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView iv = imageViewList.get(position);
            container.addView(iv);// 1. 向ViewPager中添加一个view对象
            return iv; // 2. 返回当前添加的view对象
        }
    }
}

最近效果:

到此,启动页已全部完成,告本篇一段落。源码下载:
启动页(commonstartpage项目)源码GitHub下载地址

ViewPager 从入门到带你撸个启动页之ViewPager基础入门(一)

ViewPager 从入门到带你撸个启动页之Fragment+ViewPager(二)

ViewPager 从入门到带你撸个启动页之实战启动页(三)

ViewPager 从入门到带你撸个启动页之实战PageTransformer切换动画特效(四)

发布了65 篇原创文章 · 获赞 3504 · 访问量 230万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章