ViewPager 实现 Galler 效果, 显示中间大图显示,两边小图展示

转自:http://blog.youkuaiyun.com/xiangzhihong8/article/details/52317540


正常情况下, ViewPager 一页只能显示一项数据, 但是我们常常看到网上,特别是电视机顶盒的首页经常出现中间大图显示两端也都露出一点来,这种效果怎么实现呢?先上一张效果图:


大家第一眼肯定想到了Gallery,这是最早android图库自带的效果,现在基本不用,那有没有其他好的办法呢?我们首先考虑的还是ViewPager+PagerAdapter的实现策略。
后面在网上了搜了一下, 发现要实现上面的效果,我们需要注意两个方面,首先是怎么在两边显示两个小图,第二,怎么实现无限滑动。
1,首先就是用到了View的android:clipChildren属性,.简单来说父View是默认是束缚子View 的显示范围的,所以当我们在父View有 padding , 那么 子View 则在 padding区域是不能显示内容的。当设置android:clipChildren="false"的时候,子View 就可以在父View 的padding内容区域显示内容了。
2,实现无限循环很简单,网上也有很多的解决方案,我这里不考虑性能上的东西,且看下面简单的代码:
  1. private class ImageAdapter extends PagerAdapter{    
  2.              
  3.         private ArrayList<String> viewlist;    
  4.      
  5.         public ImageAdapter(ArrayList<String> viewlist) {    
  6.             this.viewlist = viewlist;    
  7.         }    
  8.      
  9.         @Override    
  10.         public int getCount() {    
  11.             //设置成最大,使用户看不到边界,大家可以去查询下这个大小    
  12.             return Integer.MAX_VALUE;    
  13.         }         
  14.          @Override      
  15.          public void destroyItem(ViewGroup container, int position,      
  16.                  Object object) {      
  17.              //注:不要在这里调用removeView    
  18.          }     
  19.            
  20.          @Override      
  21.          public Object instantiateItem(ViewGroup container, int position) {    
  22.              //对ViewPager页号求模取出View列表中要显示的项    
  23.              position %= viewlist.size();    
  24.              if (position<0){    
  25.                  position = viewlist.size()+position;    
  26.              }    
  27.                
  28.              //这里是view  
  29.             ViewHolder viewHolder = null;  
  30.             View view = LayoutInflater.from(mContext).inflate(  
  31.                 R.layout.item_finefare_layout, null);  
  32.           if (viewHolder == null) {  
  33.             viewHolder = new ViewHolder(view);  
  34.            }  
  35.             bindView(viewHolder, data);  
  36.   
  37.           container.addView(view, LayoutParams.MATCH_PARENT,  
  38.                 LayoutParams.MATCH_PARENT);  
  39.              return view;      
  40.          }      
  41.     }    

上面代码应该注意的几点:

  • getCount() 方法的返回值:这个值直接关系到ViewPager的“边界”,因此当我们把它设置为Integer.MAX_VALUE之后,用户基本就看不到这个边界了(估计滑到这里的时候电池已经挂了吧o_O)。当然,通常情况下设置为100倍实际内容个数也是可以的,之前看的某个实现就是这么干的。

  • instantiateItem() 方法position的处理:由于我们设置了count为 Integer.MAX_VALUE,因此这个position的取值范围很大很大,但我们实际要显示的内容肯定没这么多(往往只有几项),所以这里肯定会有求模操作。但是,简单的求模会出现问题:考虑用户向左滑的情形,则position可能会出现负值。所以我们需要对负值再处理一次,使其落在正确的区间内。

  • instantiateItem() 方法父组件的处理:通常我们会直接addView,但这里如果直接这样写,则会抛出IllegalStateException假设一共有三个view,则当用户滑到第四个的时候就会触发这个异常,原因是我们试图把一个有父组件的View添加到另一个组件

