27
2017
09

基于VerticalViewPager的上下滑动,可带动画效果,类似WheelView

特别声明:本系列文章leicher著作权所有,转载请注明出处。

  • 起因

前段时间一直头疼一个关于类似WheelView的选择器,但要求有动画效果类似这样 :

http://img.blog.csdn.net/20170511095020925?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzQ5NzcyNzU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

之间也有过很多思路(都无疾而终),看过很多开源框架,但是这种效果的比较少,就在此记录一下,希望对需要的人有所帮助。
一次偶然看到张鸿洋大神的关于ViewPager的切换动画

http://blog.csdn.net/lmj623565791/article/details/40411921/

需要的可以去看下然后在github上找到了开源的VerticalViewPager ,脑袋里便蹦出了使用这个来完成此功能的念想。
那么到此关键的问题便是如何显示两边的Item了

http://blog.csdn.net/asia_deng/article/details/70176393

以上便是思路历程。那么接下来看我怎么实现的吧。
首先,VerticalViewPager 网上有很多开源的,实现方法也很简单,在这里我就不赘述了。
然后便是对于VerticalViewPager 显示上下两个Item 了,这个和ViewPager也没啥区别关键在于 :

android:clipChildren="false"

对于ViewPager 的父布局(你想要显示到的地方)和ViewPager 加上该属性便可以实现。
那么仅仅只添加这个是不行的,不知道是ViewPager的原因还是这个属性的原因,可以看到仅仅是在滑动时才能看到上下两个Item。

pager.setOffscreenPageLimit(int x); 

那么这个属性就起作用了,这会让ViewPager加载上下2*x+1个item ,就完美解决了这个问题。
难点便是解决Item 动画的问题了,看了张鸿洋大神的博客之后,想必你也会有所思路,但这不同的是我们要存储的不仅仅是三个Item,而是更多。尤其是当我们需要无限滑动的时候,就像Banner条一样。对于ViewPager来说我们自然不用担心,它会销毁已经不用的Item,但我们要对Item进行动画,
我们必然需要用List保存我们的Item引用。那我们则需要对我们的List管理好,不然滑动多了便会OOM。
以下便是代码:

class Adapter extends PagerAdapter{
    private boolean isSmall(float s){
        return s < 0.01f;
    }

    ViewPager.OnPageChangeListener listener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (!isSmall(positionOffset)){
                View top = views.get(position);
                View bottom = views.get(position + 1);
                if (bottom != null){
                    bottom.setScaleX((float) (0.5*positionOffset + 1f));
                    bottom.setScaleY((float) (0.5*positionOffset + 1f));
                }
                if (top != null){
                    top.setScaleX((float) (1.5f - 0.5*positionOffset));
                    top.setScaleY((float) (1.5f - 0.5*positionOffset));
                }

            }
        }

        @Override
        public void onPageSelected(int position) {
            View view = views.get(position);
            if (view != null){
                view.setScaleX(1.5f);
                view.setScaleY(1.5f);
            }
            views.remove(position-3);
            views.remove(position+3);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    };

    public Adapter() {
        pager.setOnPageChangeListener(listener);
    }

    SparseArray<View> views = new SparseArray<>();
    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View rootView = getLayoutInflater().inflate(R.layout.wheel_item, container, false);
        WheelImageView img = (WheelImageView) rootView.findViewById(R.id.img);
        img.setImageResource(imgs[position % 4]);
        img.setCircleColor(colors[position % 4]);
        TextView text = (TextView) rootView.findViewById(R.id.text);
        text.setText(titles[position % 4]);
        views.put(position, rootView);
        int currentItem = pager.getCurrentItem();
        if (currentItem == position){
             rootView.setScaleX(1.5f);
             rootView.setScaleY(1.5f);
        }
        ViewGroup parent = (ViewGroup) rootView.getParent();
        if (parent != null){
            parent.removeView(rootView);
        }
        container.addView(rootView);
        return rootView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }
}

为了让滑动父布局时ViewPager也能滑动

LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
parent.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return pager.onTouchEvent(event);
    }
});

当然如果你的轮播的Item个数比显示的个数多很多,那么在instantiateItem()这里可以选择复用,对于我这只有四个Item的,而显示同时三个,复用的话便会有问题。症结在于你需要在destroyItem之后再复用,不然会报错。
最后,看看我们布局文件吧。

<LinearLayout  android:id="@+id/parent" android:orientation="vertical" android:clipChildren="false" android:paddingTop="5dp" android:paddingBottom="5dp" android:layout_width="match_parent" android:layout_height="match_parent">
   <RelativeLayout  android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2">
      <View  android:layout_width="match_parent" android:layout_alignParentBottom="true" android:background="@drawable/wheel_divider" android:layout_height="@dimen/dimen_ss"/>
   </RelativeLayout>
   <com.smaradio.mobile.view.VerticalViewPager  android:overScrollMode="never" android:id="@+id/pager" android:layout_width="match_parent" android:clipChildren="false" android:layout_height="0dp" android:layout_weight="3"/>
   <RelativeLayout  android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2">
      <View  android:layout_width="match_parent" android:background="@color/white_a" android:layout_height="@dimen/dimen_ss"/>
   </RelativeLayout>
</LinearLayout>

哦,在这里再提出一个我遇到的问题,便是对这个ViewPager的Item的布局问题了。如果你的布局较为复杂,功能繁复,你可能就要先想想ViewPager的这个回调监听能不能满足你的问题了,请慎用。

上一篇:Android 的消息机制(一) 下一篇:项目总结-重写activity中的onNewIntent 方法