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


大家第一眼肯定想到了Gallery,这是最早android图库自带的效果,现在基本不用,那有没有其他好的办法呢?我们首先考虑的还是ViewPager+PagerAdapter的实现策略。
后面在网上了搜了一下, 发现要实现上面的效果,我们需要注意两个方面,首先是怎么在两边显示两个小图,第二,怎么实现无限滑动。
1,首先就是用到了View的android:clipChildren属性,.简单来说父View是默认是束缚子View 的显示范围的,所以当我们在父View有 padding , 那么 子View 则在 padding区域是不能显示内容的。当设置android:clipChildren="false"的时候,子View 就可以在父View 的padding内容区域显示内容了。
2,实现无限循环很简单,网上也有很多的解决方案,我这里不考虑性能上的东西,且看下面简单的代码:
private class ImageAdapter extends PagerAdapter{  

        private ArrayList<String> viewlist;  

        public ImageAdapter(ArrayList<String> viewlist) {
            this.viewlist = viewlist;
        }  

        @Override
        public int getCount() {
            //设置成最大,使用户看不到边界,大家可以去查询下这个大小
            return Integer.MAX_VALUE;
        }
         @Override
         public void destroyItem(ViewGroup container, int position,
                 Object object) {
             //注:不要在这里调用removeView
         }   

         @Override
         public Object instantiateItem(ViewGroup container, int position) {
             //对ViewPager页号求模取出View列表中要显示的项
             position %= viewlist.size();
             if (position<0){
                 position = viewlist.size()+position;
             }  

			 //这里是view
            ViewHolder viewHolder = null;
            View view = LayoutInflater.from(mContext).inflate(
                R.layout.item_finefare_layout, null);
          if (viewHolder == null) {
            viewHolder = new ViewHolder(view);
           }
            bindView(viewHolder, data);

          container.addView(view, LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
             return view;
         }
    }  

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

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

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

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

经过上面的解释,我们已经很清楚了,以下是代码的详细实现,数据来源于网上,大家可以自行模拟:

ViewPager类:
public class WelfareAdapter extends PagerAdapter {

    private Context mContext;
    private List<PanicBean> dataList = new ArrayList<>();

    public WelfareAdapter(Context mContext) {
        this.mContext = mContext;
    }

    public void setDatas(List<PanicBean> list) {
        if (list.size() <= 0) {
            dataList.clear();
            notifyDataSetChanged();
            return;
        }
        dataList.clear();
        dataList.addAll(list);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        position %= dataList.size();
        if (position<0){
            position = dataList.size()+position;
        }

        PanicBean data = dataList.get(position);

        ViewHolder viewHolder = null;
        View view = LayoutInflater.from(mContext).inflate(
                R.layout.item_finefare_layout, null);
        if (viewHolder == null) {
            viewHolder = new ViewHolder(view);
        }
        bindView(viewHolder, data);

        container.addView(view, LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
        return view;
    }

    private void bindView(ViewHolder viewholder, final PanicBean data) {
        Glide.with(mContext).load(data.pic).into(viewholder.welfareImage);

        viewholder.welfareImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ToastUtils.showToast("你点击了"+data.href);
            }
        });
    }

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

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

    class ViewHolder {
        @BindView(R.id.welfare_image)
        RoundedImageView welfareImage;

        ViewHolder(View view) {
            ButterKnife.bind(this, view);
            view.setTag(this);
        }

        public void reset() {
            welfareImage.setBackground(mContext.getResources().getDrawable(R.drawable.welfare_default_icon));
        }
    }

}

用到的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ptr="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <com.yju.app.widght.image.RoundedImageView
        android:id="@+id/welfare_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:scaleType="fitXY"
        ptr:corner_radius="3dp"
        android:src="@drawable/welfare_default_icon" />

</LinearLayout>

用到的数据Bean(这个大家根据情况自行模拟,只要是个列表就行)

public class FineFareEntity {

    public List<PanicBean> panic;

    public static class PanicBean {
        public String id;
        public long endtime;
        public String pic;
        public int type;
        public String href;
        public String title;
        public String share;
    }
}

为了方便使用我们都自定义成View,方便以后代码维护:

public class WelfareView extends SimpleLinearLayout {

    @BindView(R.id.finefare_count)
    TextView finefareCount;
    @BindView(R.id.viewPager)
    ViewPager viewPager;
    @BindView(R.id.finefare_name)
    TextView finefareName;
    @BindView(R.id.welfare_view)
    LinearLayout welfareView;

    private WelfareAdapter adapter = null;
    private List<PanicBean> welfareList = null;

    public WelfareView(Context context) {
        super(context);
    }

    public WelfareView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void initViews() {
        contentView = inflate(mContext, R.layout.layout_welfare, this);
        ButterKnife.bind(this);

        initViewPager();

        initTouch();

    }

