首页 文章 精选 留言 我的

精选列表

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

Android官方开发文档Training系列课程中文版:手势处理之ViewGroup的事件管理

原文地址:https://developer.android.com/training/gestures/viewgroup.html 在ViewGroup中处理触摸事件要格外小心,因为在ViewGroup中有很多子View,而这些子View对于不同的触摸事件来说是不同的目标。要确保每个View都正确的接收了相应的触摸事件。 在ViewGroup中拦截触摸事件 onInterceptTouchEvent()方法会在触摸事件到达ViewGroup的表面时调用,这包括内部的子View。如果onInterceptTouchEvent()返回了true,那么MotionEvent对象就会被拦截,这意味着该次事件不会传给子View,而是会传给ViewGroup本身的onTouchEvent()方法。 onInterceptTouchEvent()给了ViewGroup本身一个机会:在子View获得任何事件之前一个拦截该事件的机会。如果onInterceptTouchEvent()返回了true,那么原先处理该次事件的子View就会收到一个ACTION_CANCEL的事件,并且原先事件的剩余事件都会被传到该ViewGroup的onTouchEvent()方法中做常规处理。onInterceptTouchEvent()还可以返回false,这样的话,该次事件则会通过View树继续向下传递,直到到达目标View为止,目标View会在自己的onTouchEvent()方法中处理该次事件。 在下面的示例代码中,类MyViewGroup继承了ViewGroup,并包含了多个View,这些View我们在这里称之为子View,而MyViewGroup称为父容器View。如果你在水平方向上滑动手指,那么子View皆不会收到触摸事件。MyViewGroup会通过滚动它的内部来实现触摸事件的处理。不管如何,如果你按下了子View中的按钮,或者在垂直方向上滑动,那么ViewGroup则不会去拦截这些事件,因为子View是该次事件的目标View。在这些情况下,onInterceptTouchEvent()应该返回false,且MyViewGroup的onTouchEvent()方法也不会被调用。 public class MyViewGroup extends ViewGroup { private int mTouchSlop; ... ViewConfiguration vc = ViewConfiguration.get(view.getContext()); mTouchSlop = vc.getScaledTouchSlop(); ... @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onTouchEvent will be called and we do the actual * scrolling there. */ final int action = MotionEventCompat.getActionMasked(ev); // Always handle the case of the touch gesture being complete. if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { // Release the scroll. mIsScrolling = false; return false; // Do not intercept touch event, let the child handle it } switch (action) { case MotionEvent.ACTION_MOVE: { if (mIsScrolling) { // We're currently scrolling, so yes, intercept the // touch event! return true; } // If the user has dragged her finger horizontally more than // the touch slop, start the scroll // left as an exercise for the reader final int xDiff = calculateDistanceX(ev); // Touch slop should be calculated using ViewConfiguration // constants. if (xDiff > mTouchSlop) { // Start scrolling! mIsScrolling = true; return true; } break; } ... } // In general, we don't want to intercept touch events. They should be // handled by the child view. return false; } @Override public boolean onTouchEvent(MotionEvent ev) { // Here we actually handle the touch event (e.g. if the action is ACTION_MOVE, // scroll this container). // This method will only be called if the touch event was intercepted in // onInterceptTouchEvent ... } } 这里要注意,ViewGroup还提供了requestDisallowInterceptTouchEvent()方法。当子View不希望它的父容器及祖先容器拦截触摸事件时,ViewGroup会在 onInterceptTouchEvent()方法中对其进行调用,从而判断是否要拦截本次事件。 使用ViewConfiguration常量 在上面的代码中使用了ViewConfiguration来初始化一个名为mTouchSlop的变量。你可以使用ViewConfiguration来访问Android系统所使用的常用距离、速度及时间。 “mTouchSlop”引用了触摸事件在被拦截之前手指移动的以像素为单位的距离。Touch slop经常被用来在用户在执行触摸操作时防止产生意外滚动。 ViewConfiguration的另外两个常用方法是getScaledMinimumFlingVelocity()和getScaledMaximumFlingVelocity()。这两个方法分别返回了用于初始化滚动的最小、最大的速度值。以每秒几像素为单位: ViewConfiguration vc = ViewConfiguration.get(view.getContext()); private int mSlop = vc.getScaledTouchSlop(); private int mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); private int mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); ... case MotionEvent.ACTION_MOVE: { ... float deltaX = motionEvent.getRawX() - mDownX; if (Math.abs(deltaX) > mSlop) { // A swipe occurred, do something } ... case MotionEvent.ACTION_UP: { ... } if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) { // The criteria have been satisfied, do something } } 扩展子View的触控区域 Android提供的TouchDelegate使扩展子View的触控区域成为了可能。这对于子View本身特别小,而它的触控区域需要很大时很有用。如果需要的话,你也可以使用这种方式来缩小子View的触控区域。 在下面的示例中,ImageButton作为我们的”delegate view”(这里的意思是需要父容器扩展触控区域的那个View)。下面是示例的布局文件: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parent_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <ImageButton android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/icon" /> </RelativeLayout> 下面的代码做了以下这些事情: 获得父容器View,并Post一个Runnale对象到UI线程。这可以确保在调用getHitRect()方法之前父容器已经对子View完成了排布。getHitRect()会返回父容器坐标内当前View的点击矩阵(触控区域)。 找到ImageButton,然后调用它的getHitRect()方法获得该View的触控边界。 扩大ImageButton的触控区域。 实例化一个TouchDelegate,将要扩展的触控区域矩阵与要扩展触控区域的ImageView作为参数传入。 将TouchDelegate设置给父容器View,只有这样做,我们所触碰到的扩展区域才会被路由到子View上。 在TouchDelegate代理的范围内,父容器View将会接收所有的触摸事件。如果触摸事件发生在子View本身的触控区域内,那么父容器View会将所有的触摸事件传给子View处理: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get the parent view View parentView = findViewById(R.id.parent_layout); parentView.post(new Runnable() { // Post in the parent's message queue to make sure the parent // lays out its children before you call getHitRect() @Override public void run() { // The bounds for the delegate view (an ImageButton // in this example) Rect delegateArea = new Rect(); ImageButton myButton = (ImageButton) findViewById(R.id.button); myButton.setEnabled(true); myButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "Touch occurred within ImageButton touch region.", Toast.LENGTH_SHORT).show(); } }); // The hit rectangle for the ImageButton myButton.getHitRect(delegateArea); // Extend the touch area of the ImageButton beyond its bounds // on the right and bottom. delegateArea.right += 100; delegateArea.bottom += 100; // Instantiate a TouchDelegate. // "delegateArea" is the bounds in local coordinates of // the containing view to be mapped to the delegate view. // "myButton" is the child view that should receive motion // events. TouchDelegate touchDelegate = new TouchDelegate(delegateArea, myButton); // Sets the TouchDelegate on the parent view, such that touches // within the touch delegate bounds are routed to the child. if (View.class.isInstance(myButton.getParent())) { ((View) myButton.getParent()).setTouchDelegate(touchDelegate); } } }); } }

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

