从源码解析View的事件分发机制

Android View体系(五)从源码解析View的事件分发机制 三年前写过事件分发机制的文章但是写的不是很好,所以重新再写一篇,关于事件分发机制的文章已经有很多,但是希望我这篇是最简洁、最易懂的一篇。 阅读详情

Android、View视图与坐标系
View的滑动和属性动画
从源码解析View的事件分发机制
View的工作流程
Android自定义view

View的事件分发机制

  在讲到View的事件分发机制之前要首先了解
一下Activity的组成,然后从源码的角度来分析View的事件分发机制。

源码解析Activity的构成

  点击事件用MotionEvent来表示,当一个点击事件产生后,事件最先传递给Activity,所以我们首先要了解一下Activity的构成。当我们写Activity时会调用setContentView()方法来加载布局。现在来看看
setContentView()方法是怎么实现的,代码如下所示:

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

  这里调用了getWindow().setContentView(layoutResID),getWindow()指的是什么呢?接着往下看,getWindow()返回mWindow:

    public Window getWindow() {
        return mWindow;
    }

  那这个mWindow又是什么呢?我们继续查看代码,最终在Activity的attach()方法中发现了mWindow,代码如下所示:

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    mWindow = new PhoneWindow(this);
    ...
}

  而getWindow()又指的是PhoneWindow。所以来看看PhoneWindow的setContentView()方法,代码如下所示:

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();//1
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, 
        layoutResID, getContext());
            transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

  原来mWindow指的就是PhoneWindow,PhoneWindow是继承抽象类Window的,这样就知道了getWindow()得到的是一个PhoneWindow,因为Activity中setContentView()方法调用的是getWindow().setContentView(layoutResID)。

  挑关键的接着看,看看上面代码注释1处installDecor()方法里面做了什么,代码如下所示:

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor();//1
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
    ...
}    

  在前面的代码中没发现什么,紧接着查看上面代码注释1处的generateDecor方法里做了什么:

protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
}

  这里创建了一个DecorView,这个DecorView就是Activity中的根View。接着查看DecorView的源码,发现DecorView是PhoneWindow类的内部类,并且继承了FrameLayout。我们再回到installDecor()方法中,
查看generateLayout(mDecor)做了什么:

protected ViewGroup generateLayout(DecorView decor) {
    ...
    int layoutResource;
    int features = getLocalFeatures();
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
        setCloseOnSwipeEnabled(true);
    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
        } else {
            layoutResource = R.layout.screen_title_icons;
        }
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
        && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
        layoutResource = R.layout.screen_progress;
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
        } else {
            layoutResource = R.layout.screen_custom_title;
        }
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
            layoutResource = a.getResourceId(
            R.styleable.Window_windowActionBarFullscreenDecorLayout,R.layout.screen_action_bar);
        } else {
            layoutResource = R.layout.screen_title;
        }
    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
        layoutResource = R.layout.screen_simple;
    }
    ...
}

  PhoneWindow的generateLayout()方法比较长,这里只截取了一小部分关键的代码,其主要内容就是根据不同的情况加载不同的布局给layoutResource。现在查看上面代码注释1处的布局R.layout.screen_title,这个文件在frameworks,它的代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
    <!-- Popout bar for action modes -->
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

  上面的ViewStub是用来显示Actionbar的。下面的两个FrameLayout:一个是title,用来显示标题;另一
个是content,用来显示内容。看到上面的源码,大家就知道了一个Activity包含一个Window对象,这个对象是由PhoneWindow来实现的。PhoneWindow将DecorView作为整个应用窗口的根 View,而这个DecorView又将屏幕划分为两个区域:一个是 TitleView,另一个是ContentView,而我们平常做应用所写的布局正是展示在ContentView中的,
image

图5:Activity的构成

源码解析View的事件分发机制

  当我们点击屏幕时,就产生了点击事件,这个事件被封装成了一个类:MotionEvent。而当这个MotionEvent产生后,那么系统就会将这个MotionEvent传递给View的层级,MotionEvent在View中的层级传递过程就是点击事件分发。在了解了什么是事件分发后,我们还需要了解事件分发的3个重要方法。点击事件有3个重要的方法,它们分别是:

  • dispatchTouchEvent(MotionEvent ev)——用来进行事件的分发。
  • onInterceptTouchEvent(MotionEvent ev)——用来进行事件的拦截,在dispatchTouchEvent()中调用,
    需要注意的是View没有提供该方法。
  • onTouchEvent(MotionEvent ev)——用来处理点击事件,在dispatchTouchEvent()方法中进行调用。

