热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

又来一个库,WebView,RecyclerView多布局连贯滑动,Android小技巧

mScrollRange-getMeasuredHeight()-getPaddingTop()-getPaddingBottom();}***返回所有的非GONE子View*p

mScrollRange -= getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
}

/**
* 返回所有的非GONE子View
*/
private List getNonGoneChildren() {
List children &#61; new ArrayList<>();
int count &#61; getChildCount();
for (int i &#61; 0; i < count; i&#43;&#43;) {
View child &#61; getChildAt(i);
if (child.getVisibility() !&#61; GONE) {
children.add(child);
}
}
return children;
}

onMeasured的逻辑很简单&#xff0c;遍历测量子vew即可。

onLayout是把子view从上到下排列&#xff0c;就像一个垂直的LinearLayout一样。getNonGoneChildren()方法过滤掉隐藏的子view&#xff0c;隐藏的子view不参与布局。

上面的mScrollRange变量是布局自身可滑动的范围&#xff0c;它等于所有子view的高度减去布局自身的内容显示高度。在后面&#xff0c;它将用于计算布局的滑动偏移和边距限制。


拦截滑动事件

前面说过ConsecutiveScrollerLayout会拦截它的可滑动的子view的滑动事件&#xff0c;由自己来处理所有的滑动。下面是它拦截事件的实现。

&#64;Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() &#61;&#61; MotionEvent.ACTION_MOVE) {
// 需要拦截事件
if (isIntercept(ev)) {
return true;
}
}
return super.onInterceptTouchEvent(ev);
}

如果是滑动事件(ACTION_MOVE)&#xff0c;判断是否需要拦截事件&#xff0c;拦截则直接返回true&#xff0c;让事件交由ConsecutiveScrollerLayout的onTouchEvent方法处理。判断是否需要拦截的关键是isIntercept(ev)方法。

/**
* 判断是否需要拦截事件
*/
private boolean isIntercept(MotionEvent ev) {
// 根据触摸点获取当前触摸的子view
View target &#61; getTouchTarget((int) ev.getRawX(), (int) ev.getRawY());

if (target !&#61; null) {
// 判断子view是否允许父布局拦截事件
ViewGroup.LayoutParams lp &#61; target.getLayoutParams();
if (lp instanceof LayoutParams) {
if (!((LayoutParams) lp).isConsecutive) {
return false;
}
}

// 判断子view是否可以垂直滑动
if (ScrollUtils.canScrollVertically(target)) {
return true;
}
}

return false;
}

public class ScrollUtils {

static boolean canScrollVertically(View view) {
return canScrollVertically(view, 1) || canScrollVertically(view, -1);
}

static boolean canScrollVertically(View view, int direction) {
return view.canScrollVertically(direction);
}
}

判断是否需要拦截事件&#xff0c;主要是通过判断触摸的子view是否可以垂直滑动&#xff0c;如果可以垂直滑动&#xff0c;就拦截事件&#xff0c;让事件由ConsecutiveScrollerLayout自己处理。如果不是&#xff0c;就不拦截&#xff0c;一般不能滑动的view不会消费滑动事件&#xff0c;所以事件最终会由ConsecutiveScrollerLayout所消费。之所以不直接拦截&#xff0c;是为了能让子view尽可能的获得事件处理和分发给下面的view的机会。

这里有一个isConsecutive的LayoutParams属性&#xff0c;它是ConsecutiveScrollerLayout.LayoutParams的自定义属性&#xff0c;用于表示一个子view是否允许ConsecutiveScrollerLayout拦截它的滑动事件&#xff0c;默认为true。如果把它设置为false&#xff0c;父布局将不会拦截这个子view的事件&#xff0c;而是完全交由子view处理。这使得子view有了自己处理滑动事件的机会和分发事件的主动权。

这对于实现一些需要实现局部区域内滑动的特殊需求十分有用。我在GitHub中提供的demo和使用介绍中对isConsecutive有详细的说明&#xff0c;在这就不做过多介绍了。


滑动处理

把事件拦截后&#xff0c;就要在onTouchEvent方法中处理滑动事件。