经过上面的解释,我们已经很清楚了,以下是代码的详细实现,数据来源于网上,大家可以自行模拟:
ViewPager类:
  1. public class WelfareAdapter extends PagerAdapter {  
  2.   
  3.     private Context mContext;  
  4.     private List<PanicBean> dataList = new ArrayList<>();  
  5.   
  6.     public WelfareAdapter(Context mContext) {  
  7.         this.mContext = mContext;  
  8.     }  
  9.   
  10.     public void setDatas(List<PanicBean> list) {  
  11.         if (list.size() <= 0) {  
  12.             dataList.clear();  
  13.             notifyDataSetChanged();  
  14.             return;  
  15.         }  
  16.         dataList.clear();  
  17.         dataList.addAll(list);  
  18.         notifyDataSetChanged();  
  19.     }  
  20.   
  21.     @Override  
  22.     public int getCount() {  
  23.         return Integer.MAX_VALUE;  
  24.     }  
  25.   
  26.     @Override  
  27.     public int getItemPosition(Object object) {  
  28.         return POSITION_NONE;  
  29.     }  
  30.   
  31.     @Override  
  32.     public Object instantiateItem(ViewGroup container, int position) {  
  33.         position %= dataList.size();  
  34.         if (position<0){  
  35.             position = dataList.size()+position;  
  36.         }  
  37.   
  38.         PanicBean data = dataList.get(position);  
  39.   
  40.         ViewHolder viewHolder = null;  
  41.         View view = LayoutInflater.from(mContext).inflate(  
  42.                 R.layout.item_finefare_layout, null);  
  43.         if (viewHolder == null) {  
  44.             viewHolder = new ViewHolder(view);  
  45.         }  
  46.         bindView(viewHolder, data);  
  47.   
  48.         container.addView(view, LayoutParams.MATCH_PARENT,  
  49.                 LayoutParams.MATCH_PARENT);  
  50.         return view;  
  51.     }  
  52.   
  53.     private void bindView(ViewHolder viewholder, final PanicBean data) {  
  54.         Glide.with(mContext).load(data.pic).into(viewholder.welfareImage);  
  55.   
  56.         viewholder.welfareImage.setOnClickListener(new View.OnClickListener() {  
  57.             @Override  
  58.             public void onClick(View view) {  
  59.                 ToastUtils.showToast("你点击了"+data.href);  
  60.             }  
  61.         });  
  62.     }  
  63.   
  64.     @Override  
  65.     public boolean isViewFromObject(View view, Object object) {  
  66.         return view == object;  
  67.     }  
  68.   
  69.     @Override  
  70.     public void destroyItem(ViewGroup container, int position, Object object) {  
  71. //        container.removeView((View) object);  
  72.     }  
  73.   
  74.     class ViewHolder {  
  75.         @BindView(R.id.welfare_image)  
  76.         RoundedImageView welfareImage;  
  77.   
  78.         ViewHolder(View view) {  
  79.             ButterKnife.bind(this, view);  
  80.             view.setTag(this);  
  81.         }  
  82.   
  83.         public void reset() {  
  84.             welfareImage.setBackground(mContext.getResources().getDrawable(R.drawable.welfare_default_icon));  
  85.         }  
  86.     }  
  87.   
  88. }  
用到的布局:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:ptr="http://schemas.android.com/apk/res-auto"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical"  
  7.     android:gravity="center">  
  8.   
  9.     <com.yju.app.widght.image.RoundedImageView  
  10.         android:id="@+id/welfare_image"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent"  
  13.         android:layout_gravity="center"  
  14.         android:scaleType="fitXY"  
  15.         ptr:corner_radius="3dp"  
  16.         android:src="@drawable/welfare_default_icon" />  
  17.   
  18. </LinearLayout>  

用到的数据Bean(这个大家根据情况自行模拟,只要是个列表就行)
  1. public class FineFareEntity {  
  2.   
  3.     public List<PanicBean> panic;  
  4.   
  5.     public static class PanicBean {  
  6.         public String id;  
  7.         public long endtime;  
  8.         public String pic;  
  9.         public int type;  
  10.         public String href;  
  11.         public String title;  
  12.         public String share;  
  13.     }  
  14. }  

