两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义Recylerview)
一、自定义CoordinatorLayout实现图片放大功能
本文是基于折叠布局实现的图片上拉滑动,下拉图片放大,松手放大的效果,先看下效果图。

实现原理:
1.使用CoordinatorLayout自带效果实现上滑。
2.重写CoordinatorLayout触摸事件,在分发之前判断当前是否是在最顶部,并且是下拉操作。
是:进行图片放大,平移下面布局;松手后执行图片回弹动画,恢复布局。
否:不处理事件,滑动事件自动交给下面的Nestscrollview
代码实现如下:
public class CustomCoordinatorLayout extends CoordinatorLayout {
private View mZoomView;
private int mZoomViewWidth;
private int mZoomViewHeight;
private float firstPosition;//记录第一次按下的位置
private boolean isScrolling;//是否正在缩放
private boolean isScrollDown;//是否下滑
private float mScrollRate = 0.6f;//缩放系数,缩放系数越大,变化的越大
private float mReplyRate = 0.3f;//回调系数,越大,回调越慢
private View mMoveView;
private View mMoveView2;
private int height,height2;
public CustomCoordinatorLayout(@NonNull Context context) {
super(context);
}
public CustomCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setmZoomView(View mZoomView) {
this.mZoomView = mZoomView;
}
public void setmMoveView(View mMoveView1,View mMoveView2) {
this.mMoveView = mMoveView1;
this.mMoveView2 = mMoveView2;
height = mMoveView.getMeasuredHeight();
height2 = mMoveView2.getMeasuredHeight();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int[] location = new int[2];
mZoomView.getLocationOnScreen(location);
int y = location[1];
if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {
mZoomViewWidth = mZoomView.getMeasuredWidth();
mZoomViewHeight = mZoomView.getMeasuredHeight();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
if(isScrollDown) break;
//手指离开后恢复图片
isScrolling = false;
replyImage();
break;
case MotionEvent.ACTION_MOVE:
if(y != 0) return super.dispatchTouchEvent(ev);
isScrollDown = false;
if (!isScrolling) {
if (getScrollY() == 0) {
firstPosition = ev.getY();// 滚动到顶部时记录位置,否则正常返回
} else {
break;
}
}
int distance = (int) ((ev.getY() - firstPosition) * mScrollRate); // 滚动距离乘以一个系数
if (distance < 0) { // 当前位置比记录位置要小,正常返回
isScrollDown = true;
break;
}
// 处理放大
isScrolling = true;
setZoom(distance);
return super.dispatchTouchEvent(ev);
}
return super.dispatchTouchEvent(ev);
}
private void scrollDown(float zoom) {
mMoveView2.setScrollY(-(int)(height2 * ((height2 + zoom) / height2)));
}
//回弹动画
private void replyImage() {
float distance = mZoomView.getMeasuredWidth() - mZoomViewWidth;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(distance, 0f).setDuration((long) (distance * mReplyRate));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setZoom((Float) animation.getAnimatedValue());
}
});
valueAnimator.start();
mMoveView.setScrollY(height);
mMoveView2.setScrollY(height2);
}
public void setZoom(float zoom) {
if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {
return;
}
ViewGroup.LayoutParams lp = mZoomView.getLayoutParams();
lp.width = (int) (mZoomViewWidth * ((mZoomViewWidth + zoom) / mZoomViewWidth));
lp.height = (int) (mZoomViewHeight * ((mZoomViewWidth + zoom) / mZoomViewWidth));
((MarginLayoutParams) lp).setMargins(-(lp.width - mZoomViewWidth) / 2, 0, 0, 0);
mZoomView.setLayoutParams(lp);
try {
CollapsingToolbarLayout parent = (CollapsingToolbarLayout) (mMoveView.getParent());
ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();
layoutParams.height = lp.height;
parent.setLayoutParams(layoutParams);
}catch (Exception e){
}
}
}
布局文件结构:
<?xml version="1.0" encoding="utf-8"?>
<com.ingtube.common.widget.CustomCoordinatorLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/cl_layout"> <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height = "wrap_content"
>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/csl_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
//要放大的图片
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/mine_iv_avatar"
android:layout_width="match_parent"
android:layout_height="230dp"
android:alpha="0.4"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
/> //平移布局一
<RelativeLayout
android:id="@+id/rl_layout"
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_gravity="bottom"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0"
> <LinearLayout
android:id="@+id/mine_ll_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="50dp"
android:gravity="center_vertical"> <TextView
android:id="@+id/mine_tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="@color/yt_color_white"
android:textSize="@dimen/yt_text_name"
android:textStyle="normal"
tools:text="节操君" /> <ImageView
android:id="@+id/iv_ensure_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="14dp"
android:src="@drawable/ic_ensure_icon" />
</LinearLayout> <TextView
android:id="@+id/mine_tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/mine_ll_name"
android:layout_alignStart="@id/mine_ll_name"
android:layout_marginTop="6dp"
android:alpha="0.6"
android:background="@drawable/shape_bg_gray_round_stroke"
android:paddingLeft="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:text="我的主页"
android:textColor="@color/yt_color_white"
android:textSize="@dimen/yt_text_t1" /> <View
android:id="@+id/v_personal_info_dot"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_alignTop="@+id/mine_tv_info"
android:layout_alignEnd="@+id/mine_tv_info"
android:background="@drawable/shape_red_dot" /> <TextView
android:id="@+id/tv_mine_user_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/mine_tv_info"
android:layout_marginStart="8dp"
android:layout_toEndOf="@+id/mine_tv_info"
android:alpha="0.6"
android:background="@drawable/shape_bg_gray_round_stroke"
android:paddingStart="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:text="编辑主页"
android:textColor="@color/yt_color_white"
android:textSize="@dimen/yt_text_t1" /> //代码省略
...
</RelativeLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior”> //平移布局二
<LinearLayout
android:id="@+id/ll_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
//代码省略
... </LinearLayout>
</androidx.core.widget.NestedScrollView> </com.ingtube.common.widget.CustomCoordinatorLayout>
使用就非常简单了,在代码中设置要放大的view以及需要平移的view就可以了。
private fun zoomImage() {
cl_layout.setmMoveView(rl_layout,ll_layout)
cl_layout.setmZoomView(mine_iv_avatar)
}
以上只是个例子用于实现特定布局的动画效果,实际应用可根据场景来自定义view进行操作。
二、自定义recylerView实现图片放大效果
实现效果如下:

