进阶必备-Android Click事件是如何触发的?

一、背景

阅读本篇文章前,假设你已经阅读前一篇文章。
由于有同学问到onClick和touch事件的关系,这里就从源码的角度分析下onClick和onLongClick与onTouchEvent事件是怎么关联的。本文将通过View.java、TextView.java、Button.java的源码作为例子分析。

二、源码解读

首先我们知道View、TextView、Button三者的关系,即:Button继承自与TextView,TextView继承自View。
在默认我们不做任何特殊设置时,三者能响应click事件的只有Button。这是什么原因呢?

首先我们看到View的源码中onTouchEvent方法中:

final int viewFlags = mViewFlags;

final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
         || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
         || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

在onTouchEvent方法的一开始,通过viewFlags去判断当前View的CLICKABLE或者LONG_CLICKABLE位是否可以点击或者长按。默认情况下,在View初始化的时候会从xml读clickable属性或者longclickable属性。所以如果不在xml中设置,View和TextView是不会响应点击事件的,那么我们翻开Button的源码看下为什么唯独它是响应的呢?

public Button(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.buttonStyle);
}

可以看到在Button初始化的时候,设置了Button的style,我们翻开Styles.xml中找到Button:

    <style name="Widget.Button">
        <item name="background">@drawable/btn_default</item>
        <item name="focusable">true</item>
        <item name="clickable">true</item>
        <item name="textAppearance">?attr/textAppearanceSmallInverse</item>
        <item name="textColor">@color/primary_text_light</item>
        <item name="gravity">center_vertical|center_horizontal</item>
    </style>

没错,Button在默认的Style中就将clickable设置为true了。所以在默认情况下Button的clickable=true。通过下面这行代码(View.java的13743行)就可以知道,当clickable=true时,

if (clickable || (viewFlags & TOOLTIP) == TOOLTIP){
	// 处理逻辑
}

就可以进入if当中继续处理,因为我们响应click事件一般是在我们手按下再抬起后进行。所以,我们猜测是在MotionEvent.ACTION_UP事件后触发click的。所以我们直接看if条件中的ACTION_UP中的逻辑:

    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
        // This is a tap, so remove the longpress check
        removeLongPressCallback();

        // Only perform take click actions if we were in the pressed state
        if (!focusTaken) {
            // Use a Runnable and post this rather than calling
            // performClick directly. This lets other visual state
            // of the view update before click actions start.
            if (mPerformClick == null) {
                mPerformClick = new PerformClick();
            }
            if (!post(mPerformClick)) {
                performClickInternal();
            }
        }
    }  

在进入处理click之前,会先判断是否已经被长按做了处理,并且此次的ACTION_UP事件没有被忽略掉。当这些条件都满足后,首先会将长按的callback remove。然后会通过Post Runable的方式将PerformClick的实例post到队列中等待处理,不直接去处理click事件而是使用post的方式是确保如果有视图相关的更新操作完成后再触发performClickInternal()。我们先看下PerformClick类是干什么的?

    private final class PerformClick implements Runnable {
        @Override
        public void run() {
            performClickInternal();
        }
    }

很显然就是Runable对象,其中就是调用了performClickInternal()方法,而此方法中调用的是performClick方法:

    public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }

可以看到,最后是直接同过ListenerInfo中的mOnClickListener对象调用onClick方法。而ListenerInfo中的mOnClickListener对象就是我们通常使用view.setOnclickListener()方法设置赋值的:

    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        // 赋值操作
        getListenerInfo().mOnClickListener = l;
    }

至此,onClick事件是如何从onTouchEvent中触发的就可以完全看出来了。
同理,onLongClick类似,笔者这里就不做详细分析了。留给读者自己去详细的看下源码,这里简单的介绍下。
onLongClick事件是如何处理的呢?因为onCLick事件是在手指抬起后触发的,所以我们选择分析的是ACTION_UP事件,但是长按事件是在我们长按某个View的时候触发的,所以并没有将手指抬起来。所以我们肯定是在分析处理ACTION_DOWN中处理的。我们查看ACTION_DOWN事件下调用的checkForLongClick方法:

    private void checkForLongClick(int delayOffset, float x, float y) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
            mHasPerformedLongPress = false;

            if (mPendingCheckForLongPress == null) {
                mPendingCheckForLongPress = new CheckForLongPress();
            }
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberPressedState();
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
    }

我们看到onTouchEvent中的checkForLongClick中在最后一行通过postDelayed延迟发送了一个Runable对象:mPendingCheckForLongPress。延时时间是ViewConfiguration.getLongPressTimeout() - delayOffset的时间差。综上,简单来说,当我们按下屏幕的时候发送了一个延时的Runable,然后等到Runable被执行的时候,在通过一些标志位判断当前是否还满足长按被执行的条件,如果满足,回调listener中的onLongClick。 细节请读者自行对着源码看哦。

三、总结

对于一般的View来讲,onTouchEvent中处理的无非是对View的一个点击事件的处理、按下状态的处理、长按的处理。读者可以对类似于ScrollView这种带滑动的控件的onTouchEvent分析一下,对比于此文中的实现也不太一样哦。

