Android中实现页面滑动——ViewPager

ViewPager可以实现多页面滑动切换以及动画效果,在很多开发场合都十分常用,不仅方便实用而且功能强大。ViewPager类直接继承了ViewGroup类,所以它是一个容器类,可以在其中添加其他的view类。
ViewPager是Google SDK自带的附加包v4包的类(即android.support-v4.jar),所以在写布局文件时需要引用完整类名。经验表明这样一个View必然要用到适配器,Android为它定制了专属的适配器PagerAdapter。
ViewPager主要特点是可通过触屏滑动来切换界面,也可以通过标签点击切换界面,在滑动的过程中指示位置的头标或下标也会跟着滑动,而不是直接闪过去。当然,这么细致全面的功能需要复杂完善的代码作为后台,每一个细节都有一个逻辑。ViewAdapter虽然看起来复杂,但套路都是固定的。以下做两个简单的实现,是两种比较常见的模式,一个是App更新后的新手指引界面,一个是App中通过点击和滑动切换的界面。
可以通过setCurrentItem()方法设置显示第几个界面(从0开始)。

其实做这样的东西难点并不在于如何填充数据,而是在于作为标签的小灰点或者小蓝条的动画过程,ViewPager提供了界面切换的动画,但是标签的动画仍需要程序员写代码实现。

第一个实例,布局如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.briup.viewpager.MainActivity" >
    <!-- Viewpager:全类名导入 -->
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="50dp" >
		<!-- 灰点所在的布局,假设所要显示的界面数量不确定,所以灰点需要在Java代码中绘制 -->
        <LinearLayout
            android:id="@+id/ll_item"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
        </LinearLayout>
		<!-- 蓝点:由shape绘制的图片,将灰点的布局和蓝点放在同一个相对布局中,这样后面加入的蓝点就回覆盖灰点,达到想要的效果 -->
        <ImageView
            android:id="@+id/blue_iv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/blue_point" />
    </RelativeLayout>
	<!-- 开始体验按钮,首先它并不是在每个界面都显示的所以它的默认值应设为不可见 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="100dp"
        android:background="@drawable/btn_shape"
        android:text="开始体验"
        android:textColor="#00ffff"
        android:visibility="gone" />

</RelativeLayout>
蓝点shape文件blue_point.xml如下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="oval">
    <size android:width="7dp"
        android:height="7dp"/>
	<solid android:color="#18a1ff"/>
</shape>
灰点shape文件grey_point.xml如下,两个其实一样,只是填充颜色不同
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="oval">
    <size android:width="7dp"
        android:height="7dp"/>
	<solid android:color="#c3c3c3"/>
</shape>
开始体验按钮shape文件btn_shape.xml如下
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <corners android:radius="20dp"/>
	<solid android:color="#ffffff"/>
	<stroke android:width="2dp"
	    android:color="#00ffff"/>
	<padding android:left="10dp"
	    android:top="5dp"
	    android:right="10dp"
	    android:bottom="5dp"/>
</shape>
MainActivity.class类文件如下
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.Toast;

