首页 文章 精选 留言 我的

精选列表

搜索[官方镜像],共10000篇文章
优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:手势处理之滚动动画及Scroller

原文地址:http://android.xsoftlab.net/training/gestures/scroll.html 在Android中,滑动经常由ScrollView类来实现。任何超出容器边界的布局都应该将自己内嵌在ScrollView中,以便提供可滚动的视图效果。自定义滚动只有在特定的场景下才会被用到。这节课将会描述这样一种场景:使用scroller显示一种可滚动的效果。 你可以使用Scroller或者OverScroller来收集一些滑动动画所需要的数据。这两个类很相似,但是OverScroller包含了一些用于指示已经到达布局边界的方法。示例InteractiveChart在这里使用了类EdgeEffect(实际上是类EdgeEffectCompat),在用户到达布局边界时会显示一种”glow“的效果。 Note: 我们推荐使用OverScroller。这个类提供了良好的向后兼容性。还应该注意,通常只有在实现滑动自己内部的时候,才需要使用到scroller类。如果你将布局嵌入到ScrollView或HorizontalScrollView中的话,那么它们会将滚动的相关事宜做好。 Scroller用于在时间轴上做动画滚动效果,它使用了标准的滚动物理学(摩擦力、速度等等)。Scroller本身不会绘制任何东西。Scroller会随着时间的变化追踪移动的偏移量,但是它们不会自动的将这些值应用在你的View上。你需要自己获取这些值并使用,这样才会使滑动的效果更流畅。 了解滑动术语 “Scrolling”这个词在Android中可被翻译为各种不同的意思,这取决于具体的上下文。 Scrolling是一种viewport(viewport的意思是,你所看到的内容的窗口)移动的通用处理过程。当scrolling处于x轴及y轴上时,这被称为“平移(panning)”。示例程序提供了相关的类:InteractiveChart,演示了滑动的两种不同类型,dragging(拖动)及flinging(滑动): Dragging 该滚动类型在这种情况下发生:当用户的手指在屏幕上来回滑动时。简单的Dragging由GestureDetector.OnGestureListener接口的onScroll()方法实现。 Flinging 该滚动类型在这种情况下发生:当用户快速在屏幕上滑动并离开屏幕时。在用户离开了屏幕之后,常理上应该保持滚动状态,并随之慢慢减速,直到viewport停止移动。Flinging由GestureDetector.OnGestureListener接口的onFling()方法及scroller对象所实现。 将scroller对象与滑动手势结合使用是一种共通的方法。你可以重写onTouchEvent()方法直接处理触摸事件,并降低滑动的速度来响应相应的触摸事件。 实现基础滚动 这部分章节将会描述如何使用scroller。下面的代码段摘自示例应用InteractiveChart。它在这里使用了一个GestureDetector对象,重写了GestureDetector.SimpleOnGestureListener的onFling()方法。它使用了OverScroller来追踪滑动中的手势。如果用户在滑动之后到达了内容的边缘,那么APP会显示一个”glow”的效果。 Note: 示例APP InteractiveChart 展示了一个图表,这个图标可以缩放、平移、滚动等等。在下面的代码段中,mContentRect代表了矩形的坐标点,而绘制图表的View则居于其中。在给定的时间内,一个总表的子集将会被绘制到这块区域内。mCurrentViewport代表了屏幕中当前可视的部分图表。因为像素的偏移量通常被当做整型,所以mContentRect的类型是Rect。因为图形的数据范围是小数类型,所以mCurrentViewport的类型是RectF。 代码段的第一部分展示了onFling()方法的实现: // The current viewport. This rectangle represents the currently visible // chart domain and range. The viewport is the part of the app that the // user manipulates via touch gestures. private RectF mCurrentViewport = new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX); // The current destination rectangle (in pixel coordinates) into which the // chart data should be drawn. private Rect mContentRect; private OverScroller mScroller; private RectF mScrollerStartViewport; ... private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { // Initiates the decay phase of any active edge effects. releaseEdgeEffects(); mScrollerStartViewport.set(mCurrentViewport); // Aborts any active scroll animations and invalidates. mScroller.forceFinished(true); ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this); return true; } ... @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { fling((int) -velocityX, (int) -velocityY); return true; } }; private void fling(int velocityX, int velocityY) { // Initiates the decay phase of any active edge effects. releaseEdgeEffects(); // Flings use math in pixels (as opposed to math based on the viewport). Point surfaceSize = computeScrollSurfaceSize(); mScrollerStartViewport.set(mCurrentViewport); int startX = (int) (surfaceSize.x * (mScrollerStartViewport.left - AXIS_X_MIN) / ( AXIS_X_MAX - AXIS_X_MIN)); int startY = (int) (surfaceSize.y * (AXIS_Y_MAX - mScrollerStartViewport.bottom) / ( AXIS_Y_MAX - AXIS_Y_MIN)); // Before flinging, aborts the current animation. mScroller.forceFinished(true); // Begins the animation mScroller.fling( // Current scroll position startX, startY, velocityX, velocityY, /* * Minimum and maximum scroll positions. The minimum scroll * position is generally zero and the maximum scroll position * is generally the content size less the screen size. So if the * content width is 1000 pixels and the screen width is 200 * pixels, the maximum scroll offset should be 800 pixels. */ 0, surfaceSize.x - mContentRect.width(), 0, surfaceSize.y - mContentRect.height(), // The edges of the content. This comes into play when using // the EdgeEffect class to draw "glow" overlays. mContentRect.width() / 2, mContentRect.height() / 2); // Invalidates to trigger computeScroll() ViewCompat.postInvalidateOnAnimation(this); } 当onFling()方法调用postInvalidateOnAnimation()方法时,它会调用computeScroll()来更新x及y的值。 大多数View会将scroller对象的x及y的属性值直接设置给方法scrollTo()。而下面的computeScroll()则采用了不同的方法:它调用computeScrollOffset()来获取x及y的当前坐标。当显示的区域到达边界时,这里就会展示一个”glow”的效果,代码会设置一个越过边缘的效果,并会调用postInvalidateOnAnimation()来使View重新绘制。 // Edge effect / overscroll tracking objects. private EdgeEffectCompat mEdgeEffectTop; private EdgeEffectCompat mEdgeEffectBottom; private EdgeEffectCompat mEdgeEffectLeft; private EdgeEffectCompat mEdgeEffectRight; private boolean mEdgeEffectTopActive; private boolean mEdgeEffectBottomActive; private boolean mEdgeEffectLeftActive; private boolean mEdgeEffectRightActive; @Override public void computeScroll() { super.computeScroll(); boolean needsInvalidate = false; // The scroller isn't finished, meaning a fling or programmatic pan // operation is currently active. if (mScroller.computeScrollOffset()) { Point surfaceSize = computeScrollSurfaceSize(); int currX = mScroller.getCurrX(); int currY = mScroller.getCurrY(); boolean canScrollX = (mCurrentViewport.left > AXIS_X_MIN || mCurrentViewport.right < AXIS_X_MAX); boolean canScrollY = (mCurrentViewport.top > AXIS_Y_MIN || mCurrentViewport.bottom < AXIS_Y_MAX); /* * If you are zoomed in and currX or currY is * outside of bounds and you're not already * showing overscroll, then render the overscroll * glow edge effect. */ if (canScrollX && currX < 0 && mEdgeEffectLeft.isFinished() && !mEdgeEffectLeftActive) { mEdgeEffectLeft.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); mEdgeEffectLeftActive = true; needsInvalidate = true; } else if (canScrollX && currX > (surfaceSize.x - mContentRect.width()) && mEdgeEffectRight.isFinished() && !mEdgeEffectRightActive) { mEdgeEffectRight.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); mEdgeEffectRightActive = true; needsInvalidate = true; } if (canScrollY && currY < 0 && mEdgeEffectTop.isFinished() && !mEdgeEffectTopActive) { mEdgeEffectTop.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); mEdgeEffectTopActive = true; needsInvalidate = true; } else if (canScrollY && currY > (surfaceSize.y - mContentRect.height()) && mEdgeEffectBottom.isFinished() && !mEdgeEffectBottomActive) { mEdgeEffectBottom.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); mEdgeEffectBottomActive = true; needsInvalidate = true; } ... } 下面是执行实际放大的代码: // Custom object that is functionally similar to Scroller Zoomer mZoomer; private PointF mZoomFocalPoint = new PointF(); ... // If a zoom is in progress (either programmatically or via double // touch), performs the zoom. if (mZoomer.computeZoom()) { float newWidth = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.width(); float newHeight = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.height(); float pointWithinViewportX = (mZoomFocalPoint.x - mScrollerStartViewport.left) / mScrollerStartViewport.width(); float pointWithinViewportY = (mZoomFocalPoint.y - mScrollerStartViewport.top) / mScrollerStartViewport.height(); mCurrentViewport.set( mZoomFocalPoint.x - newWidth * pointWithinViewportX, mZoomFocalPoint.y - newHeight * pointWithinViewportY, mZoomFocalPoint.x + newWidth * (1 - pointWithinViewportX), mZoomFocalPoint.y + newHeight * (1 - pointWithinViewportY)); constrainViewport(); needsInvalidate = true; } if (needsInvalidate) { ViewCompat.postInvalidateOnAnimation(this); } 下面是computeScrollSurfaceSize()方法的内容。它计算了当前可滑动的界面的尺寸,以像素为单位。举个例子,如果整个图表区域是可见的,那么它的值就等于mContentRect。如果图标被放大了200%,那么返回的值就是水平及垂直方向值的两倍。 private Point computeScrollSurfaceSize() { return new Point( (int) (mContentRect.width() * (AXIS_X_MAX - AXIS_X_MIN) / mCurrentViewport.width()), (int) (mContentRect.height() * (AXIS_Y_MAX - AXIS_Y_MIN) / mCurrentViewport.height())); }

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:手势处理之记录手指移动的轨迹