Android官方开发文档Training系列课程中文版:手势处理之多点触控处理

原文地址:http://android.xsoftlab.net/training/gestures/multi.html 多点触控是指多个手指同时触摸屏幕的情况。这节课主要学习如何检测多点触控手势。 记录多个触控点 当多根手指同时触碰到屏幕时,系统会产生以下触摸事件: ACTION_DOWN -第一个触碰到屏幕的点。它是手势的起始事件。这个触控点的指针数据在MotionEvent对象中的索引总是0。 ACTION_POINTER_DOWN -除第一个触控点之外的其它点。这个触控点的指针数据的索引由getActionIndex()方法返回。 ACTION_MOVE -屏幕上的手指位置发生变化时。 ACTION_POINTER_UP -除最开始按下的其它触控点离开屏幕时。 ACTION_UP -最后一个触控点离开屏幕时。 我们可以通过每一个触控点对应的索或ID来追踪MotionEvent对象中的每一个触控点: Index: MotionEvent对象将触控点的相关信息存储于一个数组中。每一个触控点的索引则是这个触控点在数组中的相对位置。MotionEvent对象的大多数方法都可以使用这些索引来与这些点产生交互。 ID: 每一个触控点也含有一个ID映射,这个映射关系在手势事件的整个生命周期内与相对应的触控点一直保持相对关系。 每个触控点的出现顺序是不固定的。因此,触控点的索引可以由事件转移到下一个索引,但是触控点的ID始终保持为一个常量。使用getPointerId()方法可以获得指定触控点的ID,因此可以在余下的手势事件中还可以继续保持与这个触控点的联系。使用findPointerIndex()方法可以根据指定的ID获得触控点的索引: private int mActivePointerId; public boolean onTouchEvent(MotionEvent event) { .... // Get the pointer ID mActivePointerId = event.getPointerId(0); // ... Many touch events later... // Use the pointer ID to find the index of the active pointer // and fetch its position int pointerIndex = event.findPointerIndex(mActivePointerId); // Get the pointer's current position float x = event.getX(pointerIndex); float y = event.getY(pointerIndex); } 获取事件的活动 使用getActionMasked()方法可以获取MotionEvent的活动。与getAction()方法不同,getActionMasked()适用于多个触控点。它会返回正在执行的活动。你可以使用getActionIndex()方法获得与之相关联的触控点的索引。下面的代码演示了这个过程: Note: 示例中使用了MotionEventCompat类。这个类位于支持库中。你应该使用该类以便提供良好的向后兼容性。注意,MotionEventCompat类并不可以替代MotionEvent类。这个类提供了一个实用的静态方法,可以将MotionEvent对象所关联的活动提取出来。 int action = MotionEventCompat.getActionMasked(event); // Get the index of the pointer associated with the action. int index = MotionEventCompat.getActionIndex(event); int xPos = -1; int yPos = -1; Log.d(DEBUG_TAG,"The action is " + actionToString(action)); if (event.getPointerCount() > 1) { Log.d(DEBUG_TAG,"Multitouch event"); // The coordinates of the current screen contact, relative to // the responding View or Activity. xPos = (int)MotionEventCompat.getX(event, index); yPos = (int)MotionEventCompat.getY(event, index); } else { // Single touch event Log.d(DEBUG_TAG,"Single touch event"); xPos = (int)MotionEventCompat.getX(event, index); yPos = (int)MotionEventCompat.getY(event, index); } ... // Given an action int, returns a string description public static String actionToString(int action) { switch (action) { case MotionEvent.ACTION_DOWN: return "Down"; case MotionEvent.ACTION_MOVE: return "Move"; case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down"; case MotionEvent.ACTION_UP: return "Up"; case MotionEvent.ACTION_POINTER_UP: return "Pointer Up"; case MotionEvent.ACTION_OUTSIDE: return "Outside"; case MotionEvent.ACTION_CANCEL: return "Cancel"; } return ""; } 有关多点触控的更多信息,可以参见课程Dragging and Scaling.

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