View的事件分发机制

  当点击事件产生后,事件首先会传递给当前的 Activity,这会调用 Activity 的dispatchTouchEvent()方法,当然具体的事件处理工作都是交由Activity中的PhoneWindow来完成的,然后PhoneWindow再把事件处理工作交给DecorView,之后再由DecorView将事件处理工作交给根ViewGroup。所以,我们从ViewGroup的
dispatchTouchEvent()方法开始分析,代码如下所示:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {//1
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//2
            if (!disallowIntercept) {
               intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action);  
            } else {
                intercepted = false;
            }
        } else {
                intercepted = true;
        }
        ...
    return handled;    
}

  这里首先判断事件是否为DOWN事件,如果是,则进行初始化,resetTouchState方法中会把mFirstTouchTarget的值置为null。这里为什么要进行初始化呢?原因就是一个完整的事件序列是以DOWN开始,以UP结束的。所以如果是DOWN事件,那么说明这是一个新的事件序列,故而需要初始化之前的状态。接着往下看,上面代码注释1处的条件如果满足,则执行下面的句子,mFirstTouchTarget 的意义是:当前 ViewGroup是否拦截了事件,如果拦截了,mFirstTouchTarget=null;如果没有拦截并交由子View来处理,则mFirstTouchTarget!=null。假设当前的 ViewGroup 拦截了此事件,mFirstTouchTarget!=null 则为false,如果这时触发ACTION_DOWN事件,则会执行 onInterceptTouchEvent(ev) 方法;如果触发的是ACTION_MOVE、ACTION_UP事件,则不再执行onInterceptTouchEvent(ev)方法,而是直接设置
intercepted=true,此后的一个事件序列均由这个
ViewGroup
处理。再往下看,上面代码注释2 处又出现了一个FLAG_DISALLOW_INTERCEPT标志位,它主要是禁止ViewGroup 拦截除了DOWN之外的事件,一般通
过子View的requestDisallowInterceptTouchEvent来设置。所以总结一下就是,当ViewGroup要拦截事件的时候,那么后续的事件序列都将交给它处理,而不用再调用onInterceptTouchEvent()方法了。所以,onInterceptTouchEvent()方法并不是每次事件都会调用的。接下来查看onInterceptTouchEvent()方法:

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

  onInterceptTouchEvent()方法默认返回false,不进行拦截。如果想要让ViewGroup拦截事件,那么应该在自定义的ViewGroup中重写这个方法。接着来看看dispatchTouchEvent()方法剩余的部分源码,如下
所示:

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
    final float x = ev.getX(actionIndex);
    final float y = ev.getY(actionIndex);
    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
    final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
    final View[] children = mChildren;
    for (int i = childrenCount - 1; i >= 0; i--) {//1
        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
        if (childWithAccessibilityFocus != null) {
            if (childWithAccessibilityFocus != child) {
                continue;
            }
            childWithAccessibilityFocus = null;
            i = childrenCount - 1;
        }

        if (!canViewReceivePointerEvents(child) ||
        !isTransformedTouchPointInView(x, y, child, null)) {//2
            ev.setTargetAccessibilityFocus(false);
            continue;
        }

        newTouchTarget = getTouchTarget(child);
        if (newTouchTarget != null) {
            newTouchTarget.pointerIdBits |= idBitsToAssign;
            break;
        }

        resetCancelNextUpFlag(child);
        if (dispatchTransformedTouchEvent(ev, false, child,idBitsToAssign)) {//3
            mLastTouchDownTime = ev.getDownTime();
            if (preorderedList != null) {
                for (int j = 0; j < childrenCount; j++) {
                    if (children[childIndex] == mChildren[j]) {
                        mLastTouchDownIndex = j;
                        break;
                    }
                }
            } else {
                mLastTouchDownIndex = childIndex;
            }
            mLastTouchDownX = ev.getX();
            mLastTouchDownY = ev.getY();
            newTouchTarget = addTouchTarget(child, idBitsToAssign);
            alreadyDispatchedToNewTouchTarget = true;
            break;
        }

        // The accessibility focus didn't handle the event, so clear
        // the flag and do a normal dispatch to all children.
        ev.setTargetAccessibilityFocus(false);
    }
    if (preorderedList != null) preorderedList.clear();
    ...
}

  在上面代码注释1处我们看到了for循环。首先遍历ViewGroup的子元素,判断子元素是否能够接收到点击事件,如果子元素能够接收到点击事件,则交由子元素来处理。需要注意这个for循环是倒序遍历的,即从最上层的子View开始往内层遍历。接着往下看注释2处的代码,其意思是判断触摸点位置是否在子View的范围内或者子View是否在播放动画。如果均不符合则执行continue语句,表示这个子View不符合条件,开始遍历下一个子View。接下来查看注释3处的dispatchTransformedTouchEvent方法做了什么,代码如下所