    private void initTouch() {
        //这里要把父类的touch事件传给子类,不然边上的会滑不动
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return viewPager.dispatchTouchEvent(event);
            }
        });

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                position %= welfareList.size();
                if (position<0){
                    position = welfareList.size()+position;
                }
                finefareName.setText(welfareList.get(position).id);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }

    private void initViewPager() {
        viewPager.setOffscreenPageLimit(3);
        viewPager.setPageTransformer(true, new ScalePagerTransformer());
        //设置Pager之间的间距
        viewPager.setPageMargin(UIUtils.dp2px(mContext, 15));

        adapter = new WelfareAdapter(mContext);
        viewPager.setAdapter(adapter);
    }

    public void setWelfareData(List<PanicBean> datas) {
        this.welfareList = datas;
        welfareView.setVisibility(datas.size()>0?VISIBLE:GONE);
        finefareCount.setText("共有" + datas.size() + "个福利");
        finefareName.setText(welfareList.get(getCurrentDisplayItem()).title);

        adapter = new WelfareAdapter(mContext);
        adapter.setDatas(datas);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem(adapter.getCount() > 0 ? 1 : 0, true);

    }

    public int getCurrentDisplayItem() {
        if (viewPager != null) {
            return viewPager.getCurrentItem();
        }
        return 0;
    }

}

这里有一个滑动缩放的类:

public class ScalePagerTransformer implements ViewPager.PageTransformer {

        private static final float MIN_SCALE = 0.85f;
        private static final float MIN_ALPHA = 0.5f;

        @Override
        public void transformPage(View view, float position) {
            if (position >= -1 || position <= 1) {
                final float height = view.getHeight();
                final float width = view.getWidth();
                final float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
                final float vertMargin = height * (1 - scaleFactor) / 2;
                final float horzMargin = width * (1 - scaleFactor) / 2;

                view.setPivotY(0.5f * height);
                view.setPivotX(0.5f * width);

                if (position < 0) {
                    view.setTranslationX(horzMargin - vertMargin / 2);
                } else {
                    view.setTranslationX(-horzMargin + vertMargin / 2);
                }

                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);

                view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
            }
        }
    }
<pre name="code" class="html" style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px;">SimpleLinearLayout 类

public class SimpleLinearLayout extends LinearLayout {

    protected Context mContext;
    protected View contentView;
    protected AtomicBoolean isPreparingData;

    public SimpleLinearLayout(Context context) {
        super(context);
        this.mContext = context;
        isPreparingData = new AtomicBoolean(false);
        initViews();
    }

    public SimpleLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        isPreparingData = new AtomicBoolean(false);
        initViews();
    }

    protected void initViews() {

    }

}

用到的布局(android:clipChildren="false"需要注意):

<span style="color:#333333;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/welfare_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    </span><span style="color:#ff0000;">android:clipChildren="false"</span><span style="color:#333333;">
    android:layout_marginTop="10dp"
    android:background="@color/c12"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <View
            style="@style/vertical_bold_line_c8"
            />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="10dp">

            <TextView
                style="@style/style_c6_s16"
                android:layout_centerVertical="true"
                android:text="精品福利" />

            <TextView
                android:id="@+id/finefare_count"
                style="@style/style_c6_s14"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:drawablePadding="3dp"
                android:drawableRight="@drawable/arrow"
                android:text="共有n个福利" />
        </RelativeLayout>
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="140dp"
        android:layout_marginLeft="45dp"
        android:layout_marginRight="45dp"
         />

    <TextView
        android:id="@+id/finefare_name"
        style="@style/style_c8_s16"
        android:layout_gravity="center"
        android:layout_marginBottom="15dp"
        android:layout_marginTop="15dp"
        android:text="" />
</LinearLayout>
</span>

最后就是在我们的主代码中写个歌测试了,

 private void initWelfare() {
        String welfare = FileUtils.readAssert(getActivity(), "welfare.txt");
        FineFareEntity entity=JsonUtils.parseJson(welfare,FineFareEntity.class);
        if (entity!=null){
            welfareView.setWelfareData(entity.panic);
        }
    }

涉及到的布局:

<com.yju.app.shihui.welfare.view.WelfareView
                android:id="@+id/welfare_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="160dp"
                />

这里的FileUtils.readAssert 代码:

