ViewPager是android扩展包android.support.v4 里的一个继承与ViewGroup组件,通过布局管理器可以实现左右滑动来显示不同的View。而这个View由PagerAdapter产生,用法类似于ListView和listView的Adapter。
下面是一个简单例子(布局文件):
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true">
<android.support.v4.view.PagerTabStrip
android:id="@+id/ptab"
android:layout_width="wrap_content"
android:gravity="center"
android:layout_height="50dp" >
android.support.v4.view.PagerTabStrip>
android.support.v4.view.ViewPager>
PagerTabStrip是ViewPager切换的标题栏,不需要可以不写。现在示例中暂时写上看效果。
前面说了像listview一样,我们还需要一个adapter。ok,那么我们来看看PagerAdapter怎么写。
public class MyPagerAdapter extends PagerAdapter{
private Context context;
private int[] imgid = new int[]{
R.drawable.h_1,
R.drawable.h_2,
R.drawable.h_3,
R.drawable.h_4
};
public MyPagerAdapter(Context context) {
this.context = context;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return imgid.length;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// 判断是否由对象产生页面
return arg0==arg1;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//初始化postiton位置的界面
ImageView imageView = new ImageView(context);
imageView.setImageResource(imgid[position]);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// 生命周期中的资源回收,系统消耗position时会调用
container.removeView((View)object);
Drawable draw = ((ImageView)object).getDrawable();
if(draw!=null){
//解除Drawable 对view 的引用
draw.setCallback(null);
}
}
}
可以看到我们只需要继承与PagerAdapter并实现和重写上面几个必要的方法即可。代码中有注释,不一一解释了。
activity中呢,很简单,给ViewPager set一下我们的PagerAdapter就行了。
public class MainActivity extends Activity {
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new MyPagerAdapter(this));
}
}
来看看效果。
这样滑动切换界面就实现了,里面的ImageView可以替换成任何View.
大家可以看到PagerTabStrip是一个黑色的条,跟随滚动,这个可以去掉,也可以定义成我们想要的样子。
首先我们可以直接在PagerAdapter中设置要显示的标题,只要再重写一个PagerAdapter的方法:
public CharSequence getPageTitle(int position) {
// TODO Auto-generated method stub
return titls[position];
}
PagerTabStrip的样式也是可以定义的,下面给出一些常用的方法,根据需要定义。
//设置下划线的颜色,有两种方法,使用其中一种即可
pagerTabStrip.setTabIndicatorColor(Color.RED);//拉姆红
pagerTabStrip.setTabIndicatorColorResource(R.color.blue);//雷姆蓝(自定义的color资源)
//设置背景颜色
pagerTabStrip.setBackgroundColor(Color.CYAN);
ps:若出现初始加载时不显示标签文字,滑动后却显示。可以尝试换android_support_v4包。(换成实例源码中的可以,可能是某些版本的v4包有bug)
其实PagerTabStrip并没什么卵用,实际项目很少用到,因为它不符合中国的审美观。我们看到的大多数app的标题栏都是全部显示出来的。就像这样。
那么这效果怎么实现呢,这就需要自定义标题栏。
我的标题栏应该像这样的,我们让左边的那块东西随着当前item不一样,进行滑动就行了。
布局应该是这样的。
<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">
<LinearLayout
android:id="@+id/tile_ll"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:background="@color/blue">
<TextView
android:id="@+id/title1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="标题1"/>
<TextView
android:id="@+id/title2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="标题2"/>
<TextView
android:id="@+id/title3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="标题3"/>
<TextView
android:id="@+id/title4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="标题4"/>
LinearLayout>
<ImageView
android:id="@+id/cursor"
android:layout_marginTop="33dp"
android:layout_width="wrap_content"
android:layout_height="3dp"
android:src="@drawable/tile_line"/>
<android.support.v4.view.ViewPager
android:layout_below="@id/tile_ll"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android.support.v4.view.ViewPager>
RelativeLayout>
我们使用了相对布局,对标题使用线性布局,而滑块使用imageView盖上上面。这比较简单,下面就是滑块的动画实现了。
/**
* 滑块动画
* @param fromIndex 当前标签
* @param toIndex 目标标签
*/
public void startAnimation(int fromIndex,int toIndex){
int bitmapWidth = cursor.getWidth();
int tileWidth = tvTile[0].getWidth();
int offset = (tileWidth - bitmapWidth)/2;
Animation animation = new TranslateAnimation(fromIndex*tileWidth+offset, toIndex*tileWidth+offset, 0,0);
animation.setFillAfter(true);
animation.setDuration(200);
cursor.startAnimation(animation);/*
}
上面是动画的方法,具体计算位置的方法是:1.先计算出滑块和标签两边的offset.2.移动时就是index*tileWidth+offset 可以计算出目标位置。看图理解:
动画完成了,那么就需要监听viewPager的itemchange事件了。
如下:
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int pos) {
startAnimation(currentPage, pos);
currentPage = pos;
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
});
}
主要是实现了onPageSelected(int pos)方法。进行启动动画。
还差点什么吗?有,就是标题栏点击时能切换页面了。这是简单,给每个标题view添加点击监听器即可。
for (int i = 0; i < tvTile.length; i++) {
tvTile[i].setTag(i);
tvTile[i].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int item = (Integer) v.getTag();
viewPager.setCurrentItem(item);
}
});
}
做到这里其实还有一个bug,那就是初始状态没写。我发现在oncreate,onresume等获取控件的大小都是0的。因此初始化时,需要重写如下方法才能正确获取大小:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
bitmapWidth = cursor.getWidth();
tileWidth = tvTile[0].getWidth();
offset = (tileWidth - bitmapWidth)/2;
startAnimation(0, 0);
}
自定义的标题导航栏就到此为止了。
实现原理和上面类似,只是相当于将标题换成了小圆点而已,再增加自动轮播。
在res文件夹下新建文件夹drawable.并新建如下文件
白色小圆点(可用图片代替)
route_point_draw.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:useLevel="false"
android:shape="oval">
<solid android:color="#FFFFFF"/>
<stroke android:width="1dp"
android:color="@android:color/white"/>
<size android:width="7dp"
android:height="7dp"/>
shape>
白色半透明小圆点(可用图片代替)
route_point_draw_b.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:useLevel="false"
android:shape="oval">
<solid android:color="#55FFFFFF"/>
<stroke android:width="1dp"
android:color="@android:color/white"/>
<size android:width="7dp"
android:height="7dp"/>
shape>
小圆点selector
route_point.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/route_point_draw" android:state_selected="true"/>
<item android:drawable="@drawable/route_point_draw_b"/>
selector>
slide_view.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black" >
<android.support.v4.view.ViewPager
android:id="@+id/slipe_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal"
android:orientation="horizontal">
<View android:id="@+id/t_1"
android:layout_width="8dp"
android:layout_height="8dp"
android:background="@drawable/route_point" />
<View android:id="@+id/t_2"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="5dp"
android:background="@drawable/route_point" />
<View android:id="@+id/t_3"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="5dp"
android:background="@drawable/route_point" />
<View android:id="@+id/t_4"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="5dp"
android:background="@drawable/route_point" />
LinearLayout>
RelativeLayout>
这样我们的界面就有了。
剩的就是自动轮播了。
pagerAdapter的实现和上面一样,这部分就不重复了。
ublic class SlideShowView extends FrameLayout implements OnPageChangeListener{
private int SLIDE_PIC_NUM = 4;
private long SLIDE_TIME = 500;
private int[] pic_id;
private List dotViews;
private ViewPager viewPager;
private int currentPager;
private boolean isAutoPlay = false;
private ScheduledExecutorService scheduledExe;
private Handler hander = new Handler(){
public void handleMessage(android.os.Message msg) {
viewPager.setCurrentItem(currentPager);
};
};
/**
* @param context
* @param attrs
* @param defStyleAttr
*/
public SlideShowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
initview(context);
startPlay();
}
public SlideShowView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SlideShowView(Context context) {
this(context,null);
}
/**
* 4个点
* @param reid
*/
public void setImageIds(int ... reid){
pic_id = reid;
viewPager.invalidate();
}
private void init(){
dotViews = new ArrayList();
pic_id = new int[]{
R.drawable.h_1,
R.drawable.h_2,
R.drawable.h_3,
R.drawable.h_4
};
}
private void initview(Context context){
LayoutInflater.from(context).inflate(R.layout.slide_view,this,true);
for (int i = 0; i < pic_id.length; i++) {
/*ImageView view = new ImageView(context);
view.setImageResource(pic_id[i]);
images.add(view);*/
}
dotViews.add(findViewById(R.id.t_1));
dotViews.add(findViewById(R.id.t_2));
dotViews.add(findViewById(R.id.t_3));
dotViews.add(findViewById(R.id.t_4));
for (View dotview : dotViews) {
dotview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.t_1:
currentPager = 0;
break;
case R.id.t_2:
currentPager = 1;
break;
case R.id.t_3:
currentPager = 2;
break;
case R.id.t_4:
currentPager = 3;
break;
default:
break;
}
viewPager.setCurrentItem(currentPager);
}
});
}
viewPager = (ViewPager) findViewById(R.id.slipe_pager);
viewPager.setAdapter(new MypagerAdapter());
viewPager.setOnPageChangeListener(this);
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int position) {
currentPager = position;
for (int i = 0; i < dotViews.size(); i++) {
if(position==i){
dotViews.get(i).setSelected(true);
}else{
dotViews.get(i).setSelected(false);
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
case ViewPager.SCROLL_STATE_DRAGGING://手势滑动
isAutoPlay = false;
break;
case ViewPager.SCROLL_STATE_SETTLING://界面切换
isAutoPlay = true;
break;
case ViewPager.SCROLL_STATE_IDLE://切换结束
//最后一张右滑动到第一张
if(viewPager.getCurrentItem() == viewPager.getAdapter().getCount()-1 && !isAutoPlay){
viewPager.setCurrentItem(0);
}
if(viewPager.getCurrentItem() == 0 && !isAutoPlay){
viewPager.setCurrentItem(viewPager.getAdapter().getCount()-1);
}
break;
default:
break;
}
}
public void startPlay(){
scheduledExe = Executors.newSingleThreadScheduledExecutor();
scheduledExe.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// 轮播任务
synchronized (SlideShowView.this) {
currentPager = (currentPager+1)%pic_id.length;
hander.obtainMessage().sendToTarget();
}
}
}, 1, 4,TimeUnit.SECONDS);
}
public void stopPlay(){
scheduledExe.shutdown();
}
}
最终效果
这里的核心是使用了单一线程池循环发送handler信息,让viewPager循环播放图片。而小圆点的状态变化是通过selected状态实现,对于小圆点selector 的xml文件
<com.yufem.view.SlideShowView
android:layout_width="match_parent"
android:layout_height="400dp"
/>
这里的轮播时间,图片数量,来源都写死了,要想修改直接将源码修改即可。还有就是点击图片跳转的功能,只需给每个item加入点击事件即可,仿照上面自定义标题实现的点击。
有些软件滑动方式的有点不同,它的标题滑块是跟随着手指滑动而滑动的,就像下图一样。
思路:
既然要跟随页面滑动而滑动,那么就需要监听滑动事件,并计算出相应的距离。页面滑动一页,而导航栏的标题滑动一小格。假设是4个标签卡,那么就是4:1。
下面来实现一下,大体和上面的标题滑动差不多,我们之间复制进行修改。把动画部分去掉。
实现方法
/*pos是当前页面的标号,percent是当前页面滑动的百分比,px是当前页面滑动的像素*/
@Override
public void onPageScrolled(int pos, float percent, int px) {
if(scrolling){
float w = pos*tileWidth+offset+percent*tileWidth;//计算滑块的x坐标
cursor.setX(w);//设置滑块的x坐标
}
}
@Override
public void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
switch (state) {
case ViewPager.SCROLL_STATE_DRAGGING://手势滑动
scrolling = true;
break;
case ViewPager.SCROLL_STATE_SETTLING://界面切换
break;
case ViewPager.SCROLL_STATE_IDLE://切换结束
scrolling = false;
break;
default:
break;
}
}
实现OnPageChangeListener中的onPageScrolled方法即可。
初始化就是cursor.setX(offset);
具体查看源码。
全部源码下载:http://download.csdn.net/detail/u013565368/9660964
2016/10/22 11:47:11