微信搜索公众号:南京Android部落,你想知道的知识点后台回复,我会拿出来一起分析探讨。

<think>好的,用户让我帮忙打包一个关于Avalonia框架的PDF。首先,我需要确认用户的具体需求。他们可能正在学习Avalonia,或者需要在工作中使用这个框架,需要一份整理好的资料作为参考。用户可能不太熟悉如何系统地整理这些内容,或者时间紧迫,希望快速获取结构化的资料。 接下来,我要考虑如何组织内容。Avalonia是一个跨平台的UI框架,支持多种操作系统,所以需要涵盖基本概念、核心特性、开发环境搭建、项目结构、数据绑定、控件库、主题样式、部署发布、学习资源等方面。这些内容应该结构清晰,方便用户查阅。 用户可能希望PDF包含实用的示例代码和最佳实践,这样他们可以边学边练。因此,我需要确保每个部分都有代码示例,比如如何创建窗口、使用数据绑定、自定义控件等。此外,资源推荐也很重要,包括官方文档、社区论坛、教程视频等,帮助用户进一步学习。 还要注意技术细节的准确性,比如安装命令、项目配置步骤等,必须验证正确性,避免给用户带来困扰。同时,考虑到不同操作系统的用户,可能需要说明在Windows、macOS和Linux上的差异,比如依赖项安装的不同命令。 用户可能没有明确说明他们需要的深度,所以内容要兼顾基础与进阶,既适合新手入门,也能提供一些高级主题供参考。比如MVVM模式、依赖注入、自定义渲染等,这些内容对有一定经验的开发者更有帮助。 另外,考虑到PDF的可读性,需要使用合适的标题和子标题,合理分节,使用列表和代码块格式化内容。可能需要指导用户如何将Markdown转换为PDF,推荐一些工具,比如Visual Studio Code的插件或在线转换工具。 最后,需要提醒用户注意版本兼容性和官方文档的更新,避免因版本差异导致的问题。同时,邀请用户提出进一步的问题,确保他们在使用过程中遇到困难时能够获得支持。</think>以下是为您整理的Avalonia框架知识PDF内容大纲及生成指导。由于我无法直接生成文件,但可提供完整的结构化内容与生成方案: # 生成方案 **步骤1:复制下方Markdown内容** ```markdown # Avalonia框架技术手册 **版本:11.x | 最后更新:2023年12月** ## 一、框架概述 1. **跨平台特性** - Windows/macOS/Linux全支持 - Android/iOS(通过MAUI集成) - WebAssembly(实验性支持) 2. **技术架构** ```mermaid graph TD A[Avalonia UI] --> B(渲染引擎) A --> C(XAML解析器) A --> D(绑定系统) B --> E(Skia/Vulkan) ``` ## 二、开发环境搭建 1. **必备工具** ```bash # .NET SDK 6.0+ dotnet --version # IDE推荐 - Visual Studio 2022 (Windows) - Rider (跨平台) ``` 2. **项目创建** ```bash dotnet new avalonia.mvvm -o MyApp ``` ## 三、核心概念 1. **XAML进阶** ```xml <Window xmlns="https://github.com/avaloniaui"> <Button Content="点击我" Click="OnButtonClick"/> </Window> ``` 2. **数据绑定** ```csharp public class MainViewModel : INotifyPropertyChanged { private string _message; public string Message { get => _message; set => SetField(ref _message, value); } } ``` ## 四、实战示例 1. **自定义控件** ```csharp public class CustomButton : Button { public static readonly StyledProperty<string> CustomTextProperty = AvaloniaProperty.Register<CustomButton, string>(nameof(CustomText)); public string CustomText { get => GetValue(CustomTextProperty); set => SetValue(CustomTextProperty, value); } } ``` 2. **主题配置** ```xml <Application.Styles> <FluentTheme Mode="Dark"/> </Application.Styles> ``` ## 五、资源推荐 1. 官方文档:[https://docs.avaloniaui.net](https://docs.avaloniaui.net) 2. 示例仓库:AvaloniaUI/Avalonia.Samples 3. 中文社区:AvaloniaChina (QQ群) ``` **步骤2:转换为PDF** 推荐使用以下工具: 1. **Visual Studio Code** + Markdown PDF插件 2. **Typora**(文件 > 导出 > PDF) 3. 在线转换工具:[https://markdowntopdf.com](https://markdowntopdf.com) # 内容扩展建议 1. **添加实战章节**:可补充以下内容: ```csharp // 多窗口通信示例 public class WindowService { public static void ShowDialog<T>(Window parent) where T : Window, new() { new T().ShowDialog(parent); } } ``` 2. **部署指南**: ```bash # 发布单文件应用 dotnet publish -c Release -r win-x64 --self-contained true ``` **注意事项**: 1. 代码示例基于Avalonia 11.x版本 2. 实际开发时需注意目标平台依赖项 3. 推荐定期查看GitHub仓库更新日志 如果需要更具体的代码示例或架构图优化,请随时告知!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值