public static String readAssert(Context context, String fileName){
        String resultString="";
        try {
            InputStream inputStream=context.getResources().getAssets().open(fileName);
            byte[] buffer=new byte[inputStream.available()];
            inputStream.read(buffer);
            resultString=new String(buffer,"utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

最后为了方便大家的模拟,我把数据给大家,有点多,大家自己放到assert目录下,

{
panic: [
{
id: "2412",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M04/E3/67/CgvUBVe9vyOAV47CAACXZZs5GrU558_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n770",
title: "今日限时抢",
share: ""
},
{
id: "2417",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M09/E4/37/CgvUA1e91VmAMYrwAAC5qcblOUg650_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n781",
title: "今日限时抢",
share: ""
},
{
id: "2413",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M05/E3/D4/CgvUA1e9v2SAVf3qAAB9GcBIWYA268_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n771",
title: "今日限时抢",
share: ""
},
{
id: "2414",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M05/E3/69/CgvUBVe9v4aAMaFRAABWy73vn2g252_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n772",
title: "今日限时抢",
share: ""
},
{
id: "2415",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v6WAP85NAAC6EK5e5Vg469_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n773",
title: "今日限时抢",
share: ""
},
{
id: "2416",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v8CAHyXVAACELcKFT_M328_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n775",
title: "今日限时抢",
share: ""
}
]
}

其实针对上面的代码,有个bug,就是上面的代码虽然实现了滑动,却没有真正的实现左右滑动,针对上面的问题,请看下一篇优化篇。

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



ViewPager 实现 Galler 效果, 中间大图显示,两边小图展示的更多相关文章

  1. ViewPager 实现 Galler 效果, 中间大图显示,两边小图展示(优化篇)

    上一张效果图: 之前的项目有一个Galley的项目,但是代码结构特别乱,别问我为什么,我也是刚接手这个项目,为了方便以后阅读和维护我对一些模块进行了重构.ViewPager实现Galler效果,但是当 ...

  2. 【笔记】WPF实现ViewPager引导界面效果及问题汇总

    最近在开发项目的首次使用引导界面时,遇到了问题,引导界面类似于安卓手机ViewPager那样的效果,希望通过左右滑动手指来实现切换不同页面,其间伴随动画. 实现思路: 1.界面布局:新建一个UserC ...

  3. 基于jQuery左侧小图滚动右侧大图显示代码

    今天给大家分享一款 jQuery左侧小图滚动右侧大图显示代码是一款基于jQuery实现的左侧滚动图片点击大图查看效果代码.该实例适用浏览器:IE8.360.FireFox.Chrome.Safari. ...

  4. jQuery 效果 —— 隐藏和显示

    jQuery 效果 -- 隐藏和显示 1.隐藏和显示 (1)在jQuery中我们可以使用hide()和show()分别隐藏和显示HTML元素: //隐藏元素 $("button") ...

  5. 精致3D图片切换效果,最适合企业产品展示

    这是一个精致的立体图片切换效果,特别适合企业产品展示,可立即用于实际项目中.支持导航和自动播放功能, 基于 CSS3 实现,推荐使用最新的 Chrome,Firefox 和 Safari 浏览器浏览效 ...

  6. TexturePacker大图还原成小图工具带源码

    TexturePacker是一个把好多小图打成大图的软件,生成的是大图以及小图在大图位置的.plist描述文件,但是不支持把大图还原成小图.网上偷的图一般都是大图和plist,想得到小图比较麻烦,于是 ...

  7. Python - 工具:将大图切片成小图,将小图组合成大图

    训练keras时遇到了一个问题,就是内存不足,将 .fit 改成 .fit_generator以后还是放不下一张图(我的图片是8192×8192的大图==64M).于是解决方法是将大图切成小图,把小图 ...

  8. Android照片墙加强版,使用ViewPager实现画廊效果

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/12646775 记得关于照片墙的文章我已经写过好几篇了,有最基本的照片墙,有瀑布流模 ...

  9. 【安卓高级】ViewPager视差动画效果

    在安卓开发中,是否遇见过一些很酷的视差动画效果,当ViewPager滑动下一页的时候,页面内的各种元素也能跟随滑动做位移效果,整体看起来非常有活力. 关键的PageTransformer PageTr ...

随机推荐

  1. oracle拆分一个连续的字符串

    create or replace procedure pc(  sss out varchar2)isstr varchar2(20):='ph,p,cod,do,cu';en integer:=i ...

  2. dubbo服务的发布和调用

    Dubbo是分布式服务架构,是一个优秀的开源服务型框架,使得应用可以通过高性能的rpc实现服务的输入和输出功能.其实dubbo就是资源调度和治理中心的管理工具. 发布dubbo服务:在提供服务的应用中 ...

  3. 剑指架构师系列-Logstash分布式系统的日志监控

    Logstash主要做由三部署组成: Collect:数据输入 Enrich:数据加工,如过滤,改写等 Transport:数据输出 下面来安装一下: wget https://download.el ...

  4. Nodejs 模块查找机制还不错(从当前目录开始逐级向上查找node_modules)

    比如 m.js是能够调用a.js的, 这样子目录就可以避免重复安装node_modules. 够用了.

  5. Gradle 1.12用户指南翻译——第四十八章. Wrapper 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  6. 新浪微博Oauth2.0授权认证及SDK、API的使用(Android)

    ---------------------------------------------------------------------------------------------- [版权申明 ...

  7. HBase的环境配置及其应用

    -------------------------------------------------------------------------------------- [版权申明:本文系作者原创 ...

  8. DoesNotExist at /admin/

    DoesNotExist at /admin/ User has no account. Request Method: GET Request URL: http://127.0.0.1:8000/ ...

  9. CentOS6.7下安装MySQL

    第一步:到MySQL官网上下载linux版本的MySQL rpm 安装包. 第二步: 将该压塑包解压后,有如下文件: 第三步:安装文件,我们需要安装的文件有 MySQL-server-5.6.26-1 ...

  10. ssh用法及命令

    http://blog.csdn.net/pipisorry/article/details/52269785 什么是SSH? 简单说,SSH是一种网络协议,用于计算机之间的加密登录.如果一个用户从本 ...