&#64;Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点
mTouchY &#61; (int) ev.getY();
// 追踪滑动速度
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
break;
case MotionEvent.ACTION_MOVE:
if (mTouchY &#61;&#61; 0) {
mTouchY &#61; (int) ev.getY();
return true;
}
int y &#61; (int) ev.getY();
int dy &#61; y - mTouchY;
mTouchY &#61; y;
// 滑动布局
scrollBy(0, -dy);
// 追踪滑动速度
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchY &#61; 0;

if (mVelocityTracker !&#61; null) {
// 处理惯性滑动
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int yVelocity &#61; (int) mVelocityTracker.getYVelocity();
recycleVelocityTracker();
fling(-yVelocity);
}
break;
}
return true;
}

// 惯性滑动
private void fling(int velocityY) {
if (Math.abs(velocityY) > mMinimumVelocity) {
mScroller.fling(0, mOwnScrollY,
1, velocityY,
0, 0,
Integer.MIN_VALUE, Integer.MAX_VALUE);
invalidate();
}
}

&#64;Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int curY &#61; mScroller.getCurrY();
// 滑动布局
dispatchScroll(curY);
invalidate();
}
}

onTouchEvent方法的逻辑非常简单&#xff0c;就是根据手指的滑动距离通过view的scrollBy方法滑动布局内容&#xff0c;同时通过VelocityTracker追踪手指的滑动速度&#xff0c;使用Scroller配合computeScroll()方法实现惯性滑动。


滑动距离的分发

在处理惯性滑动是时候&#xff0c;我们调用了dispatchScroll()方法&#xff0c;这个方法是整个ConsecutiveScrollerLayout的核心&#xff0c;它决定了应该由谁来消费这次滑动&#xff0c;应该滑动那个布局。

其实ConsecutiveScrollerLayout的scrollBy()和scrollTo()方法最终都是调用它来处理滑动的分发的。

这里有个mOwnScrollY属性&#xff0c;是用于记录ConsecutiveScrollerLayout的整体滑动距离的&#xff0c;相当于View的mScrollY属性。

dispatchScroll()方法把滑动分成向上和向下两部分处理。让我们先看向上滑动部分的处理。

