百行代码打造高级联动特效

2024-02-29 08:08

本文主要是介绍百行代码打造高级联动特效,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前两天突然看到一个联动效果蛮不错的,虽然不知道具体什么地方会用到。不过也随手鲁了一个。效果如下图:
这里写图片描述

效果是不是挺好玩的~~。那么让我们接下来一步步的分析一下。

思路

首先,让我们想象一下如何实现?自定义view?自定义layout?还是什么?首先这是多层布局的嵌套。肯定会发生的就是事件拦截和分发。我们想像一下Coordinatorlayout+Appbarlayout+CollapsingToolbarLayout和我们现在的效果是不是差不多?只是上者是一个这个是多个而已。于是我的想法就是鲁一个behavior来实现此效果。

自定义layout

首先我们把需要的title和Recyclerview放入一个自定义布局里面。这是为了简单我们的代码。所以把功能放到自定义layout里面。代码如下:

 private void initial(Context context, AttributeSet attrs) {LayoutInflater.from(context).inflate(R.layout.cardlayout, this);TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SWCardLayout);TextView title = (TextView) findViewById(R.id.title);title.setText(array.getText(R.styleable.SWCardLayout_text));title.setBackgroundColor(array.getColor(R.styleable.SWCardLayout_backgroundcolor,Color.BLACK));RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);recyclerView.setLayoutManager(new LinearLayoutManager(context));NumberAdapter numberAdapter = new NumberAdapter(context);recyclerView.setAdapter(numberAdapter);array.recycle();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {if (w != oldw || h != oldh) {titleheight = findViewById(R.id.title).getMeasuredHeight();}}public int getTitleheight() {return titleheight;}

这边代码我就不多说了。返回title的高度为了后面的计算。

随手鲁一个behavior

我们先自定义一个behavior放入我们刚刚写的自定义layout。代码如下:


public class CardBehavior extends Behavior<SWCardLayout> {private int childheight = 0;public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,View directTargetChild, View target, int nestedScrollAxes) {return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && directTargetChild == child;}
}

至于nestedscrolling的这个方法我就不多少了。前面有几篇讲到这个的。
现在我们需要思考的是如何摆放他们的位置。其实behavior都帮我们处理好了。那就是onLayoutChild和OnmesureChild。控制他们的宽高和布局显示都在这2个方法里面。接下来我们看下这块的代码如何去写:

//控制子控件的onlayout和onmesure@Overridepublic boolean onLayoutChild(CoordinatorLayout parent, SWCardLayout child, int layoutDirection) {//按照默认的情况控制,会导致child重叠parent.onLayoutChild(child, layoutDirection);//控制顶部的偏移量(上一个距离顶部的边距+自身title高度)SWCardLayout frontChild = getFrontChild(parent, child);if (frontChild != null) {int offset = frontChild.getTop() + frontChild.getTitleheight();child.offsetTopAndBottom(offset);}childheight = child.getTop();return true;}@Overridepublic boolean onMeasureChild(CoordinatorLayout parent, SWCardLayout child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {//防止布局的重绘int offset = getChildOffset(parent, child);int height = View.MeasureSpec.getSize(parentHeightMeasureSpec) - offset;child.measure(parentWidthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));return true;}

我们来分析下,先从onmesure说起。我们需要得到他的偏移量,也就是上一个的title的高度。如果是若干个布局,那就是若干的title的高度。计算title的偏移量的代码很简单,就是叠加title的高度:

    private int getChildOffset(CoordinatorLayout parent, SWCardLayout child) {int offset = 0;for (int i = 0; i < parent.getChildCount(); i++) {View view = parent.getChildAt(i);if (view != child) {if (view instanceof SWCardLayout) {offset += ((SWCardLayout) view).getTitleheight();}}}return offset;}

得到它的偏移量之后我们直接通过子view的onmesure布局扔进去。所以这个不是很难。

