oschina源码分析之侧滑菜单界面之可以拖动的ScrollView

2023-11-13

先上源码:

package net.oschina.app.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
 * 可以拖动的ScrollView
 *
 */
public class CustomerScrollView extends ScrollView {
    private static final String TAG = "CustomerScrollView";
    private static final int size = 4;
    private View inner;
    private float y;
    private Rect normal = new Rect();

    public CustomerScrollView(Context context) {
        super(context);
    }

    public CustomerScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        if (getChildCount() > 0) {
            inner = getChildAt(0);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (inner == null) {
            return super.onTouchEvent(ev);
        } else {
            commOnTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }

    public void commOnTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            y = ev.getY();
            break;
        case MotionEvent.ACTION_UP:
            if (isNeedAnimation()) {
                // Log.v("mlguitar", "will up and animation");
                animation();
            }
            break;
        case MotionEvent.ACTION_MOVE:
            final float preY = y;
            float nowY = ev.getY();
            /**
             * size=4 表示 拖动的距离为屏幕的高度的1/4
             */
            int deltaY = (int) (preY - nowY) / size;
            Log.i(TAG, "commOnTouchEvent: deltay="+ deltaY +"preY" + preY +"nowY" + nowY );
            // 滚动
            // scrollBy(0, deltaY);

            y = nowY;
            if (isNeedMove()) {
                Log.i(TAG, "commOnTouchEvent:  normal.isEmpty:"  + normal.isEmpty());
                if (normal.isEmpty()) {
                    Log.i(TAG, "commOnTouchEvent: inner:" + inner.getLeft() + "," + inner.getTop() + "," + inner.getRight() + "," + inner.getBottom());

                    normal.set(inner.getLeft(), inner.getTop(),
                            inner.getRight(), inner.getBottom());
                    return;
                }
                int yy = inner.getTop() - deltaY;
                Log.i(TAG, "commOnTouchEvent: new layout:" + inner.getLeft() + "," + yy + "," + inner.getRight() + "," + (inner.getBottom() - deltaY));
                // 移动布局
                inner.layout(inner.getLeft(), yy, inner.getRight(),
                        inner.getBottom() - deltaY);
            }
            break;
        default:
            break;
        }
    }

    public void animation() {
        TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),
                normal.top);
        ta.setDuration(200);

        Log.i(TAG, "animation start " + "(0,0," + inner.getTop() +"," + normal.top + ")");


        inner.startAnimation(ta);
        inner.layout(normal.left, normal.top, normal.right, normal.bottom);
        normal.setEmpty();
    }

    public boolean isNeedAnimation() {
        Log.i(TAG, "isNeedAnimation: " + !normal.isEmpty());
        return !normal.isEmpty();
    }

    public boolean isNeedMove() {
        int offset = inner.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        Log.i(TAG, "isNeedMove: " + "scrollY=" + scrollY + "offset=" + offset);
        if (scrollY == 0 || scrollY == offset) {
            return true;
        }
        return false;
    }

}
  • 先看isNeedMove()函数:

    1. int offset = inner.getMeasuredHeight() - getHeight();

    前者一般与后者相等,只有当view超出屏幕后前者才会比后者大,getMeasuredHeight() 等于 getHeight()加上屏幕之外没有显示的大小。

    1. int scrollY = getScrollY();

    看下官方对这个函数的介绍:The top edge of the displayed part of your view, in pixels. 返回的是view的顶端超过屏幕部分的大小。

因此,这个函数的判断条件其实就是view在屏幕最上面或者最下面这两种情况才需要移动。

  • 再看移动事件(commOnTouchEvent):
    1. 点击获取初始位置,抬起判断是否需要移动,主要看一下移动这个事件(MotionEvent.ACTION_MOVE)。
    2. 首先根据移动的Y和初始的Y得到移动的距离:int deltaY = (int) (preY - nowY) / size;
    3. 判断是否需要移动。如果需要,第一次用normal这个变量来记录原始的view范围,然后用inner.layout(inner.getLeft(), yy, inner.getRight(),
      inner.getBottom() - deltaY);来移动布局。就是根据先前得到的移动距离来改变view的上下显示范围。
    4. 最后就是退回动画:TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),
      normal.top);就是简单的由现在的Y变化到记录的原始的Y。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

oschina源码分析之侧滑菜单界面之可以拖动的ScrollView 的相关文章

  • Tomcat的日志切分和定时删除

    在我负责的一个小系统中 Linux环境下 由于默认日志都是写入在 cattalina out中 我查看日志catalina out 竟然已经到了 40G了 我想做一下 文件内容检索来追踪问题都无法进行 于是我决定删除以前的无用日志 以每日作

