使用RecyclerView实现一个画廊效果,主要是使用support库中最新加入的PagerSnapHelper类,通过计算滑动偏移来计算scale的值。

基本实现

首先需要为RecyclerView添加一个滚动监听,然后为RecyclerView的第一个与最后一个itemView添加一个ItemDecoration,使位于第一个与最后一个itemView的位置居中对齐。


public void attachToRecyclerView(final RecyclerView recyclerView) {
this.recyclerView = recyclerView;
snapHelper.attachToRecyclerView(recyclerView);
recyclerView.addOnScrollListener(scrollListener);
recyclerView.addItemDecoration(new ScalableCardItemDecoration());
recyclerView.post(new Runnable() {
@Override
public void run() {
pageScrolled();
}
});
}

ScalableCardItemDecoration用于计算itemView左右剩余空间,然后为第一个itemView与最后一个itemView添加偏移量。


private static class ScalableCardItemDecoration extends RecyclerView.ItemDecoration { @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
int position = holder.getAdapterPosition() == RecyclerView.NO_POSITION ? holder.getOldPosition() : holder.getAdapterPosition();
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int itemCount = layoutManager.getItemCount(); if(position != 0 && position != itemCount - 1){
return;
} int peekWidth = getPeekWidth(parent, view);
boolean isVertical = layoutManager.canScrollVertically();
//移除item时adapter position为-1。 if (isVertical) {
if (position == 0) {
outRect.set(0, peekWidth, 0, 0);
} else if (position == itemCount - 1) {
outRect.set(0, 0, 0, peekWidth);
} else {
outRect.set(0, 0, 0, 0);
}
} else {
if (position == 0) {
outRect.set(peekWidth, 0, 0, 0);
} else if (position == itemCount - 1) {
outRect.set(0, 0, peekWidth, 0);
} else {
outRect.set(0, 0, 0, 0);
}
}
}
}

在为item添加ItemDecoration时,需要计算两边的空间。这里需要手动测量itemView的宽高, 然后计算第一个itemView左边的偏移与最后一个itemView右边的偏移。这里碰到个问题,如果LayoutMangaer的方面是垂直或水平的且RecyclerView对应的的高度或宽度设为wrap_content时,这里计算的值不会正确。

 public static int getPeekWidth(RecyclerView recyclerView, View itemView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
boolean isVertical = layoutManager.canScrollVertically();
int position = recyclerView.getChildAdapterPosition(itemView);
//TODO RecyclerView使用wrap_content时,获取的宽度可能会是0。
int parentWidth = recyclerView.getMeasuredWidth();
int parentHeight = recyclerView.getMeasuredHeight(); //有时会拿到0
parentWidth = parentWidth == 0 ? recyclerView.getWidth() : parentWidth;
parentHeight = parentHeight == 0 ? recyclerView.getHeight() : parentHeight;
int parentEnd = isVertical ? parentHeight : parentWidth;
int parentCenter = parentEnd / 2; int itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth(); if (itemSize == 0) { ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
int widthMeasureSpec =
RecyclerView.LayoutManager.getChildMeasureSpec(parentWidth,
layoutManager.getWidthMode(),
recyclerView.getPaddingLeft() + recyclerView.getPaddingRight(),
layoutParams.width, layoutManager.canScrollHorizontally()); int heightMeasureSpec =
RecyclerView.LayoutManager.getChildMeasureSpec(parentHeight,
layoutManager.getHeightMode(),
recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(),
layoutParams.height, layoutManager.canScrollVertically()); itemView.measure(widthMeasureSpec, heightMeasureSpec);
itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth();
} /*
计算ItemDecoration的大小,确保插入的大小正好使view的start + itemSize / 2等于parentCenter。
*/
int startOffset = parentCenter - itemSize / 2;
int endOffset = parentEnd - (startOffset + itemSize); return position == 0 ? startOffset : endOffset;
}

添加完ItemDecoration后,我们需要在RecyclerView每次滚动的时候计算左、中、右3个itemView的缩放比例。


private void pageScrolled() {
if (recyclerView == null || recyclerView.getChildCount() == 0)
return; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); View snapingView = snapHelper.findSnapView(layoutManager);
int snapingViewPosition = recyclerView.getChildAdapterPosition(snapingView);
View leftSnapingView = layoutManager.findViewByPosition(snapingViewPosition - 1);
View rightSnapingView = layoutManager.findViewByPosition(snapingViewPosition + 1); float leftSnapingOffset = calculateOffset(recyclerView, leftSnapingView);
float rightSnapingOffset = calculateOffset(recyclerView, rightSnapingView);
float currentSnapingOffset = calculateOffset(recyclerView, snapingView); if (snapingView != null) {
snapingView.setScaleX(currentSnapingOffset);
snapingView.setScaleY(currentSnapingOffset);
} if (leftSnapingView != null) {
leftSnapingView.setScaleX(leftSnapingOffset);
leftSnapingView.setScaleY(leftSnapingOffset);
} if (rightSnapingView != null) {
rightSnapingView.setScaleX(rightSnapingOffset);
rightSnapingView.setScaleY(rightSnapingOffset);
} if(snapingView != null && currentSnapingOffset >= 1){
OnPageChangeListener listener = pageChangeListenerRef != null ? pageChangeListenerRef.get(): null;
if(listener != null)
listener.onPageSelected(snapingViewPosition);
} Log.d(TAG, String.format("left: %f, right: %f, current: %f", leftSnapingOffset, rightSnapingOffset, currentSnapingOffset));
}

计算缩放比例时是根据左、中、右三个itemView的中间点与RecyclerView中间点的距离来计算的。


private float calculateOffset(RecyclerView recyclerView, View view) {
if (view == null)
return -1; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
boolean isVertical = layoutManager.canScrollVertically();
int viewStart = isVertical ? view.getTop() : view.getLeft();
int viewEnd = isVertical ? view.getBottom() : view.getRight();
int centerX = isVertical ? recyclerView.getHeight() / 2 : recyclerView.getWidth() / 2;
int childCenter = (viewStart + viewEnd) / 2;
int distance = Math.abs(childCenter - centerX); if (distance > centerX)
return STAY_SCALE; float offset = 1.f - (distance / (float) centerX);
return (1.f - STAY_SCALE) * offset + STAY_SCALE;
}

项目地址: https://github.com/yjwfn/recyclerview-gallery



《架构文摘》每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性 能、高稳定)、大数据、机器学习等各个热门领域。

RecyclerView实现Gallery画廊效果的更多相关文章

  1. RecyclerView 实现gallery画廊效果

    1.RecyclerView的基本用法 首先主Activity的布局文件: [html] view plaincopy <RelativeLayout xmlns:android="h ...

  2. RecyclerView实现终极画廊效果 中间突出并且压住两侧布局

    先给大家上个gif 要不然下面很枯燥 忘记原来在哪里看到了..... 这个效果我找了NNNNNN长时间,,,我认为凭我现在的能力 写出来需要好久 所以 退而求其次找大神写好的... 你们不要小看了这个 ...

  3. Swift - 使用CollectionView实现图片Gallery画廊效果(左右滑动浏览图片)

    1,效果图 (1)图片从左至右横向排列(只有一行),通过手指拖动可以前后浏览图片. (2)视图滚动时,每张图片根据其与屏幕中心距离的不同,显示尺寸也会相应地变化.越靠近屏幕中心尺寸就越大,远离屏幕中心 ...

  4. 第31讲 UI组件之 Gallery画廊控件

    第31讲 UI组件之 Gallery画廊控件 1.Gallery的简介 Gallery(画廊)是一个锁定中心条目并且拥有水平滚动列表的视图,一般用来浏览图片,并且可以响应事件显示信息.Gallery只 ...

  5. 仿百度壁纸客户端(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸客户端(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸客户端(一)--主框架搭建,自定义Tab + ViewPager + Fragment 仿百度 ...

  6. 使用RecyclerView打造Gallery

    RecyclerView概述 RecyclerView是谷歌推出的用于向大型数据集提供有限窗口的灵活视图.可以通过导入support-v7对其进行使用. 据官方的介绍,该控件用于在有限的窗口中展示大量 ...

  7. Android画廊效果

    Android画廊效果 前言:Gallery是一个内部元素控件,可以水平滚动,并且可以把当前选择的子元素定位在它中心的布局组件:画廊Gallery一般用来显示可左右移动图片的列表(具体请看实例). 效 ...

  8. 仿百度壁纸client(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸client(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸client(一)--主框架搭建,自己定义Tab + ViewPager + Fragm ...

  9. UI组件之AdapterView及其子类(四)Gallery画廊控件使用

    听说 Gallery如今已经不使用了,API使用ViewPaper取代了,以后再学专研ViewPaper吧如今说说Gallery画廊,就是不停显示图片的意思 Gallery是用来水平滚动的显示一系列项 ...

随机推荐

  1. 王某人从0开始学习lorawan的笔记_0

    最近老板想做lorawan的项目,交给我了,我也应承下来了,但是!!!我TM连lorawan是啥子我都不知道啊啊啊啊啊! 真希望我女朋友可以看穿我的倔强,给我1千万,让我专心当舔狗,等等,我的女朋友? ...

  2. MySql定时器,亲测可用

    1. 查看数据库的event功能是否开启,在MySql中event默认是关闭的,需要查看并且要确保event处于开启状态 sql:show VARIABLES LIKE '%sche%'; 如果eve ...

  3. Leetcode之回溯法专题-90. 子集 II(Subsets II)

    Leetcode之回溯法专题-90. 子集 II(Subsets II) 给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入 ...

  4. ES6之模块化导入导出

    1.概述 在js的历史上一直没有模块(module)体系,无法将一个大程序拆分成相互依赖的小文件,再用简单的方法拼装起来,这对开发大型的.复杂的项目形成了巨大障碍. 在 ES6 之前,社区制定了一些模 ...

  5. HelloDjango 第 13 篇:分类、归档和标签页

    作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 侧边栏已经正确地显示了最新文章列表.归档.分类.标签等信息.现在来完善归档.分类和标签 ...

  6. TK图形界面

    import tkinter 1.使用tkinter模块前 一般先要建立一个tkinter的对象     例: window = tkinter.TK()   2.建立完对象设置好窗口属性以及所有功能 ...

  7. [python]变量和赋值

    1. python的变量名以字母开头,包含字母.数字.下划线. 2. python是动态类型语言,即不需要预先声明变量的类型.变量的类型和值在赋值的时候被初始化. 变量赋值通过等号来执行. 代码: c ...

  8. Hexo博客Next v7.X 主题升级,美化警示录

    本文转载于:Hexo博客Next v7.X 主题升级,美化警示录丨奥怪的小栈 前言 经历了好几天(懒癌晚期懒得数了)的与主题升级斗争后,我终于完成基本上完成了next主题的升级!升到了V7.3!哈哈哈 ...

  9. 【Offer】[4] 【二维数组中的查找】

    题目描述 思路分析 Java代码 代码链接 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数 ...

  10. 试试 IEnumerable 的另外 6 个小例子

    IEnumerable 接口是 C# 开发过程中非常重要的接口,对于其特性和用法的了解是十分必要的.本文将通过6个小例子,来熟悉一下其简单的用法. <!-- more --> 阅读建议 在 ...