实现原理:
重写recylerview的onTouchEvent方法,在顶部往下滑的时候,进行第一个item图片放大及布局下移操作。
好处:多布局中自带滑动,不用处理其他item的滑动,流畅程度100%
代码实现如下:
public class ZoomRecyclerView extends RecyclerView {
private View mZoomView;
private int mZoomViewWidth;
private int mZoomViewHeight;
private int mViewParentHeight;
private float mScrollRate = 0.3f;//缩放系数,缩放系数越大,变化的越大
private float mReplyRate = 0.3f;//回调系数,越大,回调越慢
// 记录首次按下位置
private float mFirstPosition = 0;
// 是否正在放大
private Boolean mScaling = false;
LinearLayoutManager mLinearLayoutManager ;
public ZoomRecyclerView( Context context) {
super(context);
}
public ZoomRecyclerView( Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZoomRecyclerView( Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setZoomView(SimpleDraweeView v, LinearLayoutManager linearLayoutManager) {
this.mZoomView = v;
mLinearLayoutManager = linearLayoutManager ;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(mZoomView !=null){
if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {
mZoomViewWidth = mZoomView.getMeasuredWidth();
mZoomViewHeight = mZoomView.getMeasuredHeight();
}
if(mViewParentHeight <= 0) {
try {
RelativeLayout parent = (RelativeLayout) mZoomView.getParent();
ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();
mViewParentHeight = layoutParams.height;
}catch (Exception e){}
}
//判断触摸事件
switch (event.getAction()) {
//触摸结束
case MotionEvent.ACTION_UP:
mScaling = false;
replyImage();
break;
//触摸中
case MotionEvent.ACTION_MOVE:
//判断是否正在放大 mScaling 的默认值为false
if (!mScaling) {
//当图片也就是第一个item完全可见的时候,记录触摸屏幕的位置
if (mLinearLayoutManager.findViewByPosition(mLinearLayoutManager.findFirstVisibleItemPosition()).getTop() == 0) {
//记录首次按下位置
mFirstPosition = event.getY();
} else {
break;
}
}
// 滚动距离乘以一个系数
int distance = (int) ((event.getY() - mFirstPosition) * mScrollRate);
if (distance < 0) {
break;
}
// 处理放大
mScaling = true;
setZoom(distance);
default:
break;
}
}
return super.onTouchEvent(event);
}
private void setZoom(float distance) {
if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {
return;
}
ViewGroup.LayoutParams lp = mZoomView.getLayoutParams();
lp.width = (int) (mZoomViewWidth * ((mZoomViewWidth + distance) / mZoomViewWidth));
lp.height = (int) (mZoomViewHeight * ((mZoomViewWidth + distance) / mZoomViewWidth));
mZoomView.setLayoutParams(lp);
try {
RelativeLayout parent = (RelativeLayout)mZoomView.getParent();
ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();
layoutParams.height = (int) (mViewParentHeight * ((mZoomViewWidth + distance) / mZoomViewWidth));
parent.setLayoutParams(layoutParams);
}catch (Exception e){
}
}
/**
* 图片回弹动画
*/
private void replyImage() {
float distance = mZoomView.getMeasuredWidth() - mZoomViewWidth;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(distance, 0f).setDuration((long) (distance * mReplyRate));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setZoom((Float) animation.getAnimatedValue());
}
});
valueAnimator.start();
}
布局很简单:
<?xml version="1.0" encoding="utf-8"?>
<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"
android:orientation="vertical"> <com.ingtube.common.widget.ZoomRecyclerView
android:id="@+id/rv_personal_info"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> </RelativeLayout>
使用也非常简单了。
实现recylerview的滑动监听,在布局为0的时候,设置图片放大及布局下移操作。
rv_personal_info.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
this@PersonalInfoActivity.scrollY = recyclerView.computeVerticalScrollOffset()
if (scrollY == 0 && pageItems.size != 0) {
rv_personal_info.setZoomView(personalPageHeadViewBinder!!.getZoomView(), rv_personal_info.layoutManager as? LinearLayoutManager)
}
}catch (e:Exception){
e.printStackTrace()
}
}
})
两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义Recylerview)的更多相关文章
- iOS开发-UITableView顶部图片下拉放大
关于顶部图片下拉放大,在用户展示的个人中心显示用户个人头像信息,设置UITableView的headerView实现,UITableView继承自UIScrollView,同样的设置UIScrollV ...
- Java使用poi生成Excel,生成两种表格下拉框
想要使用POI操作以xsl结尾的Excel,首先要下载poi相关的jar包,用到的jar有: poi-3.9.jar poi-ooxml-3.9.jar poi-ooxml-schemas-3.9.j ...
- Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果
前言 接着上一期Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件 的博客开始,同样,在开始前我们先来看一下目标效果: 下面上一下本章需要实现的效果图: 大家看到这个效果 ...
- WPF 下两种图片合成或加水印的方式(转载)
来源:http://www.cnblogs.com/lxblog/ 最近项目中应用多次应用了图片合成,为了今后方便特此记下. 在WPF下有两种图片合成的方式,一种还是用原来C#提供的GDI+方式,命名 ...
- IOS下拉放大图片
代码地址如下:http://www.demodashi.com/demo/11623.html 一.实现效果图 现在越来越多的APP中存在下拉放大图片的效果,今天贡献一下我的实现这种方法的原理,和我遇 ...
- html、css实现导航栏5种常用下拉效果
实现的效果:鼠标移入按钮时按钮中的内容就会出现,分别展示不同的出现效果.效果难点:不使用JavaScript,那这个效果的难点就是在于:hover伪类的掌控,以及考验对html的结构掌握. 1. ht ...
- android一个下拉放大库bug的解决过程及思考
android一个下拉放大库bug的解决过程及思考 起因 项目中要做一个下拉缩放图片的效果,搜索了下github上面,找到了两个方案. https://github.com/Frank-Zhu/Pul ...
- JQuery和ASP.NET分别实现级联下拉框效果
在学习JQuery之前知道下拉框的级联效果可以通过asp.net控件实现,现在学习了JQuery,知道了JQuery和select也能实现.我分别举两个小例子说明这两种方法如何实现. 1.用JQuer ...
- 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突
使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种冲突 如果你还在为处理滑动冲突而发愁,那么你需要静 ...
随机推荐
- 多测师讲解第一个月 _综合面试题_高级讲师肖sir
第一个月综合面试题 1. 冒烟测试是什么意思? 对主要的用例测试 2.你们公司的项目流程是什么? 3.你们公司的bug分几个级别? 4个 4.你对外键是怎么理解的? 你会使用外键吗?给一个表添加 ...
- 面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 编程学习,先铺宽度还是挖深度? 其实技术宽度与技术深度是相辅相成的,你能了解多少技术 ...
- 单调队列+线性dp题Watching Fireworks is Fun (CF372C)
一.Watching Fireworks is Fun(紫题) 题目:一个城镇有n个区域,从左到右1编号为n,每个区域之间距离1个单位距离节日中有m个烟火要放,给定放的地点ai,时间ti当时你在x,那 ...
- go读取excel表格数据
go读取excel表格数据 使用工具 github.com/Luxurioust/excelize 百度到的都是使用这个 实际上已经改名了 github.com/360EntSecGroup-Skyl ...
- Vue中封装axios组件实例
首先要创建一个网络模块network文件夹 里面要写封装好的几个组件 在config.js里面这样写 在index.js要这样写 core.js文件里面内容如下 然后要在main.js文件里面要设置 ...
- ThreadLocal与Thread与Runable之间的关系
ThreadLocal继承Object,相当于没继承任何特殊的. ThreadLocal没有实现任何接口. ThreadLocal并不是一个Thread,而是Thread的局部变量
- 联赛模拟测试24 D. 你相信引力吗 单调栈
题目描述 分析 因为跨过最大值的区间一定是合法的,所以我们人为地把最大值放在最左边 我们要统计的就是在最大值右边单调不降的序列,可以用单调栈维护 需要特殊处理相同的情况 代码 #include< ...
- this.getResolve is not a function VUE中使用sass
1. 安装以下依赖 npm install node-sass --save-dev //安装node-sass npm install sass-loader --save-dev //安装sass ...
- 一起学Vue:CRUD(增删改查)
目标 使用Vue构建一个非常简单CRUD应用程序,以便您更好地了解它的工作方式. 效果页面 比如我们要实现这样列表.新增.编辑三个页面: 列表页面 新增页面 编辑页面 我们把这些用户信息保存到Todo ...
- 【总结】rabbitmq
一.rabbitmq基础 1.简介 RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的.Erlang语言在数据交互方面性能优秀,有着和原生Socket一样的延迟, ...