示:

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
        ...
    }

  如果有子View,则调用子View的dispatchTouchEvent(event)方法。如果ViewGroup没有子View,则调
用super.dispatchTouchEvent(event)方法。ViewGroup是继承View的,再来查看View的dispatchTouchEvent(event):

public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    boolean result = false;
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
}

  我们看到如果OnTouchListener不为null并且onTouch方法返回true,则表示事件被消费,就不会执行onTouchEvent(event);否则就会执行onTouchEvent(event)。可以看出OnTouchListener中的onTouch()方法优先级要高于onTouchEvent(event)方法。下面再来看看onTouchEvent()方法的部分源码:

public boolean onTouchEvent(MotionEvent event) {
    ...
        final int action = event.getAction();

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();
                            if (!focusTaken) {
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                ...
            }
            return true;
        }
    return false;
}

  从上面的代码可以看到,只要View的CLICKABLE和LONG_CLICKABLE有一个为true,那么onTouchEvent()就会返回true消耗这个事件。CLICKABLE和LONG_CLICKABLE代表View可以被点击和长按点击,可以通过View的setClickable和setLongClickable方法来设置,也可以通过View的
setOnClickListenter和setOnLongClickListener来设置,它们会自动将View设置为CLICKABLE和LONG_CLICKABLE。接着在ACTION_UP事件中会调用performClick()方法:

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

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

  从上面代码注释 1 处可以看出,如果 View 设置了点击事件 OnClickListener,那么它的onClick()方法
就会被执行。View事件分发机制的源码分析就讲到这里了,接下来介绍点击事件分发的传递规则。

点击事件分发的传递规则

  由前面事件分发机制的源码分析可知点击事件分发的这3个重要方法的关系,用伪代码来简单表示:

    public boolean dispatchTouchEvent(MotionEvent event){
        boolean result = false;
        if (onInterceptHoverEvent(event)){
            result = super.onTouchEvent(event);
        }else {
            result = child.dispatchTouchEvent(event);
        }
        return result;
    }

  onInterceptTouchEvent方法和onTouchEvent方法都在dispatchTouchEvent方法中调用。现在我们根据这段
伪代码来分析一下点击事件分发的传递规则。

  首先讲一下点击事件由上而下的传递规则,当点击事件产生后会由 Activity 来处理,传递给
PhoneWindow,再传递给DecorView,最后传递给顶层的ViewGroup。一般在事件传递中只考虑 ViewGroup
的 onInterceptTouchEvent 方法,因为一般情况下我们不会重写 dispatchTouchEvent()方法。对于根
ViewGroup,点击事件首先传递给它的dispatchTouchEvent()方法,如果该ViewGroup的onInterceptTouchEvent()方法返回true,则表示它要拦截这个事件,这个事件就会交给它的onTouchEvent()方法处理;如果onInterceptTouchEvent()方法返回false,则表示它不拦截这个事件,则这个事件会交给它的子元素的dispatchTouchEvent()来处理,如此反复下去。如果传递给底层的View,View是没有子View的,就会调用View的dispatchTouchEvent()方法,一般情况下最终会调用View的onTouchEvent()方法。

  接下来讲解点击事件由下而上的传递。当点击事件传给底层的 View 时,如果其onTouchEvent()方法返回true,则事件由底层的View消耗并处理;如果返回false则表示该View不做处理,则传递给父View的onTouchEvent()处理;如果父View的onTouchEvent()仍旧返回false,则继续传递给该父View的父View处理,如此反复下去。

以上内容摘自《Android进阶之光》