public class MainActivity extends Activity {
	private ViewPager viewPager;
	private LinearLayout ll_item;//灰点所在的线性布局
	private ImageView blue_iv;//小蓝点
	private Button btn;
	int position;//当前界面数(从0开始)
	private int pointWidth;//小灰点的距离
	private int[] images = {R.drawable.p1,R.drawable.p2,R.drawable.p3};//新手导航一般都是图片做界面
	private List<ImageView> list;//存放图片控件的List集合
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);//设置App不显示菜单,黑乎乎的不好看
		//注意这两句代码的顺序,上面一句写在下面一句后面会报错
		setContentView(R.layout.activity_main);
		viewPager = (ViewPager) findViewById(R.id.viewpager);
		list = new ArrayList<ImageView>();
		ll_item = (LinearLayout) findViewById(R.id.ll_item);
		blue_iv = (ImageView) findViewById(R.id.blue_iv);
		btn = (Button) findViewById(R.id.btn);
		//开始按钮做一个简单的测试,前面两个界面开始按钮是不可见的,同时也是点不到的
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
			}
		});
		//将图片的引用转化为图片控件存在List的集合中
		for(int i=0;i<images.length;i++){
			ImageView imageView = new ImageView(this);
			imageView.setImageResource(images[i]);//将相应的图片设置到IamageView
			imageView.setScaleType(ScaleType.FIT_XY);//设置图片的拉伸方式为充满
			list.add(imageView);
			//绘制小灰点儿,有几个界面就绘制几个
			ImageView points = new ImageView(this);
			points.setImageResource(R.drawable.grey_point);//通过shape文件绘制好灰点
			//给第一个以外的小灰点儿设置左边距,保证三个灰点水平居中
			LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(
					LinearLayout.LayoutParams.WRAP_CONTENT,
					LinearLayout.LayoutParams.WRAP_CONTENT);//拿到灰点所处的线性布局一样的形状(一些距离属性)
			if(i>0)
				lllp.leftMargin = 30;//设置左外边距,像素
			points.setLayoutParams(lllp);//把设置好左外边距的形状设置给灰点
			ll_item.addView(points);//将灰点加入线性布局
		}
		//为了完成蓝点在界面滑动时的动画效果,必须获取到灰点的边距,通过动态的给蓝点设置边距来完成动画效果
		//由于在执行onCreate方法时,界面还没有绘制完成,无法获取pointWidth,设定小蓝点绘制完成的事件监听,当小蓝点绘制完成再获取
		blue_iv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			@Override
			public void onGlobalLayout() {
				//获取小灰点圆心间的距离,第1个灰点和第二个灰点的距离
				pointWidth = ll_item.getChildAt(1).getLeft()-ll_item.getChildAt(0).getLeft();
			}
		});
		
		VPAdapter vpAdapter = new VPAdapter();//创建适配器
		viewPager.setAdapter(vpAdapter);//ViewPager加载适配器
		//为ViewPager设定监听器,界面是滑动时让蓝点也跟着动
		viewPager.addOnPageChangeListener(new OnPageChangeListener() {
			@Override
			//当前选中第几个界面
			public void onPageSelected(int arg0) {
				position = arg0;
			}
			/**
			 * 界面滑动时回调此方法
			 * arg0:当前界面数
			 * arg1:界面滑动过的百分数(0.0-1.0)
			 * arg2:当前界面偏移的像素位置
			 */
			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
				int width;//小蓝点当前滑动距离
				width = (int) (arg1*pointWidth+arg0*pointWidth);//1个界面就要一个小灰点的距离,再加上滑动过的百分比距离就是当前蓝点的位置
				RelativeLayout.LayoutParams rllp= (LayoutParams) blue_iv.getLayoutParams();//拿到蓝点所在布局的形状
				rllp.leftMargin=width;//设置蓝点的左外边距
				blue_iv.setLayoutParams(rllp);//将设置好的形状设置给蓝点
				//开始体验按钮只能出现在最后一页,并且在滑动的过程中保持消失,这样效果更好,不信可以把后面的判断删去,在最后一页回移的时候,按钮先会跟着移动,然后突然就不见了
				if(position==images.length-1&&arg1==0)
					btn.setVisibility(View.VISIBLE);
				else
					btn.setVisibility(View.INVISIBLE);
			}
			//状态改变时调用:arg0=0还没滑动,arg0=1正在滑动,arg0=2滑动完毕
			@Override
			public void onPageScrollStateChanged(int arg0) {
			}
		});
	}
	
	class VPAdapter extends PagerAdapter{
		//返回ViewPager中总页数
		@Override
		public int getCount() {
			return images.length;
		}
		//判断视图是否由对象生成
		@Override
		public boolean isViewFromObject(View view, Object object) {
			return view==object;
		}
		@Override
		/**
		 * 返回将哪一个对象放在当前ViewPager中
		 * container:每一页的父容器
		 * position:当前页(从0开始)
		 */
		public Object instantiateItem(ViewGroup container, int position) {
			//浪费资源,每次滑到新的页都会创建新的的ImageView,我们选择先把ImageView控件存在List集合中,再按需要获取
//			ImageView imageView = new ImageView(MainActivity.this);
//			imageView.setImageResource(images[position]);
			ImageView imageView = list.get(position);
			container.addView(imageView);
			return imageView;
		}
		@Override
		/**
		 * 从ViewPager中移除View对象
		 */
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView((View) object);
		}
	}
	
}
效果如下

滑动时,蓝点也跟随移动


按钮点击测试


