欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-06 23:05:32
画廊在很多的app设计中都有,如下图所示: 该例子是我没事的时候写的一个小项目,具体源码地址请访问https://github.com/alexsmille/yi...

画廊在很多的app设计中都有,如下图所示:

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

该例子是我没事的时候写的一个小项目,具体源码地址请访问https://github.com/alexsmille/yingmi

该画廊类似封面的效果,滑到中间的图片会慢慢变大,离开的view会慢慢的缩小,同时可设置滑动监听和点击监听。

网上有很多例子都是通过gallery实现的,而上例的实现是通过viewpager实现,解决了性能优化的问题,今天特此把它抽出来,封装一下,以便以后的方便使用。最终实现的效果如下:

Android利用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);
 }
 }

点击事件的设置

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

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