macOS 官方共三年更新支持,但你知道苹果给固件提供多久的支持吗?

众所周知,Apple 支持每个版本的 macOS 整整一年的更新,然后再提供两年的安全更新。但它对每个型号的固件支持多长时间?现在更新 Mac 固件的唯一方法是安装 macOS 更新,这会如何影响支持期?本文试图回答这些问题,并在此过程中揭开这长达十多年的谜团。 数据 Apple 不会发布任何有关固件版本或更新的信息,甚至很少在安全更新的发布说明中提及它们。幸运的是,自从七年前 High Sierra 发布以来,我一直在跟踪每种型号的固件版本,因此我有自己的记录,这些记录来自 macOS 更新中包含的版本。我将这些记录与 Ian Page 的Mactracker 数据库中给出的型号推出和停产日期进行了匹配,并在此总结了结果。 更新的工作原理 每次 macOS 更新都可能带来固件更新,尽管在支持的第一年,纯安全补丁带来的更新往往较少。通常同时发布的三个 macOS 更新中的固件更新都是相同的。因此,最近更新到 14.6 带来的更新与 13.6.8 和 12.7.5 中的更新相同,适用于各自支持的型号,但每次更新只会安装其支持的型号的更新。借助示例,这一点变得更加清晰,这些示例也揭示了这些更新的内在奥秘。 2020 年 7 月 15 日,主要更新带来了 macOS 10.15.6,以及针对 macOS 10.13 和 10.14 的安全更新 (SU)。其中包括以下 EFI 固件版本: 适用于 iMac12,1 版本 87.0.0.0.0(2019 年 6 月 14 日) 适用于 iMac13,1 版本 292.0.0.0.0,发布日期:2020 年 6 月 10 日 适用于 MacBookPro8,1 版本 87.0.0.0.0(2019 年 6 月 13 日) 适用于 MacBookPro9,1 版本 233.0.0.0.0,发布日期为 2020 年 6 月 10 日。 这两个适用于 iMac12,1 和 MacBookPro8,1 的固件版本均为 2019 年,当时已经有一年的历史了,因为 Apple 已于 2019 年 6 月停止为这两款型号发布新固件版本。但是,如果 iMac13,1 和 MacBookPro9,1 型号安装了 macOS 10.15.6 或任一安全更新,则会收到新版本的固件。 一年后,即 2021 年 7 月 21 日,Apple 发布了 macOS 11.5 更新,并发布了 Mojave SU 2021-005。由于 iMac12,1 和 MacBookPro8,1 不再能够运行受支持的 macOS 版本,因此它们都没有固件更新,只能运行 2019 年 6 月的版本。随后,两款较新的型号进行了以下更新: 适用于 iMac13,1 版本 422.0.0.0.0,2021 年 6 月 4 日 适用于 MacBookPro9,1 版本 422.0.0.0.0,发布日期:2021 年 6 月 4 日。 又过了一年,即 2022 年 7 月 20 日,这两款型号仍可运行受支持的 macOS,并在 Catalina SU 2022-005 中进行了以下固件更新: 适用于 iMac13,1 版本 429.0.0.0.0,发布日期:2022 年 3 月 18 日 适用于 MacBookPro9,1 版本 429.0.0.0.0,发布日期:2022 年 3 月 18 日。 但这些并不是该 SU 中的新功能,因为那时这两种型号的固件更新已经停止,而在 2023 年 5 月 18 日的 Big Sur 11.7.7 中,这两种型号都没有任何可用的固件,因为它们不再受仍在接收更新的 macOS 版本的支持。 这个例子揭示了一个鲜明的事实:对于相隔一年多发布的 ​​iMac 和 MacBook Pro 的连续型号,上次发布的固件更新却相隔了近三年的时间: 对于 iMac12,1 最新发布于 2019 年 6 月,对于 iMac13,1 最新发布于 2022 年 3 月 对于 MacBookPro8,1 来说,最新发布于 2019 年 6 月,对于 MacBookPro9,1 来说,最新发布于 2022 年 3 月。 多久? 因此,我收集了 2009 年 10 月至今推出的 40 款未配备 T2 芯片的英特尔 Mac 的数据,每款产品显然都已通过了最终固件更新。这不包括目前仍在接收固件更新的少数型号。 此图表按型号推出日期显示了每个型号的最后一次固件更新日期。2012 年之前推出的大量 Mac 于 2019 年 6 月收到了最后一次固件更新,之后近两年的时间里,所有后续型号都收到了进一步的固件更新,之后的下一批旧型号(这次是 2012-13 年推出的)才收到了最终更新。右上角可见一个异常值,即 2019 年 3 月推出的 iMac19,1,但似乎在 2024 年 2 月进行了最后一次更新,非常早。虽然此后没有收到任何固件更新,但将来可能会收到更多固件更新。 此图表显示了该型号推出之日起固件支持的总长度(以年为单位)。有三个不同的组: 2012 年之前的模型,在左侧形成一条陡峭的线,支持时间从不到 8 年到近 10 年不等; 更新的模型,形成不太密集的散点,支持时间从不到 7.5 年到近 10 年; 右下角是 iMac19,1 异常值,其支持时间极短,约为 5 年。 这是同一张图表,但叠加了标签,标明了每种型号的名称。型号范围(例如 iMac)和支持期限之间似乎没有任何关联。 因此,对于自 2009 年以来推出的大多数不带 T2 芯片的英特尔型号,固件更新支持已延长至推出以来至少 8 年。由于型号的推出和停产之间的时间差异很大,因此在以停产日期表示时,分散性较大。 差距 有几个可能的原因可以解释 2012 年之前推出的 Mac 与最近推出的 Mac 之间的差异。这些包括: 2011-12 年推出的 Mac 电脑从 Sandy Bridge 过渡到 Ivy Bridge; 在推出 Apple 硅片型号期间,Intel Mac 预计会保持一段稳定期; 苹果选择在 Covid 大流行期间不停止固件支持,尽管我不记得曾经明确表示过这一点; 苹果固件支持政策的任意改变。 我赞成不再使用 Sandy Bridge,因为众所周知,Sandy Bridge 存在一些问题,可能导致固件支持比预期更早地结束。 值得注意的是,这种差距并不意味着在此期间没有发布固件更新,而只是意味着在此期间仍在更新的型号会继续更新,并且不会终止任何更新。 T2 和 Apple 芯片 这些较新的型号从 2017 年开始推出,完全改变了固件更新。所有配备 T2 芯片的 Mac 都会收到看似相同的固件更新。尽管 Mac 仍受 macOS 更新支持,但后续更新仍被放弃(在某些情况下会发生这种情况),但 T2 固件更新似乎只有在型号不再受 macOS 更新支持时才会停止。 由于 Apple 完全拥有 Apple Silicon Mac 的硬件和操作系统,因此可以决定对每款产品的支持期限。 结论 对于大多数未配备 T2 芯片的 Intel Mac,Apple 在该型号推出后至少 8 年内都提供了固件更新。对于许多型号,在它们无法运行受支持的 macOS 版本之前,就已经进行了固件更新。 2012 年之前推出的一些 Mac 搭载了 Sandy Bridge 芯片组,其固件支持早早被取消。原因尚不清楚,但可能与芯片组有关。 T2 和 Apple Silicon Mac 将会有所不同。 原文链接:https://eclecticlight.co/2024/08/06/how-long-does-apple-support-mac-firmware

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

