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