第二个实例,布局文件如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <RelativeLayout
        android:id="@+id/rl_title_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
		<!-- 标签卡:也可以使用TextView,由于没有制作shape,这里又用了白色背景,按钮点击没有效果,但是蓝条可以告诉你点击成功了 -->
        <LinearLayout
            android:id="@+id/ll_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/btn1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="#ffffff"
                android:text="吸血鬼"
                android:textSize="20sp" />

            <Button
                android:id="@+id/btn2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="#ffffff"
                android:text="木乃伊"
                android:textSize="20sp" />

            <Button
                android:id="@+id/btn3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="#ffffff"
                android:text="雪怪"
                android:textSize="20sp" />
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/rl_progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/ll_title" >
		<!-- 标签和可切换界面的分界,标志位置的蓝条通过Java代码加入,相对布局,直接加入就回覆盖分界 -->
            <View
                android:layout_width="match_parent"
                android:layout_height="10dp"
                android:background="#eeeeee" />
        </RelativeLayout>
    </RelativeLayout>
	<!-- Viewpager:全类名导入 -->
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/rl_title_top" />

</RelativeLayout>
蓝条shape文件progress.xml如下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="#0000ff"/>
	<corners android:radius="10dp"/>
</shape>
MainActivity.class类文件如下
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity {
	private ViewPager vp;
	private Button btn1,btn2,btn3;
	private RelativeLayout rl_progress;//分隔标签和界面的分界布局,把蓝条加入这个布局就能达到想要的效果
	private VPAdapter vpa;
	private int[] items = {R.drawable.a49,R.drawable.a53,R.drawable.a57};//这里用简单的图片代替界面
	private List<ImageView> list;
	
	private TextView view;//因为蓝条的宽度需要通过代码获取屏幕宽度来计算,所以蓝条在Java代码中绘制
						  //蓝条使用一个TextView绘制,其他View也可以
	private int width;//蓝条的宽度,或者说屏幕宽度n等分的长度
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		vp = (ViewPager) findViewById(R.id.viewpager);
		btn1 = (Button) findViewById(R.id.btn1);
		btn2 = (Button) findViewById(R.id.btn2);
		btn3 = (Button) findViewById(R.id.btn3);
		rl_progress = (RelativeLayout) findViewById(R.id.rl_progress);
		list = new ArrayList<ImageView>();
		view = new TextView(this);
		//获取屏幕宽度的方法
		DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		width = metrics.widthPixels/items.length;//由于我这里只有3个界面,所以我只要设置蓝条宽度为屏幕宽/3(metrics.widthPixels/3)
		view.setWidth(width);
		view.setHeight(25);//多次尝试才设置成正好的高度,shape文件中设置和布局文件相同的高度(10dp)却要大一些
		view.setBackground(getResources().getDrawable(R.drawable.progress));//shape文件中设置了蓝条为蓝色背景、圆角
		rl_progress.addView(view);//把蓝条加入到相对布局
		
		for(int i=0;i<items.length;i++){
			ImageView iv = new ImageView(this);
			iv.setImageResource(items[i]);
			iv.setScaleType(ScaleType.FIT_XY);
			list.add(iv);
		}
		
		vpa = new VPAdapter();
		vp.setAdapter(vpa);
		
		vp.addOnPageChangeListener(new OnPageChangeListener() {
			@Override
			public void onPageSelected(int arg0) {
			}
			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
				int left = width*arg0 + (int) (width*arg1);//蓝条滑动的距离
				RelativeLayout.LayoutParams rllp = (LayoutParams) view.getLayoutParams();
				rllp.leftMargin = left;
				view.setLayoutParams(rllp);
			}
			@Override
			public void onPageScrollStateChanged(int arg0) {
			}
		});
		//标签按钮的监听事件:点到哪个就切换到那一个界面
		btn1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				vp.setCurrentItem(0);
			}
		});
		btn2.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				vp.setCurrentItem(1);
			}
		});
		btn3.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				vp.setCurrentItem(2);
			}
		});
	}
	class VPAdapter extends PagerAdapter{
		@Override
		public int getCount() {
			return items.length;
		}
		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0==arg1;
		}
		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			ImageView iv = list.get(position);
			container.addView(iv);
			return iv;
		}
		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView((View) object);
		}
	}
}
效果如下


滑动时,蓝条跟着移动


  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值