现在我们继续看onlayoutchild方法。我注释也写的比较明确了。如果单独通过layout放置的话,child会重叠在一起。那么计算方法就是计算前一个child顶部的距离加上他自身的title的高度。那么我们来看看如何得到前一个child。其他代码很简单,遍历父view就可以。代码如下:

 //得到前一个childprivate SWCardLayout getFrontChild(CoordinatorLayout parent, SWCardLayout child) {int index = parent.indexOfChild(child);for (int i = index - 1; i >= 0; i--) {View view = parent.getChildAt(i);if (view instanceof SWCardLayout) {return (SWCardLayout) view;}}return null;}

我们先来看下效果。
这里写图片描述

我们会发现现在是滚动不了的。那么既然显示都显示出来 了。还怕他滚不了。看老夫随手一段代码分分钟让他滚起来。

那么滚动的处理在那边呢。当然是在onnestprescroll里面。nestedscrolling的一个方法里面处理。这个我前面有介绍过。不了解nestedscrolling可以先翻翻之前的文章。

那么我们应该怎么处理呢?还是老套路,计算偏移量。我们需要先处理自己本身的滑动,在处理其他的滑动来实现联动效果。具体代码如下:

  //为了处理联动public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,View target, int dx, int dy, int[] consumed) {//先处理自己的int min = childheight;int max = childheight + child.getHeight() - child.getTitleheight();int top = child.getTop();int offset = Math.min(Math.max(top - dy, min), max) - top;child.offsetTopAndBottom(offset);consumed[1] = -offset;Log.i("----------", "onNestedPreScroll: " + consumed[1]);//再处理其他的if (consumed[1] == 0) {return;} else if (consumed[1] > 0) {//上滑SWCardLayout current = child;SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(cardLayout, current);cardLayout.offsetTopAndBottom(-layoutoffset);current = cardLayout;cardLayout = getFrontChild(coordinatorLayout, current);}} else if (consumed[1] < 0) {//下滑SWCardLayout current = child;SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(current, cardLayout);cardLayout.offsetTopAndBottom(layoutoffset);current = cardLayout;cardLayout = getNextChild(coordinatorLayout, current);}}}

上滑的时候我们需要得到前一个的swcardlayout带着他滚动,下滑的时候我们需要得到下一个child进行滚动,得到下一个child的方法其实他之前得到前一个的方法是差不多的。代码如下:

private SWCardLayout getNextChild(CoordinatorLayout parent, SWCardLayout child) {int index = parent.indexOfChild(child);for (int i = index + 1; i < parent.getChildCount(); i++) {View view = parent.getChildAt(i);if (view instanceof SWCardLayout) {return (SWCardLayout) view;}}return null;}

那么就这么写完了。我们先来看下效果:
这里写图片描述

额。。。似乎翻车了。不过就看着图,我们知道应该是偏移量的问题搞错了。我们的偏移量必须要大于0应该。那么我们修改下代码。改成如下的:

