当前位置: 移动技术网 > 移动技术>移动开发>Android > Android利用ViewPager实现可滑动放大缩小画廊效果

Android利用ViewPager实现可滑动放大缩小画廊效果

2019年07月24日  | 移动技术网移动技术  | 我要评论

画廊在很多的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);
 }
 }

点击事件的设置

直接对数据源循环设置监听即可

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网