为了方便使用我们都自定义成View,方便以后代码维护:
  1. public class WelfareView extends SimpleLinearLayout {  
  2.   
  3.     @BindView(R.id.finefare_count)  
  4.     TextView finefareCount;  
  5.     @BindView(R.id.viewPager)  
  6.     ViewPager viewPager;  
  7.     @BindView(R.id.finefare_name)  
  8.     TextView finefareName;  
  9.     @BindView(R.id.welfare_view)  
  10.     LinearLayout welfareView;  
  11.   
  12.     private WelfareAdapter adapter = null;  
  13.     private List<PanicBean> welfareList = null;  
  14.   
  15.     public WelfareView(Context context) {  
  16.         super(context);  
  17.     }  
  18.   
  19.     public WelfareView(Context context, AttributeSet attrs) {  
  20.         super(context, attrs);  
  21.     }  
  22.   
  23.     @Override  
  24.     protected void initViews() {  
  25.         contentView = inflate(mContext, R.layout.layout_welfare, this);  
  26.         ButterKnife.bind(this);  
  27.   
  28.         initViewPager();  
  29.   
  30.         initTouch();  
  31.   
  32.     }  
  33.   
  34.     private void initTouch() {  
  35.         //这里要把父类的touch事件传给子类,不然边上的会滑不动  
  36.         setOnTouchListener(new OnTouchListener() {  
  37.             @Override  
  38.             public boolean onTouch(View v, MotionEvent event) {  
  39.                 return viewPager.dispatchTouchEvent(event);  
  40.             }  
  41.         });  
  42.   
  43.         viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {  
  44.             @Override  
  45.             public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  46.   
  47.             }  
  48.   
  49.             @Override  
  50.             public void onPageSelected(int position) {  
  51.                 position %= welfareList.size();  
  52.                 if (position<0){  
  53.                     position = welfareList.size()+position;  
  54.                 }  
  55.                 finefareName.setText(welfareList.get(position).id);  
  56.             }  
  57.   
  58.             @Override  
  59.             public void onPageScrollStateChanged(int state) {  
  60.   
  61.             }  
  62.         });  
  63.   
  64.     }  
  65.   
  66.     private void initViewPager() {  
  67.         viewPager.setOffscreenPageLimit(3);  
  68.         viewPager.setPageTransformer(true, new ScalePagerTransformer());  
  69.         //设置Pager之间的间距  
  70.         viewPager.setPageMargin(UIUtils.dp2px(mContext, 15));  
  71.   
  72.         adapter = new WelfareAdapter(mContext);  
  73.         viewPager.setAdapter(adapter);  
  74.     }  
  75.   
  76.     public void setWelfareData(List<PanicBean> datas) {  
  77.         this.welfareList = datas;  
  78.         welfareView.setVisibility(datas.size()>0?VISIBLE:GONE);  
  79.         finefareCount.setText("共有" + datas.size() + "个福利");  
  80.         finefareName.setText(welfareList.get(getCurrentDisplayItem()).title);  
  81.   
  82.         adapter = new WelfareAdapter(mContext);  
  83.         adapter.setDatas(datas);  
  84.         viewPager.setAdapter(adapter);  
  85.         viewPager.setCurrentItem(adapter.getCount() > 0 ? 1 : 0, true);  
  86.   
  87.     }  
  88.   
  89.     public int getCurrentDisplayItem() {  
  90.         if (viewPager != null) {  
  91.             return viewPager.getCurrentItem();  
  92.         }  
  93.         return 0;  
  94.     }  
  95.   
  96. }  
这里有一个滑动缩放的类:
  1. public class ScalePagerTransformer implements ViewPager.PageTransformer {  
  2.   
  3.         private static final float MIN_SCALE = 0.85f;  
  4.         private static final float MIN_ALPHA = 0.5f;  
  5.   
  6.         @Override  
  7.         public void transformPage(View view, float position) {  
  8.             if (position >= -1 || position <= 1) {  
  9.                 final float height = view.getHeight();  
  10.                 final float width = view.getWidth();  
  11.                 final float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));  
  12.                 final float vertMargin = height * (1 - scaleFactor) / 2;  
  13.                 final float horzMargin = width * (1 - scaleFactor) / 2;  
  14.   
  15.                 view.setPivotY(0.5f * height);  
  16.                 view.setPivotX(0.5f * width);  
  17.   
  18.                 if (position < 0) {  
  19.                     view.setTranslationX(horzMargin - vertMargin / 2);  
  20.                 } else {  
  21.                     view.setTranslationX(-horzMargin + vertMargin / 2);  
  22.                 }  
  23.   
  24.                 view.setScaleX(scaleFactor);  
  25.                 view.setScaleY(scaleFactor);  
  26.   
  27.                 view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));  
  28.             }  
  29.         }  
  30.     }  


用到的布局( android:clipChildren="false"需要注意 ):
  1. <span style="color:#333333;"><?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     android:id="@+id/welfare_view"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     </span><span style="color:#ff0000;">android:clipChildren="false"</span><span style="color:#333333;">  
  8.     android:layout_marginTop="10dp"  
  9.     android:background="@color/c12"  
  10.     android:orientation="vertical">  
  11.   
  12.   
  13.     <LinearLayout  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:orientation="horizontal">  
  17.   
  18.         <View  
  19.             style="@style/vertical_bold_line_c8"  
  20.             />  
  21.   
  22.         <RelativeLayout  
  23.             android:layout_width="match_parent"  
  24.             android:layout_height="wrap_content"  
  25.             android:orientation="horizontal"  
  26.             android:padding="10dp">  
  27.   
  28.             <TextView  
  29.                 style="@style/style_c6_s16"  
  30.                 android:layout_centerVertical="true"  
  31.                 android:text="精品福利" />  
  32.   
  33.             <TextView  
  34.                 android:id="@+id/finefare_count"  
  35.                 style="@style/style_c6_s14"  
  36.                 android:layout_alignParentRight="true"  
  37.                 android:layout_centerVertical="true"  
  38.                 android:drawablePadding="3dp"  
  39.                 android:drawableRight="@drawable/arrow"  
  40.                 android:text="共有n个福利" />  
  41.         </RelativeLayout>  
  42.     </LinearLayout>  
  43.   
  44.     <android.support.v4.view.ViewPager  
  45.         android:id="@+id/viewPager"  
  46.         android:layout_width="match_parent"  
  47.         android:layout_height="140dp"  
  48.         android:layout_marginLeft="45dp"  
  49.         android:layout_marginRight="45dp"  
  50.          />  
  51.   
  52.     <TextView  
  53.         android:id="@+id/finefare_name"  
  54.         style="@style/style_c8_s16"  
  55.         android:layout_gravity="center"  
  56.         android:layout_marginBottom="15dp"  
  57.         android:layout_marginTop="15dp"  
  58.         android:text="" />  
  59. </LinearLayout>  
  60. </span>  