ArcGIS Pro中文帮助离线包安装指南:版本匹配与操作详解 本文详细指导如何为ArcGIS Pro安装中文帮助离线包,解决在线帮助访问不畅的问题。核心在于确保软件版本与离线包的精确匹配,并提供了从版本查看、离线包获取、安装步骤到软件内配置的完整操作流程。文中重点强调了版本匹配的重要性,并分享了高效使用本地帮助搜索及管理多版本帮助的实用技巧。 阅读详情

相关推荐

bartender 10.1破解版|bartender条码打印10.1

点击下载出处:BarTender10.1破解版 bartender10.1破解版是一款专业的条码打印软件,在行业内广受好评,通过使用bartender我们可以将标签设计这个复杂的过程变得更简便,在设计中快速调整,即刻打印,非常迅速,BarTender10.1破解版不仅支持的标签类型包括EPC Class 1 、Gen2 、ISO 18000-6b 、ISO 15693 、Tag-It 、I-CO...

weixin_44924147的博客 6万+

View的事件分发机制

1、点击事件的传递规则 点击事件,即要分析的对象是MotionEvent,所以,点击事件的分发,实际是MotionEvent 的分发过程,主要由dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。 public boolean dispatchTouchEvent(MotionEvent ev) 用来进行事件的分发,如果事件能够传递到当前的View,则调用此方法,返回结果受当前View的onTouchEvent和下一级的dispatchTouchEv

qq_34629725的博客 3815

【实战解析】Ubuntu双网卡冲突:如何解决同一网段下网关无法访问及上网问题

本文深入解析Ubuntu服务器双网卡配置在同一网段时引发的路由冲突问题,导致网关无法ping通及无法上网。通过分析路由表与Metric值原理,提供使用route命令调整优先级及修改Netplan配置文件的实战解决方案,确保主次路由明确,一劳永逸解决网络冲突。

f9g0h的博客 284

View的事件分发机制(源码分析)

View的事件分发机制点击事件的传递规则事件分发就是当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递过程就是分发过程,由三个很重要的方法来共同完成:dispatchTouchEvent,onInterceptTouchEvent和onTouchEventpublic boolean dispatchTouchEvent(MotionEvent ev)用来

benhuo931115的博客 464

数据工程师——ETL

文章摘要 ETL(数据抽取、转换、加载)是数据处理的核心流程,负责将分散数据源整合为高质量数据。ETL与ELT的主要区别在于转换步骤顺序,分别适用于不同规模数据场景。数据仓库通过ETL从多个数据源集成数据,支持分析决策。维度建模中的事实表记录业务指标,维度表提供上下文信息。缓慢变化维度(SCD)处理随时间变化的维度数据,常见类型包括直接覆盖型、新增版本型等。数据清洗解决数据缺失、重复、错误等问题,确保数据质量。数据血缘记录数据流转路径,便于问题排查和影响分析。常用ETL工具如Kettle通过转换和作业实现数

weixin_45729710的博客 1562

基于LSTM的股票价格预测系统,Django框架,Web的股票预测系统,训练+数据集+源码(无需安装环境版)

股票价格预测是投资领域的一个重点关注课题。由于股票价格受到诸多非线性因素的影响,得到精确的预测结果较为困难。使用 PyCharm 作为代码开发工具,通过下载好的股票金融数据,进行数据预处理,构建 LSTM 网络模型,训练模型并训练出最优模型,输出股票未来收盘价波动的走势,最后在训练好的模型上,设计股票模型应用系统,运行效率较高,对股票预测具有一定的积极意义。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 956

Android6.0源码解读之View点击事件分发机制

本篇博文是Android点击事件分发机制系列博文的第二篇,主要是从解读View类的源码入手,根据源码理清View点击事件分发原理,并掌握View点击事件分法机制。特别声明的是,本源码解读是基于最新的Android6.0版本。各位童鞋可以参考下面链接进行系统学习 (一)Android6.0触摸事件分发机制解读(二)Android6.0源码解读之View点击事件分发机制(三)Android6.0源码解

Android研发专栏 8274

OpenHarmony深度定制:从系统到模块的全景剖析与自定义模块实战

目标:在中新增一个算法库,让产品可直接调用。关闭不用的 20+ 系统部件,ROM 缩小 50 %;为自家芯片新增专用驱动模块,快速点亮开发板;内将核心算法库以部件形式集成,实现商业闭源。万物互联时代,差异化才是护城河。

m0_67505153的博客 1040

Android View 事件分发机制源码详解(ViewGroup篇)