原文地址:http://android.xsoftlab.net/training/gestures/movement.html 这节课将会学习如何在触摸事件中记录手指移动的轨迹。 当手指触摸的位置、压力或者尺寸发生变化时,ACTION_MOVE事件就会被触发。与Detecting Common Gestures中描述的一样,所有的事件都被记录在一个MotionEvent对象中。 因为基于手指的触摸并不是很精确的交互方式,所以检测触摸事件的行为需要更多的轨迹点。为了帮助APP区分基于轨迹的手势(比如滑动等移动的手势)与非轨迹手势(比如单点等不移动的手势),Android提出了一个名为”touch slop”的概念。Touch slop指的是用户按下的以像素为单位的距离。 这里有若干项不同的追踪手势轨迹的方法,具体使用哪个方法取决于应用程序的需求: 指针的起始位置与结束位置。 指针位移的方向,由X,Y的坐标判断。 历史记录,你可以通过getHistorySize()获得手势的历史尺寸。然后可以通过getHistorical(Value)方法获得这些历史事件的位置,尺寸,事件以及压力。当渲染手指的轨迹时,比如在屏幕上用手指画线条等,历史记录这时就会派上用场。 指针在屏幕上滑动的速度。 轨迹的速度 在记录手势的特性或者在检查何种手势事件发生时,除了要依靠手指移动的距离、方向这两个要素之外。还需要另外一个非常重要的因素就是速度。为了使速度计算更加容易,Android为此提供了VelocityTracker类以及VelocityTrackerCompat类。VelocityTracker用于辅助记录触摸事件的速度。这对于判断哪个速度是手势的标准部分,比如飞速滑动。 下面的例子用于演示在VelocityTracker API中方法的目的: public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: " + VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId)); Log.d("", "Y velocity: " + VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } Note: 注意,应当在ACTION_MOVE事件内部计算速度,不要在ACTION_UP内部计算,因为在ACTION_UP内部计算所得到的X与Y的速度值都是0.