随机推荐

  • Qt 与外部exe进程间通信-基于操作系统的消息传递

    步骤 进程A 通过WindowAPI找到需要传递信息的窗口 然后通过windowAPI发送自定义的消息 其实本质上还是window操作系统定义的消息结构 只不过其中有个字段的值被设置成了自己特有的值 const ULONG PTR CUST
  • Java分割字符串(spilt())

    String 类的 split 方法可以按指定的分割符对目标字符串进行分割 分割后的内容存放在字符串数组中 该方法主要有如下两种重载形式 str split String sign str split String sign int lim
  • 学好了Python可以干什么?

    随着我国对编程的重视程度上升 Python编程的学习趋势逐渐低龄化 在全国掀起Python编程热的同时 还是有许多人对于学习Python抱有怀疑 那么我们就来看看学好了Python究竟可以干什么 根据目前就业市场的反馈 我们可以看到Pyth
  • Cscope的使用

    转载自 http blog csdn net dengxiayehu article details 6330200 首先在目录下建立cscope索引文件 find name c gt cscope file cscope Rbkq 这个命
  • ST芯片使用串口 + DMA接收 + 空闲中断处理,有USART1和LPUART

    普通串口 USART1 首先是DMA初始化 DMA初始化 void MX DMA Init void Init with LL driver DMA controller clock enable LL AHB EnableClock LL
  • 最新淘宝主图下载方法-最新淘宝主图下载工具-马赛克视频助手

    做电商的朋友经常需要去下载淘宝上的别人的主图视频 那么淘宝主图视频怎么下载呢 工具 材料 马赛克视频助手 免费工具 www mask4 com 大家自行下载 操作方法 1 先打开马赛克视频助手 2 随便打开一个淘宝主图地址 如果是手机端的朋
  • ORA-00937: not a single-group group function说明及解决方法

    A SELECT list cannot include both a group function such as AVG COUNT MAX MIN SUM STDDEV or VARIANCE and an individual co
  • uni-app调用手机摄像头进行扫码

  • 放弃node-sass,启用sass

    在下载一个新项目时运行 npm run install 发现报错 npm uninstall 异常 Error Could not find any Visual Studio installation to use 或是 You need
  • 滑动平均滤波

    滑动平均滤波是一种常用的数字信号处理技术 它可以有效地去除信号中的噪声 提高信号的质量 滑动平均滤波的原理是对信号的每一个采样点 取其周围的若干个点的平均值作为滤波后的值 这样 信号中的随机噪声会被平均掉 而信号中的有用信息会被保留 用C语
  • 人民日报:密码,让百姓生活更安全

    密码技术是保障网络与信息安全的核心技术和基础支撑 通过加密保护和安全认证两大核心功能 可以完整实现防假冒 防泄密 防篡改 抗抵赖等安全需求 在网络空间中扮演着 信使 卫士 和 基因 的重要角色 信息化 网络化 数字化高度发达的今天 密码技术
  • 【突发】解决remote: Support for password authentication was removed on August 13, 2021. Please use a perso

    今天 github突然宣布 无法通过用户名加密码进行上传代码和访问 git push remote Support for password authentication was removed on August 13 2021 Plea
  • 策略模式+注解,代替if-else

    策略模式 经常在网上看到一些名为 别再if else走天下了 教你干掉if else 等之类的文章 大部分都会讲到用策略模式去代替if else 策略模式实现的方式也大同小异 主要是定义统一行为 接口或抽象类 并实现不同策略下的处理逻辑 对
  • 判断exe是64位还是32位

    右击exe属性 查看兼容模式 如果有windwos vista之前的版本则为32位的 如下图 如果没有windwos vista之前的版本则为64位的 如下图 转载于 https www cnblogs com tiandsp p 9603
  • Dubbo基础学习

    目录 第一章 概念介绍 1 1 什么是RPC框架 1 2 什么是分布式系统 1 3 Dubbo概述 1 3 Dubbo基本架构 第二章 服务提供者 直连 2 1 目录结构和依赖 2 2 model层 2 3 service层 2 4 res
  • 复用推挽输出与推挽输出区别

    GPIO InitStructure GPIO Mode GPIO Mode AF PP 复用推挽输出 GPIO SetBits GPIOE GPIO Pin 5 如果LED1是上拉的话 这时候它被点亮了 GPIO Mode AF PP g
  • QT 调用执行 linux脚本的三种方法

    在linux系统下 Qt执行shell命令的方式有3种 1 QProcess execute ls 2 system ls 3 QProcess process new QProcess process gt start ls 注1 以上3
  • 2020电赛准备总结(四)

    现在是2020 10 6 距离电赛开始还有三天 最近在考虑要不要让自己提前紧张起来 第一次准备这样的比赛 实在是没有经验 还差一口气没顶上去的感觉 最近开始调整状态了 从开学9 10到现在也快一个月了 感觉自己写博客总是在诉苦 像个怨妇一样
  • UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 以三个点的当前位置作为状态 广度优先遍历 找到终点即为最短次数 注意 一次可以移动多个点 但是每个点只能移动一步 在同一次中 B可
  • oschina源码分析之侧滑菜单界面之可以拖动的ScrollView

    先上源码 package net oschina app widget import android annotation SuppressLint import android content Context import android