private void scrollUp(int offset) {
int scrollOffset &#61; 0;  // 消费的滑动记录
int remainder &#61; offset; // 未消费的滑动距离
do {
scrollOffset &#61; 0;
// 是否滑动到底部
if (!isScrollBottom()) {
// 找到当前显示的第一个View
View firstVisibleView &#61; findFirstVisibleView();
if (firstVisibleView !&#61; null) {
awakenScrollBars();
// 获取View滑动到自身底部的偏移量
int bottomOffset &#61; ScrollUtils.getScrollBottomOffset(firstVisibleView);
if (bottomOffset > 0) {
// 如果bottomOffset大于0&#xff0c;表示这个view还没有滑动到自身的底部&#xff0c;那么就由这个view来消费这次的滑动距离。
int childOldScrollY &#61; ScrollUtils.computeVerticalScrollOffset(firstVisibleView);
// 计算需要滑动的距离
scrollOffset &#61; Math.min(remainder, bottomOffset);
// 滑动子view
scrollChild(firstVisibleView, scrollOffset);
// 计算真正的滑动距离
scrollOffset &#61; ScrollUtils.computeVerticalScrollOffset(firstVisibleView) - childOldScrollY;
} else {
// 如果子view已经滑动到自身的底部&#xff0c;就由父布局消费滑动距离&#xff0c;直到把这个子view滑出屏幕
int selfOldScrollY &#61; getScrollY();
// 计算需要滑动的距离
scrollOffset &#61; Math.min(remainder,
firstVisibleView.getBottom() - getPaddingTop() - getScrollY());
// 滑动父布局
scrollSelf(getScrollY() &#43; scrollOffset);
// 计算真正的滑动距离
scrollOffset &#61; getScrollY() - selfOldScrollY;
}
// 计算消费的滑动距离&#xff0c;如果还没有消费完&#xff0c;就继续循环消费。
mOwnScrollY &#43;&#61; scrollOffset;
remainder &#61; remainder - scrollOffset;
}
}
} while (scrollOffset > 0 && remainder > 0);


最后

简历首选内推方式&#xff0c;速度快&#xff0c;效率高啊&#xff01;然后可以在拉钩&#xff0c;boss&#xff0c;脉脉&#xff0c;大街上看看。简历上写道熟悉什么技术就一定要去熟悉它&#xff0c;不然被问到不会很尴尬&#xff01;做过什么项目&#xff0c;即使项目体量不大&#xff0c;但也一定要熟悉实现原理&#xff01;不是你负责的部分&#xff0c;也可以看看同事是怎么实现的&#xff0c;换你来做你会怎么做&#xff1f;做过什么&#xff0c;会什么是广度问题&#xff0c;取决于项目内容。但做过什么&#xff0c;达到怎样一个境界&#xff0c;这是深度问题&#xff0c;和个人学习能力和解决问题的态度有关了。大公司看深度&#xff0c;小公司看广度。大公司面试你会的&#xff0c;小公司面试他们用到的你会不会&#xff0c;也就是岗位匹配度。

面试过程一定要有礼貌&#xff01;即使你觉得面试官不尊重你&#xff0c;经常打断你的讲解&#xff0c;或者你觉得他不如你&#xff0c;问的问题缺乏专业水平&#xff0c;你也一定要尊重他&#xff0c;谁叫现在是他选择你&#xff0c;等你拿到offer后就是你选择他了。

另外&#xff0c;描述问题一定要慢&#xff01;不要一下子讲一大堆&#xff0c;慢显得你沉稳、自信&#xff0c;而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide&#xff0c;所以会有个弊端&#xff0c;当我们在面试讲解很容易不知道某个方法怎么读&#xff0c;这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准&#xff0c;有些面试官不耐烦会说“你到底说的是哪个&#xff1f;”这时我们会容易乱了阵脚。正确的发音&#43;沉稳的描述&#43;好听的嗓音决对是一个加分项&#xff01;

最重要的是心态&#xff01;心态&#xff01;心态&#xff01;重要事情说三遍&#xff01;面试时间很短&#xff0c;在短时间内对方要摸清你的底子还是比较不现实的&#xff0c;所以&#xff0c;有时也是看眼缘&#xff0c;这还是个看脸的时代。

希望大家都能找到合适自己满意的工作&#xff01;
如果需要PDF版本可以在GitHub中自行领取&#xff01;


进阶学习视频

附上&#xff1a;我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 &#xff08;含BAT、小米、华为、美团、滴滴&#xff09;和我自己整理Android复习笔记&#xff08;包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。&#xff09;

[外链图片转存中…(img-c7siyZSt-1646484882168)]

附上&#xff1a;我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 &#xff08;含BAT、小米、华为、美团、滴滴&#xff09;和我自己整理Android复习笔记&#xff08;包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。&#xff09;

[外链图片转存中…(img-YxfmTA2T-1646484882170)]


推荐阅读
  • 本文介绍如何使用布局文件在Android应用中排列多行TextView和Button,使其占据屏幕的特定比例,并提供示例代码以帮助理解和实现。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 本文介绍如何在 Android 中通过代码模拟用户的点击和滑动操作,包括参数说明、事件生成及处理逻辑。详细解析了视图(View)对象、坐标偏移量以及不同类型的滑动方式。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了 com.facebook.drawee.view.SimpleDraweeView 中的 setScaleType 方法,提供了多个实际代码示例,并解释了其在不同场景下的应用。 ... [详细]
  • 本文介绍如何通过 JavaScript 实现一个基于鼠标坐标的 Tooltip 弹出层,详细解释了如何获取窗口和文档的尺寸及滚动位置,并优化了代码结构。 ... [详细]
  • 本文详细介绍了如何在Android 4.4及以上版本中配置WebView以实现内容的自动高度调整和屏幕适配,确保中文显示正常,并提供代码示例。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
author-avatar
呵呵哒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有