🔥🔥🔥 最好用的跨平台 asiinema 终端录屏分享工具,比官方工具还强大

项目地址: https://github.com/gvcgo/asciinema 支持的平台:**MacOS/Linux/Windows** 支持的操作: 录制 播放 编辑(加速、剪切、整理延迟) 上传 asciinema.org 官网,并回显链接 转换为 gif 演示: 以下 gif 均使用本项目录制、编辑、转换,最终生成 gif 动图 1 、原速 gif( https://asciinema.org/a/651138) 2 、2 倍速 gif( https://asciinema.org/a/651140)

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

iPhone 13第三方换屏更难:Face ID会失效 即便官方配件也不行

苹果对 iPhone 13 系列屏幕的重新设计,意味着第三方维修公司现在更难更换屏幕。因为在尝试更换损坏掉的屏幕,可能会导致失去对 Face ID 的支持。碎屏是最常见的手机故障之一,任何意外的跌落、磕碰、挤压等等都会造成。 当 iPhone 出现故障,用户可以选择就近的 Apple Store 天才吧、或者苹果认证的维修网点、亦或者是第三方的维修中心。而如果是 iPhone 屏幕损坏,那么不能通过第三方维修中心进行维护。 油管频道 Phone Repair Guru 表示,虽然将屏幕替换成相同型号的 iPhone 13 屏幕在理论上可行的。但是在更换之后系统警告说,设备并没有使用正规的屏幕,而且 Face ID 也无法使用。 虽然更换屏幕是存在问题的,但是包括麦克风、接近传感器和环境光传感器依然可以更换的。尽管使用的是真正的苹果显示屏,但该通知基本上意味着在维修过程中有一个步骤必须执行,以使显示屏能够与特定的iPhone一起使用,而这一步骤是苹果授权的维修服务可以做的,但第三方不能做。 【责任编辑:未丽燕 TEL:(010)68476606】

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

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

Sublime Text

Sublime Text

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