else if (consumed[1] > 0) {//上滑SWCardLayout current = child;SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(cardLayout, current);if (layoutoffset > 0) {cardLayout.offsetTopAndBottom(-layoutoffset);}current = cardLayout;cardLayout = getFrontChild(coordinatorLayout, current);}} else if (consumed[1] < 0) {//下滑SWCardLayout current = child;SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(current, cardLayout);if (layoutoffset > 0) {cardLayout.offsetTopAndBottom(layoutoffset);}current = cardLayout;cardLayout = getNextChild(coordinatorLayout, current);}}

判断下偏移量的值在做child的offset设置。我们在来看一下效果。
这里写图片描述

代码

一个成功的联动效果就这么出来了。最后我放出自定义behavior的整体代码:


public class CardBehavior extends Behavior<SWCardLayout> {private int childheight = 0;public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,View directTargetChild, View target, int nestedScrollAxes) {return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && directTargetChild == child;}//为了处理联动public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, SWCardLayout child,View target, int dx, int dy, int[] consumed) {//先处理自己的int min = childheight;int max = childheight + child.getHeight() - child.getTitleheight();int top = child.getTop();int offset = Math.min(Math.max(top - dy, min), max) - top;child.offsetTopAndBottom(offset);consumed[1] = -offset;Log.i("----------", "onNestedPreScroll: " + consumed[1]);//再处理其他的if (consumed[1] == 0) {return;} else if (consumed[1] > 0) {//上滑SWCardLayout current = child;SWCardLayout cardLayout = getFrontChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(cardLayout, current);if (layoutoffset > 0) {cardLayout.offsetTopAndBottom(-layoutoffset);}current = cardLayout;cardLayout = getFrontChild(coordinatorLayout, current);}} else if (consumed[1] < 0) {//下滑SWCardLayout current = child;SWCardLayout cardLayout = getNextChild(coordinatorLayout, child);while (cardLayout != null) {int layoutoffset = getHeightOffset(current, cardLayout);if (layoutoffset > 0) {cardLayout.offsetTopAndBottom(layoutoffset);}current = cardLayout;cardLayout = getNextChild(coordinatorLayout, current);}}}private SWCardLayout getNextChild(CoordinatorLayout parent, SWCardLayout child) {int index = parent.indexOfChild(child);for (int i = index + 1; i < parent.getChildCount(); i++) {View view = parent.getChildAt(i);if (view instanceof SWCardLayout) {return (SWCardLayout) view;}}return null;}private int getHeightOffset(SWCardLayout top, SWCardLayout bottom) {return top.getTop() + top.getTitleheight() - bottom.getTop();}//控制子控件的onlayout和onmesure@Overridepublic boolean onLayoutChild(CoordinatorLayout parent, SWCardLayout child, int layoutDirection) {//按照默认的情况控制,会导致child重叠parent.onLayoutChild(child, layoutDirection);//控制顶部的偏移量(上一个距离顶部的边距+自身title高度)SWCardLayout frontChild = getFrontChild(parent, child);if (frontChild != null) {int offset = frontChild.getTop() + frontChild.getTitleheight();child.offsetTopAndBottom(offset);}childheight = child.getTop();return true;}//得到前一个childprivate SWCardLayout getFrontChild(CoordinatorLayout parent, SWCardLayout child) {int index = parent.indexOfChild(child);for (int i = index - 1; i >= 0; i--) {View view = parent.getChildAt(i);if (view instanceof SWCardLayout) {return (SWCardLayout) view;}}return null;}@Overridepublic boolean onMeasureChild(CoordinatorLayout parent, SWCardLayout child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {//防止布局的重绘int offset = getChildOffset(parent, child);int height = View.MeasureSpec.getSize(parentHeightMeasureSpec) - offset;child.measure(parentWidthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));return true;}private int getChildOffset(CoordinatorLayout parent, SWCardLayout child) {int offset = 0;for (int i = 0; i < parent.getChildCount(); i++) {View view = parent.getChildAt(i);if (view != child) {if (view instanceof SWCardLayout) {offset += ((SWCardLayout) view).getTitleheight();}}}return offset;}
}

当然那个SWCardLayout你们可以自己进行封装。想怎么玩怎么玩~。就是这么6。

这篇关于百行代码打造高级联动特效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/758090

相关文章

Python从零打造高安全密码管理器

《Python从零打造高安全密码管理器》在数字化时代,每人平均需要管理近百个账号密码,本文将带大家深入剖析一个基于Python的高安全性密码管理器实现方案,感兴趣的小伙伴可以参考一下... 目录一、前言:为什么我们需要专属密码管理器二、系统架构设计2.1 安全加密体系2.2 密码强度策略三、核心功能实现详解

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

基于Python打造一个可视化FTP服务器

《基于Python打造一个可视化FTP服务器》在日常办公和团队协作中,文件共享是一个不可或缺的需求,所以本文将使用Python+Tkinter+pyftpdlib开发一款可视化FTP服务器,有需要的小... 目录1. 概述2. 功能介绍3. 如何使用4. 代码解析5. 运行效果6.相关源码7. 总结与展望1

前端高级CSS用法示例详解

《前端高级CSS用法示例详解》在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交互和动态效果的关键技术之一,随着前端技术的不断发展,CSS的用法也日益丰富和高级,本文将深... 前端高级css用法在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN