android界面开发:ViewPager的详解,并用于仿微博滑动实例和图片滚动实例

1.ViewPager简单使用

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.

2.PagerTabStrip使用

大家可以看到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)

3.类似微博的头部滑动效果

其实PagerTabStrip并没什么卵用,实际项目很少用到,因为它不符合中国的审美观。我们看到的大多数app的标题栏都是全部显示出来的。就像这样。

那么这效果怎么实现呢,这就需要自定义标题栏。

android界面开发:ViewPager的详解,并用于仿微博滑动实例和图片滚动实例_第1张图片

我的标题栏应该像这样的,我们让左边的那块东西随着当前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 可以计算出目标位置。看图理解:

android界面开发:ViewPager的详解,并用于仿微博滑动实例和图片滚动实例_第2张图片

动画完成了,那么就需要监听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);
    }

自定义的标题导航栏就到此为止了。

4.viewpager应用:实现轮播图片功能 ,写成自定义view

实现原理和上面类似,只是相当于将标题换成了小圆点而已,再增加自动轮播。

1.画小圆点(可不画,用图片代替)

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

2.布局文件

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的实现和上面一样,这部分就不重复了。

3.轮播逻辑

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文件

4.自定义的SlideShowView使用

   <com.yufem.view.SlideShowView
         android:layout_width="match_parent"
         android:layout_height="400dp"
          />

这里的轮播时间,图片数量,来源都写死了,要想修改直接将源码修改即可。还有就是点击图片跳转的功能,只需给每个item加入点击事件即可,仿照上面自定义标题实现的点击。

附:跟随的滑动

有些软件滑动方式的有点不同,它的标题滑块是跟随着手指滑动而滑动的,就像下图一样。

android界面开发:ViewPager的详解,并用于仿微博滑动实例和图片滚动实例_第3张图片

思路:
既然要跟随页面滑动而滑动,那么就需要监听滑动事件,并计算出相应的距离。页面滑动一页,而导航栏的标题滑动一小格。假设是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

你可能感兴趣的:(Android开发)