使用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. Leetcode之深度优先搜索(DFS)专题-1123. 最深叶节点的最近公共祖先(Lowest Common Ancestor of Deepest Leaves)

    Leetcode之深度优先搜索(DFS)专题-1123. 最深叶节点的最近公共祖先(Lowest Common Ancestor of Deepest Leaves) 深度优先搜索的解题详细介绍,点击 ...

  2. Codeforces 735D Taxes(简单数论)

    题目链接 http://codeforces.com/problemset/problem/735/D 题意:一个人的收入为n他要交的税是n的最大除数,他为了少缴税将n分成k个数n1,n2,n2... ...

  3. 【Redis】基础学习概览【汇总】

    一.概述 1.1 简介 1.2 Redis单线程好处 1.3 单线程弊端 1.4 Redis应用场景 二.安装.开启以及关闭 三.Redis基本数据类型 四.SpringBoot整合Redis 五.R ...

  4. SpringCloud学习笔记(1):Eureka注册中心

    简介 Eureka是Netflix开源的基于rest的服务治理方案,分为Server端和Client端,Server端为注册中心,其他微服务通过Client端连接Server端进行服务的注册和发现. ...

  5. QRowTable表格控件(四)-效率优化之-优化数据源

    目录 一.开心一刻 二.问题分析 三.重写数据源 1.自己存储数据 2.重写data接口 四.比较 五.相关文章 原文链接:QRowTable表格控件(四)-效率优化之-优化数据源 一.开心一刻 一程 ...

  6. 基于Selenium+Python的web自动化测试框架

    一.什么是Selenium? Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案.Selenium主要包括三部分:Selenium IDE.S ...

  7. Map四种获取key和value值的方法,以及对map中的元素排序(转)

    获取map的值主要有四种方法,这四种方法又分为两类,一类是调用map.keySet()方法来获取key和value的值,另一类则是通过map.entrySet()方法来取值,两者的区别在于,前者主要是 ...

  8. Tomcat9控制台中文乱码的解决方案

    1.网上大部分都是这种方法 注释掉 tomcat 9 安装目录下的conf里的 logging.properties 找到 java.util.logging.ConsoleHandler.encod ...

  9. Linux执行后台work相关

    Linux的后台运行.关闭.查看后台任务 & ctrl+z jobs fg bg kill nohup setsid disown screen 1.& 加在命令的最后,可以把命令放到 ...

  10. Centos7 C++ 安装使用googletest单元测试

    废话不多说,直接开始吧. 环境说明 系统环境:centos7.0 g++ 版本: g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) 查看方法: g++ -vers ...