前言我们在学习View的时候,不可避免会遇到事件的分发,而往往遇到的很多滑动冲突的问题都是由于处理事件分发时不恰当所造成的。因此,深入了解View事件分发机制的原理,对于我们来说是很有必要的。由于View事件分发机制是一个比较复杂的机制,因此笔者将写成两篇文章来详细讲述,分别是ViewGroup和View。因为我们平时所接触的View都不是单一的View,往往是由若干个ViewGroup组合而成,而

蓝天白云梦的csdn博客 1万+

6、Cocos2D游戏开发:精灵添加与角色实现

本文详细介绍了在Cocos2D游戏开发中如何添加背景精灵和玩家角色精灵。通过创建GameScene场景、组织项目资源、使用多分辨率图像适配不同设备,并深入讲解了将角色拆分为上下半身以实现更真实瞄准效果的原理。文章还解析了Cocos2D的图像搜索机制、锚点概念及其应用,以及如何通过父子节点关系管理复合角色。最终实现了猎人角色在场景中的正确显示与定位,为后续动画和交互功能打下基础。

e3f4g5的博客 90

华为OD机考双机位C卷 - 最左侧冗余覆盖子串 (Java)

华为OD机考双机位C卷 - 最左侧冗余覆盖子串 (Java), 提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。

算法大师! 59

View的事件分发机制源码解析

上一篇博文,详细讲述了View的事件分发机制,地址为:View的事件分发机制,这篇博客将从源码的角度进一步分析,并正式上一篇博文的结论。 我们知道当一个点击事件产生后,它的传递过程遵循如下顺序: Activity——>Window——>View 即事件总是先传递给Activity,Activity再传递给Window,最后Window再传递给顶级View。顶级View接受事件后,就会按照事件

qianhaifeng2012的博客 4216

【机器学习】一文搞懂算法模型之:Transformer

必会算法模型之:Transformer

商务合作 | 面试培训 | 职场规划 ==>主页扫码 5610

Android View 事件分发机制 源码解析 (上)

一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~首先我们先写个简单的例子来测试View的事件转发的流程~1、案例为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~MyButtonpackage com.example.zhy_event03; import android.conte

Hongyang 7万+

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android View 事件分发机制 源码解析(ViewGroup篇)

本期三篇文章目录(可点击跳转)一. Android TouchEvent事件传递机制初识 二. android点击事件传递源码讲解(ViewGroup) 三.android点击事件传递源码讲解(View) 1. 前言android点击 事件一直以来都是很多安卓程序员的心病,之前通过demo模拟总结出一些经验,但是不看源码的程序员不是好程序员,这段时间,系统的梳理了下整个事件传递的源码,希望可以

曹银飞的专栏 6866

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【Android】View 事件分发机制与源码解析

文章主要介绍了Android View的基础知识和工作原理。首先阐述了View的位置参数体系,包括坐标系、偏移量计算和获取绝对坐标的方法。其次详细讲解了MotionEvent触摸事件和TouchSlop最小滑动距离的概念。随后介绍了VelocityTracker速度追踪和GestureDetector手势检测的使用方法。最后重点分析了View的事件分发机制,包括三个关键方法dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent的协作流程,以及事

2401_87853595的博客 495

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Android 事件分发机制源码解析-view层

本篇文章我们专门来研究一下view层的事件分发机制,我们在学习过程中总会碰到关于事件分发的各种问题,如onTouch和onTouchEvent的关系,setOnTouchListener和setOnClickListener的关系等等,类似这样的问题很多,结论我们都知道,有的时候是死记硬背的,记不长久,本篇文章我们来从源码的角度来分析总结一下各种关系,这样才能理解,便于记忆。 分析工具 //And...

ostracod 244

Connor学Android - View事件分发机制源码解析

Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/W5DRa,话不多说我们马上开始!

ConnorYan的博客 444

Android View 事件分发机制 源码解析

一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1、案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~ MyButton ? 1 2 3

lshihao1的专栏 923

ViewGroup事件分发机制源码解析(一)View篇

本篇基于Android9.0的源码进行分析View的事件分发机制。 View事件处理机制源码分析 布局文件如下,根View是一个LinearLayout,内部放了两个view分别是Button和ImageView. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://s.

周将的博客 532
上一篇: View的滑动和属性动画
下一篇: View的工作流程
「已注销」
博客等级 码龄9年 13粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值