优秀的个人博客,低调大师

一步步把一个SpringBoot应用打包成Docker镜像并运行

(1) 首先要有一个可以工作的SpringBoot应用。 从Jerry的github上clone这个github repository到本地: cd进入项目文件夹内,使用命令行mvn spring-boot:run 当看到控制台输出 Tomcat started on port: 5030(http)的提示后,说明SpringBoot应用在本地启动成功, 这时用下面的url可以访问这个SpringBoot应用,如果一切正常,http://localhost:5030/commerce/product 可以在浏览器里看到Hello World: 祝:该SpringBoot应用监听的端口为5030,如果想修改成其他端口,在application.properties里修改。 (2) 下一步是登录阿里云服务器,将该SpringBoot打包成Docke

优秀的个人博客,低调大师

一批Android网银木马躲过官方应用商店检测 清理前下载量已达30万

Threat Fabric 安全研究人员刚刚公布了一批 Android 网银木马,而且在被 Google Play 清理之前,其下载量就已经超过了 30 万次。在二维码扫描仪、PDF 扫描仪、加密货币钱包等表象的掩饰下,这些恶意应用会在暗中窃取用户登录凭证、双因素身份验证码、记录按键、以及屏幕截图。 (来自:Threat Fabric) 通过持续四个月的追踪,Threat Fabric 发现了四个独立的 Android 恶意软件系列。可知其利用了多种技巧,来规避 Google Play 应用商店的检测机制 安全研究人员指出,之所以从 Google Play 的自动化(安全沙箱)和机器学习审核流程中逃逸,正是该平台试试权限限制的直接后果。 通常情况下,这些恶意软件会先以一款良性 App 的面目示人。所以在早期的 VirusTotal 恶意软件检测过程中,它们并不会在第一时间被揪出。 但在用户安装后,它们就会开始诱骗用户下载并安装带有“附加功能”的更新包。此时这些恶意应用会通过第三方来源来获取,但此时它们已经骗取了用户的普遍信任。 为了躲避雷达追踪,这些恶意程序还利用了其它手段。在许多情况下,幕后操纵者只有在检查受感染的设备的地理位置、或通过增量更新后,才会手动部署恶意内容。 这种致力于躲过不必要关注的手段,实在让人难以置信。然而现实表明,基于自动化流程的传统恶意软件检测方案,正在变得不那么可靠。 在近日发表的一篇博客文章中,Threat Fabric 详细阐述了被调查的 9 款 dropper 恶意软件。其中造成最多感染的,被称作 Anatsa 家族。 这款“相当先进”的 Android 网银木马内置了许多功能,包括远程访问和自动转账系统。受害者将被无情地清空账户,将资金转移到幕后黑手控制的账户中。 感染 Anatsa 恶意软件的过程,是从 Google Play 下载看似人畜无害的初始安装包后开始的。之后相关 App 会强制用户更新,以继续使用该应用程序。 但现实是,幕后黑手在远程更新服务器上托管了夹带私货的恶意内容,并通过骗取信任的方式,将之安装在了毫无戒备的受害者设备上。 为了装得更像一些,幕后团伙甚至会雇人在 Google Play 应用商店刷好评,以引诱更多无辜者上当受骗。 最后,研究人员还发现了另外三大恶意软件家族(分别称之为 Alien、Hydra 和 Ermac)。 其特点是植入了 Gymdrop 恶意负载,并利用基于受感染设备模型的过滤规则,来躲过安全研究人员的搜捕。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册