Android自定义ViewPager实现个性化的图片切换效果
第一次见到viewpager这个控件,瞬间爱不释手,做东西的主界面通通viewpager,以及图片切换也抛弃了imageswitch之类的,开始让viewpager来做。时间长了,viewpager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如何改变viewpager切换时的效果,实现个性化的图片切换
看一下这样效果的图片切换:
是不是比传统的效果个性很多,嘿嘿~~其实很简单,学习完这篇文章,保证你可以自定义切换效果,做出各式各样的效果。
1、制作前的分析
观察下效果图,实际上改变的就是切换时的动画,那么简单了,只需要用户在切换时,拿到当前的view和下一个view,然后添加动画是不是就可以了。
第一步,获取用户切换时的当前view和切换至的目的view。
我们在来看一下,如果或者了当前view和目的view,对于动画我们需要缓慢的变化,最好是根据用户的手势滑动。比如上述效果,用户滑动时,目的图片根据用户滑动距离缓缓出现和慢慢变大。
第二步,设计动画的梯度变化。
经过分析,我们总结出两个步骤,下面我们开始一步一步来打造千变万化的图片切换效果
2、获取用户切换时当前view和切换至的目的view。viewpager也需要监听用户的手势,所以肯定提供了某个方法。于是纵观viewpager的方法,发现了一个叫做 onpagescrolled(int position, float positionoffset, int positionoffsetpixels)的方法~~
没错就是这个方法:在页面滚动时调用~
下面仔细研究下这几个参数:
直接说测试结果:
在非第一页与最后一页时,滑动到下一页,position为当前页位置;滑动到上一页:position为当前页-1
positionoffset 滑动到下一页,[0,1)区间上变化;滑动到上一页:(1,0]区间上变化
positionoffsetpixels这个和positionoffset很像:滑动到下一页,[0,宽度)区间上变化;滑动到上一页:(宽度,0]区间上变化
第一页时:滑动到上一页position=0 ,其他基本为0 ;最后一页滑动到下一页 position为当前页位置,其他两个参数为0
豁然发现,我们需要的步骤的第二步解决了,positionoffset很适合作为,渐变,缩放的控制参数;positionoffsetpixels则可以作为平移等的控制参数。
那么如何获得当前view和目的view呢:
分享几个我的歧途:
1、【错误】我通过getchildat(position),getchildat(position+1),getchildat(position-1)获得滑动时,左右的两个view;乍一看,还真觉得不错~~~在代码写出来,再乍效果也出不来~~错误原因:我们忽略一个特别大的东西,viewpager的机制,滑动时动态加载和删除view,viewpager其实只会维持2到3个view,而position的范围基本属于无限~~
2、【错误】我通过getcurrentitem获得当前的位置,然后+1,-1获得后一个或者前一个~~正在窃喜,赶快代码改过来,效果怎么也不对,乱七八糟的~~仔细观察日志,这个getcurrentitem当用户手指离开的屏幕,page还在动画执行时,就改变了~~难怪~整个滑动过程并不是固定的~~唉,心都碎了~
3、【错误】position在整个滑动的过程中是不变化的,而且viewpager会保存2个或3个view;那么我考虑,如果是第一页、或者最后一页那么我取getchildat(0)和getchildat(1),如果在其他页面则为getchildat(0),getchildat(2),然后经过一系列的变化~我想这会总该对了吧,于是我遇到第一问题,第一页的时候,不管左右position都为0,尼玛,这哪个为左view,哪个为右view~~
说了这么多错误,大家可以绕过这些弯路,也能从这些弯路里面看出点什么~
下面说正确的,其实viewpager在添加一个view或者销毁一个view时,是我们自己的pageadapter中控制的,于是我们可以在viewpager里面维系一个hashmap<position,view>,然后滑动的时候,通过get(position)取出,比如上述效果,始终是右边的view变化,要么从小到大,要么从大到小
那么滑倒下一页:左边的view:map.get(position) ,右边的view : map.get(position+1) .
那么滑倒上一页:左边的view : map.get(position) , 右边的view : map.get(position+1) , 一样的,因为滑到上一页,position为当前页-1
好了,至此,我们分析了且解决了所有步骤。
3、代码
mainactivity
package com.example.zhy_jazzyviewpager; import android.app.activity; import android.os.bundle; import android.support.v4.view.pageradapter; import android.view.menu; import android.view.view; import android.view.viewgroup; import android.widget.imageview; import android.widget.imageview.scaletype; public class mainactivity extends activity { protected static final string tag = "mainactivity"; private int[] mimgids; private myjazzyviewpager mviewpager; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mimgids = new int[] { r.drawable.a, r.drawable.b, r.drawable.c, r.drawable.d }; mviewpager = (myjazzyviewpager) findviewbyid(r.id.id_viewpager); mviewpager.setadapter(new pageradapter() { @override public boolean isviewfromobject(view arg0, object arg1) { return arg0 == arg1; } @override public void destroyitem(viewgroup container, int position, object object) { container.removeview((view) object); } @override public object instantiateitem(viewgroup container, int position) { imageview imageview = new imageview(mainactivity.this); imageview.setimageresource(mimgids[position]); imageview.setscaletype(scaletype.center_crop); container.addview(imageview); mviewpager.setobjectforposition(imageview, position); return imageview; } @override public int getcount() { return mimgids.length; } }); } }
这个很常见的代码,就是初始化viewpager~~就没啥可说的了~~有一点需要注意:在instantiateitem方法,我们多调用了一个mviewpager.setobjectforposition(imageview, position);其实就是为了给我们的map存值
主要看自定义的viewpager
package com.example.zhy_jazzyviewpager; import java.util.hashmap; import java.util.linkedhashmap; import android.content.context; import android.support.v4.view.viewpager; import android.util.attributeset; import android.util.log; import android.view.view; import com.nineoldandroids.view.viewhelper; public class myjazzyviewpager extends viewpager { private float mtrans; private float mscale; /** * 最大的缩小比例 */ private static final float scale_max = 0.5f; private static final string tag = "myjazzyviewpager"; /** * 保存position与对于的view */ private hashmap<integer, view> mchildrenviews = new linkedhashmap<integer, view>(); /** * 滑动时左边的元素 */ private view mleft; /** * 滑动时右边的元素 */ private view mright; public myjazzyviewpager(context context, attributeset attrs) { super(context, attrs); } @override public void onpagescrolled(int position, float positionoffset, int positionoffsetpixels) { // log.e(tag, "position=" + position+", positionoffset = "+positionoffset+" ,positionoffsetpixels = " + positionoffsetpixels+" , currentpos = " + getcurrentitem()); //滑动特别小的距离时,我们认为没有动,可有可无的判断 float effectoffset = issmall(positionoffset) ? 0 : positionoffset; //获取左边的view mleft = findviewfromobject(position); //获取右边的view mright = findviewfromobject(position + 1); // 添加切换动画效果 animatestack(mleft, mright, effectoffset, positionoffsetpixels); super.onpagescrolled(position, positionoffset, positionoffsetpixels); } public void setobjectforposition(view view, int position) { mchildrenviews.put(position, view); } /** * 通过过位置获得对应的view * * @param position * @return */ public view findviewfromobject(int position) { return mchildrenviews.get(position); } private boolean issmall(float positionoffset) { return math.abs(positionoffset) < 0.0001; } protected void animatestack(view left, view right, float effectoffset, int positionoffsetpixels) { if (right != null) { /** * 缩小比例 如果手指从右到左的滑动(切换到后一个):0.0~1.0,即从一半到最大 * 如果手指从左到右的滑动(切换到前一个):1.0~0,即从最大到一半 */ mscale = (1 - scale_max) * effectoffset + scale_max; /** * x偏移量: 如果手指从右到左的滑动(切换到后一个):0-720 如果手指从左到右的滑动(切换到前一个):720-0 */ mtrans = -getwidth() - getpagemargin() + positionoffsetpixels; viewhelper.setscalex(right, mscale); viewhelper.setscaley(right, mscale); viewhelper.settranslationx(right, mtrans); } if (left != null) { left.bringtofront(); } } }
可以看到,核心代码都是onpagescrolled,我们通过findviewfromobject(position); findviewfromobject(position + 1);分别获取了左右两边的view,然后添加动画效果;当前这个例子添加了两个动画,一个是从0.5放大到1.0或者1.0缩小到0.5,没错由我们的positionoffset提供梯度的变化~~还有个平移的动画:下一页直接移动到当前屏幕(默认是在右边,可以注释这个效果,怎么运行看看),然后不断的通过positionoffsetpixels抵消原来默认移动时的位移,让用户感觉它就在原地放大缩小~~
好了,这样就实现了~~你可以随便写自己喜欢的动画效果,比如在默认上面加个淡入淡出或者神马,随便~~是不是很随意~~
我们的布局文件:
<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.example.zhy_jazzyviewpager.myjazzyviewpager android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/id_viewpager" /> </relativelayout>
4、jazzyviewpager的使用
其实上面的实现就是github上jazzyviewpager的源码,用法不用说了,就是我们的mainactivity,它内置了大概10来种效果,我们可以通过代码或者布局上面设置动画效果~~我们上面的例子效果,它叫做stack;
使用jazzviewpager的代码:其实基本一样~~最后也会贴上jazzyviewpager的源码的下载
mainactivity
package com.jfeinstein.jazzyviewpager; import com.jfeinstein.jazzyviewpager.jazzyviewpager.transitioneffect; import android.app.activity; import android.os.bundle; import android.support.v4.view.pageradapter; import android.view.view; import android.view.viewgroup; import android.widget.imageview; import android.widget.imageview.scaletype; public class mainactivity extends activity { protected static final string tag = "mainactivity"; private int[] mimgids; private jazzyviewpager mviewpager; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mimgids = new int[] { r.drawable.a, r.drawable.b, r.drawable.c, r.drawable.d }; mviewpager = (jazzyviewpager) findviewbyid(r.id.id_viewpager); //设置切换效果 mviewpager.settransitioneffect(transitioneffect.stack); mviewpager.setadapter(new pageradapter() { @override public boolean isviewfromobject(view arg0, object arg1) { return arg0 == arg1; } @override public void destroyitem(viewgroup container, int position, object object) { container.removeview((view) object); } @override public object instantiateitem(viewgroup container, int position) { imageview imageview = new imageview(mainactivity.this); imageview.setimageresource(mimgids[position]); imageview.setscaletype(scaletype.center_crop); container.addview(imageview); mviewpager.setobjectforposition(imageview, position); return imageview; } @override public int getcount() { return mimgids.length; } }); } }
与我们的代码唯一区别就是:
//设置切换效果
mviewpager.settransitioneffect(transitioneffect.stack);
它有12种可选的切换效果,其实就是写了12个切换的动画~~~
好了,最后附上一个我比较喜欢的效果:tablet
源码下载: viewpager图片切换
以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。
上一篇: Android开发中的数据库事务用法分析
下一篇: java使用Rxtx实现串口通信调试工具
推荐阅读
-
Android自定义ViewPager实现个性化的图片切换效果
-
Android实现闪屏及注册和登录界面之间的切换效果
-
Android自定义view实现阻尼效果的加载动画
-
Android 开发之BottomBar+ViewPager+Fragment实现炫酷的底部导航效果
-
Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码
-
最近较流行的效果 Android自定义View实现倾斜列表/图片
-
Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7)
-
Android自定义ImageView实现在图片上添加图层效果
-
Android UI设计系列之自定义SwitchButton开关实现类似IOS中UISwitch的动画效果(2)
-
Android中自定义View实现圆环等待及相关的音量调节效果