Android利用ViewPager实现可滑动放大缩小画廊效果
画廊在很多的app设计中都有,如下图所示:
该例子是我没事的时候写的一个小项目,具体源码地址请访问https://github.com/alexsmille/yingmi。
该画廊类似封面的效果,滑到中间的图片会慢慢变大,离开的view会慢慢的缩小,同时可设置滑动监听和点击监听。
网上有很多例子都是通过gallery实现的,而上例的实现是通过viewpager实现,解决了性能优化的问题,今天特此把它抽出来,封装一下,以便以后的方便使用。最终实现的效果如下:
使用方式
布局中添加该自定义控件
<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"> <!-- 布局中添加自定义控件--> <com.mahao.alex.customviewdemo.viewpager.coverflowviewpager android:id="@+id/cover" android:layout_width="match_parent" android:layout_height="wrap_content" /> </relativelayout>
代码中设置
代码中设置分为以下几个步骤:
•查找控件
•初始化数据
•将需要显示的数据设置到控件上
•设置滑动监听
public class mainactivity extends appcompatactivity { private coverflowviewpager mcover; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mcover = (coverflowviewpager) findviewbyid(r.id.cover); // 初始化数据 list<view> list = new arraylist<>(); for(int i = 0;i<10;i++){ imageview img = new imageview(this); img.setbackgroundcolor(color.parsecolor("#"+getrandcolorcode())); list.add(img); } //设置显示的数据 mcover.setviewlist(list); // 设置滑动的监听,该监听为当前页面滑动到*时的索引 mcover.setonpageselectlistener(new onpageselectlistener() { @override public void select(int position) { toast.maketext(getapplicationcontext(),position+"",toast.length_short).show(); } }); } /** * 获取随机颜色,便于区分 * @return */ public static string getrandcolorcode(){ string r,g,b; random random = new random(); r = integer.tohexstring(random.nextint(256)).touppercase(); g = integer.tohexstring(random.nextint(256)).touppercase(); b = integer.tohexstring(random.nextint(256)).touppercase(); r = r.length()==1 ? "0" + r : r ; g = g.length()==1 ? "0" + g : g ; b = b.length()==1 ? "0" + b : b ; return r+g+b; } }
实现原理
实现过程中有两个难点:
•如何实现滑动过程中的放大与缩小
•如何显示viewpager中未被显示的页面
如何实现滑动过程中的放大与缩小?
在设置每一个viewpager 的页面时,对每一个页面都设置一个固定的padding值,这样每个页面都会显示缩小状态。同时viewpager设置addonpagechangelistener(),滑动监听,在该滑动监听中会回调viewpager的滑动的状态,滑动的偏移量等,根据滑动的偏移量进行放大缩小。及根据padding值设置控件的显示大小
如何显示viewpager中未被显示的页面
在xml中有一个不常用的属性android:clipchildren,是否限制子view的显示。设置为false,则子view的显示不受父控件的限制。
代码实现
编写控件的布局文件
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipchildren="false"> <android.support.v4.view.viewpager android:id="@+id/vp_conver_flow" android:layout_width="180dp" android:layout_height="260dp" android:layout_centerhorizontal="true" android:clipchildren="false" /> </relativelayout>
一个相对布局中嵌入一个viewpager,相对布局用于确定显示的范围,viewpager用以实现滑动,放大缩小等。
创建coverflowviewpager,加载布局
/** * * 实现封面浏览 * created by alex_mahao on 2016/8/25. */ public class coverflowviewpager extends relativelayout implements onpageselectlistener { /** * 用于左右滚动 */ private viewpager mviewpager; public coverflowviewpager(context context, attributeset attrs) { super(context, attrs); inflate(context, r.layout.widget_cover_flow,this); mviewpager = (viewpager) findviewbyid(r.id.vp_conver_flow); //init(); }
查找控件,并加载布局。
编写适配器,实现滑动的监听
既然有了viewpager,那么肯定要有适配器adapter。因为我们要在滑动监听中,根据偏移量操作每一个子元素,放大或缩小,而对于子元素,当然适配器最容易获取,所以将adapter实现了viewpager的滑动监听接口。
/** * 滚动的适配器 * created by alex_mahao on 2016/8/25. */ public class coverflowadapter extends pageradapter implements viewpager.onpagechangelistener { /** * 默认缩小的padding值 */ public static int swidthpadding; public static int sheightpadding; /** * 子元素的集合 */ private list<view> mviewlist; /** * 滑动监听的回调接口 */ private onpageselectlistener listener; /** * 上下文对象 */ private context mcontext; public coverflowadapter(list<view> mimageviewlist, context context) { this.mviewlist = mimageviewlist; mcontext = context; // 设置padding值 swidthpadding = dp2px(24); sheightpadding = dp2px(32); } @override public void destroyitem(viewgroup container, int position, object object) { container.removeview(mviewlist.get(position)); } @override public object instantiateitem(viewgroup container, int position) { view view = mviewlist.get(position); container.addview(view); return view; } @override public int getcount() { return mviewlist == null ? 0 : mviewlist.size(); } @override public boolean isviewfromobject(view view, object object) { return view == object; } @override public void onpagescrolled(int position, float positionoffset, int positionoffsetpixels) { // 该方法回调viewpager 的滑动偏移量 if (mviewlist.size() > 0 && position < mviewlist.size()) { //当前手指触摸滑动的页面,从0页滑动到1页 offset越来越大,padding越来越大 log.i("info", "重新设置padding"); int outheightpadding = (int) (positionoffset * sheightpadding); int outwidthpadding = (int) (positionoffset * swidthpadding); // 从0滑动到一时,此时position = 0,其应该是缩小的,符合 mviewlist.get(position).setpadding(outwidthpadding, outheightpadding, outwidthpadding, outheightpadding); // position+1 为即将显示的页面,越来越大 if (position < mviewlist.size() - 1) { int inwidthpadding = (int) ((1 - positionoffset) * swidthpadding); int inheightpadding = (int) ((1 - positionoffset) * sheightpadding); mviewlist.get(position + 1).setpadding(inwidthpadding, inheightpadding, inwidthpadding, inheightpadding); } } } @override public void onpageselected(int position) { // 回调选择的接口 if (listener != null) { listener.select(position); } } @override public void onpagescrollstatechanged(int state) { } /** * 当将某一个作为最*时的回调 * * @param listener */ public void setonpageselectlistener(onpageselectlistener listener) { this.listener = listener; } /** * dp 转 px * * @param dp * @return */ public int dp2px(int dp) { int px = (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dp, mcontext.getresources().getdisplaymetrics()); return px; } }
改代码分为两部分,pageradapter实现,滑动监听的实现。pageradapter的实现不在多说,最基础的东西。重点在滑动监听的实现。
滑动监听有三个回调方法:其中onpagescrolled(int position, float positionoffset, int positionoffsetpixels)回调的便是viewpager的滑动得偏移量,我们再次动态的设置相应元素的padding值,实现放大缩小。
onpageselected()为选中的回调,通过自定义接口的方式回调给其调用者。后面会提。
初始化viewpager
既然有了适配器,那么自然就开始编写适配器的部分:
/** * 初始化方法 */ private void init() { // 构造适配器,传入数据源 madapter = new coverflowadapter(mviewlist,getcontext()); // 设置选中的回调 madapter.setonpageselectlistener(this); // 设置适配器 mviewpager.setadapter(madapter); // 设置滑动的监听,因为adpter实现了滑动回调的接口,所以这里直接设置adpter mviewpager.addonpagechangelistener(madapter); // 自己百度 mviewpager.setoffscreenpagelimit(5); // 设置触摸事件的分发 setontouchlistener(new ontouchlistener() { @override public boolean ontouch(view v, motionevent event) { // 传递给viewpager 进行滑动处理 return mviewpager.dispatchtouchevent(event); } }); }
注释很详细,唯一需要解释的便是事件的分发。
我们的viewpager的大小是固定的,只有中间的显示区域,那么对于手指在两个侧边滑动时,viewpager自然接受不到触摸事件,通过设置外层相对布局的触摸事件监听,将触摸的事件传递到viewpager,实现滑动viewpager之外区域时,viewpager仍能够实现对应的滑动。
数据源的包装
适配器有了,viewpager也有了,那么只剩下数据源了。
因为我们是根据设置padding值实现的,那么对于需要显示的控件,他的背景将无法实现放大缩小,所以对控件在包装一层外部控件,这样设置外部控件的padding值,自然需要显示的控件会放大缩小。
/** * 设置显示的数据,进行一层封装 * @param lists */ public void setviewlist(list<view> lists){ if(lists==null){ return; } mviewlist.clear(); for(view view:lists){ framelayout layout = new framelayout(getcontext()); // 设置padding 值,默认缩小 layout.setpadding(coverflowadapter.swidthpadding,coverflowadapter.sheightpadding,coverflowadapter.swidthpadding,coverflowadapter.sheightpadding); layout.addview(view); mviewlist.add(layout); } // 刷新数据 madapter.notifydatasetchanged(); }
选中监听的回调
当我们滑动时,可能会根据不同的滑动,显示不同的数据。
通过设置滑动监听之后,对onpageselected实现层层的接口回调。
接口的定义onpageselectlistener
public interface onpageselectlistener { void select(int position); }
coverflowadapter中添加回调
@override public void onpageselected(int position) { // 回调选择的接口 if (listener != null) { listener.select(position); } }
coverflowviewpager中添加回调
// 显示的回调 @override public void select(int position) { if(listener!=null){ listener.select(position); } }
点击事件的设置
直接对数据源循环设置监听即可
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: Java递归方法求5!的实现代码