最后就是在我们的主代码中写个歌测试了,
  1. private void initWelfare() {  
  2.        String welfare = FileUtils.readAssert(getActivity(), "welfare.txt");  
  3.        FineFareEntity entity=JsonUtils.parseJson(welfare,FineFareEntity.class);  
  4.        if (entity!=null){  
  5.            welfareView.setWelfareData(entity.panic);  
  6.        }  
  7.    }  
涉及到的布局:
  1. <com.yju.app.shihui.welfare.view.WelfareView  
  2.                 android:id="@+id/welfare_view"  
  3.                 android:layout_width="match_parent"  
  4.                 android:layout_height="wrap_content"  
  5.                 android:minHeight="160dp"  
  6.                 />  

这里的 FileUtils.readAssert 代码:
  1. public static String readAssert(Context context, String fileName){  
  2.         String resultString="";  
  3.         try {  
  4.             InputStream inputStream=context.getResources().getAssets().open(fileName);  
  5.             byte[] buffer=new byte[inputStream.available()];  
  6.             inputStream.read(buffer);  
  7.             resultString=new String(buffer,"utf-8");  
  8.         } catch (Exception e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.         return resultString;  
  12.     }  
最后为了方便大家的模拟,我把数据给大家,有点多,大家自己放到assert目录下,
  1. {  
  2. panic: [  
  3. {  
  4. id: "2412",  
  5. endtime: 1472097600000,  
  6. pic: "http://pc1.img.ymatou.com/G02/M04/E3/67/CgvUBVe9vyOAV47CAACXZZs5GrU558_o.jpg",  
  7. type: 1,  
  8. href: "http://evt.ymatou.com/n770",  
  9. title: "今日限时抢",  
  10. share: ""  
  11. },  
  12. {  
  13. id: "2417",  
  14. endtime: 1472097600000,  
  15. pic: "http://pc1.img.ymatou.com/G02/M09/E4/37/CgvUA1e91VmAMYrwAAC5qcblOUg650_o.jpg",  
  16. type: 1,  
  17. href: "http://evt.ymatou.com/n781",  
  18. title: "今日限时抢",  
  19. share: ""  
  20. },  
  21. {  
  22. id: "2413",  
  23. endtime: 1472097600000,  
  24. pic: "http://pc1.img.ymatou.com/G02/M05/E3/D4/CgvUA1e9v2SAVf3qAAB9GcBIWYA268_o.jpg",  
  25. type: 1,  
  26. href: "http://evt.ymatou.com/n771",  
  27. title: "今日限时抢",  
  28. share: ""  
  29. },  
  30. {  
  31. id: "2414",  
  32. endtime: 1472097600000,  
  33. pic: "http://pc1.img.ymatou.com/G02/M05/E3/69/CgvUBVe9v4aAMaFRAABWy73vn2g252_o.jpg",  
  34. type: 1,  
  35. href: "http://evt.ymatou.com/n772",  
  36. title: "今日限时抢",  
  37. share: ""  
  38. },  
  39. {  
  40. id: "2415",  
  41. endtime: 1472097600000,  
  42. pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v6WAP85NAAC6EK5e5Vg469_o.jpg",  
  43. type: 1,  
  44. href: "http://evt.ymatou.com/n773",  
  45. title: "今日限时抢",  
  46. share: ""  
  47. },  
  48. {  
  49. id: "2416",  
  50. endtime: 1472097600000,  
  51. pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v8CAHyXVAACELcKFT_M328_o.jpg",  
  52. type: 1,  
  53. href: "http://evt.ymatou.com/n775",  
  54. title: "今日限时抢",  
  55. share: ""  
  56. }  
  57. ]  
  58. }  

写得有点急,欢迎大家留言,有什么不懂得,请进我们的开发群,一定细心讲解: 278792776

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值