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

概述

本篇主要分析的是touch事件的分发机制,网上关于这个知识点的分析文章非常多。但是还是想通过结合自身的总结,来加深自己的理解。对于事件分发机制,我将使用两篇文章对其进行分析,一篇是针对View的事件分发机制解析,一篇是针对ViewGroup的事件分发机制解析。本片是对View的事件分发机制进行解析,主要采用案例结合源码的方式来进行分析。

前言

在分析事件分发机制之前,我们先来学习一下基本的知识点,以便后面的理解。

View中有两个关键方法参与到Touch事件分发

dispatchTouchEvent(MotionEvent event) 和 onTouchEvent(MotionEvent event)

所有Touch事件类型都被封装在对象MotionEvent中,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP等等。

每个执行动作必须执行完一个完整的流程,再继续进行下一个动作。比如:ACTION_DOWN事件发生时,必须等这个事件的分发流程执行完(包括该事件被提前消费),才会继续执行ACTION_MOVE或者ACTION_UP的事件。

案例分析

为了能够清楚的监视事件的分发过程,我们采用自定义View的形式,查看内部的方法执行过程。

上代码:

package com.yuminfeng.touch;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;

public class MyButton extends Button {

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i("yumf", "MyButton=====dispatchTouchEvent ACTION_DOWN");
            break;

        case MotionEvent.ACTION_UP:
            Log.i("yumf", "MyButton=====dispatchTouchEvent ACTION_UP");
            break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i("yumf", "MyButton=====onTouchEvent ACTION_DOWN");
            break;

        case MotionEvent.ACTION_UP:
            Log.i("yumf", "MyButton=====onTouchEvent ACTION_UP");
            break;
        }
        return super.onTouchEvent(event);
    }

}

在XML布局中引用该控件,非常简单。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.yuminfeng.myviewpager.FirstActivity" >

    <com.yuminfeng.touch.MyButton
        android:id="@+id/mybutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

以上代码都非常简单,没有什么逻辑,就是重写Button的dispatchTouchEvent和onTouchEvent的方法,然后引用该控件即可。

然后执行代码,查看日志打印,如下:

由此看到,当点击控件时,首先执行的是dispatchTouchEvent方法,然后再执行onTouchEvent的方法。

如果此时我们修改dispatchTouchEvent的返回值为true时(默认为false),那么onTouchEvent方法便不再执行,如下:

流程示意图如下:

接着我们恢复之前的返回值false,继续让mybutton设置一个setOnTouchListener监听事件,关键代码如下:

     mybutton = (Button) findViewById(R.id.mybutton);

        mybutton.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.i("yumf", "Activity=====onTouch ACTION_DOWN");
                    break;

                case MotionEvent.ACTION_UP:
                    Log.i("yumf", "Activity=====onTouch ACTION_UP");
                    break;
                }
                return false;
            }
        });

执行后,日志打印如下:

由此我们可以看到,首先执行方法dispatchTouchEvent,然后再执行OnTouchListener中onTouch方法,最后执行onTouchEvent方法。

同上,如果我们继续修改dispatchTouchEvent的返回值为true时,那么后面的方法onTouch,onTouchEvent均不执行。

如果我们修改onTouch的返回值为true,那么后面的onTouchEvent事件就不会执行了。

流程示意图如下:

如上,恢复默认返回值false,然后在button上设置一个监听点击事件,代码如下:

     mybutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.i("yumf", "Activity=====onClick");
            }
        });

执行后,查看日志打印信息,如下:

由此我们可以知道,在完整的事件结束之后(从ACTION_DOWN开始,到ACTION_UP结束),这时才会去执行button的onClick方法。

综合以上所述,View在处理Touch事件时,都是从dispatchTouchEvent方法开始的,因此我们在分析源码时,可以从该方法入手。

源码阅读

我们当前的MyButton是继承自Button,而Button又是继承自TextView,TextView继承自View,逐步往上查看,可以发现父类的dispatchTouchEvent方法,就是View的dispatchTouchEvent方法。如下:

   /**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            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;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn‘t want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

如上代码中,我们来逐一进行分析,首先是

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

通过查看mInputEventConsistencyVerifier,得知这段代码主要是用来调试的,可以不用关注。接着继续查看下一段代码

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

当执行ACTION_DOWN事件时,进入方法stopNestedScroll()中,进入该方法中

 /**
     * Stop a nested scroll in progress.
     *
     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
     *
     * @see #startNestedScroll(int)
     */
    public void stopNestedScroll() {
        if (mNestedScrollingParent != null) {
            mNestedScrollingParent.onStopNestedScroll(this);
            mNestedScrollingParent = null;
        }
    }

该方法主要是用来停止View的滑动,当一个滚动的view不是当前进行接收事件的View时不会受到影响。下面的一段代码是关键的代码,我们来看看

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            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;
            }
        }

上面的代码中,首先根据安全策略过滤event,来确定是否响应这个事件,返回true表示响应。响应该事件后,将mListenerInfo赋值给ListenerInfo对象。那么这个mListenerInfo到底是什么呢,我们现在来分析一下mListenerInfo的初始化

首先,我们可以在View的属性中,能看到该对象的引用:

ListenerInfo mListenerInfo;

接着,在getListenerInfo()方法中初始化:

    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

最后,在为该View的对象设置监听器时,会将对应的监听器对象返回赋值给mListenerInfo对象,如下:

/**
     * Register a callback to be invoked when focus of this view changed.
     *
     * @param l The callback that will run.
     */
    public void setOnFocusChangeListener(OnFocusChangeListener l) {
        getListenerInfo().mOnFocusChangeListener = l;
    }

    /**
     * Returns the focus-change callback registered for this view.
     *
     * @return The callback, or null if one is not registered.
     */
    public OnFocusChangeListener getOnFocusChangeListener() {
        ListenerInfo li = mListenerInfo;
        return li != null ? li.mOnFocusChangeListener : null;
    }

    /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

    /**
     * Register a callback to be invoked when this view is clicked and held. If this view is not
     * long clickable, it becomes long clickable.
     *
     * @param l The callback that will run
     *
     * @see #setLongClickable(boolean)
     */
    public void setOnLongClickListener(OnLongClickListener l) {
        if (!isLongClickable()) {
            setLongClickable(true);
        }
        getListenerInfo().mOnLongClickListener = l;
    }

    /**
     * Register a callback to be invoked when a touch event is sent to this view.
     * @param l the touch listener to attach to this view
     */
    public void setOnTouchListener(OnTouchListener l) {
        getListenerInfo().mOnTouchListener = l;
    }

如上,其实里面涉及的方法非常多,我只抽出了几个常见的方法,如:setOnClickListener,setOnTouchListener等。

所以说当我们给View的对象设置监听器时,通过回调的方式,最后都会赋值到mListenerInfo对象中。mListenerInfo类里面包含了许多的监听器类型,如下:

    static class ListenerInfo {
        /**
         * Listener used to dispatch focus change events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        protected OnFocusChangeListener mOnFocusChangeListener;

        /**
         * Listeners for layout change events.
         */
        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;

        /**
         * Listeners for attach events.
         */
        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;

        /**
         * Listener used to dispatch click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        public OnClickListener mOnClickListener;

        /**
         * Listener used to dispatch long click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        protected OnLongClickListener mOnLongClickListener;

        /**
         * Listener used to build the context menu.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        protected OnCreateContextMenuListener mOnCreateContextMenuListener;

        private OnKeyListener mOnKeyListener;

        private OnTouchListener mOnTouchListener;

        private OnHoverListener mOnHoverListener;

        private OnGenericMotionListener mOnGenericMotionListener;

        private OnDragListener mOnDragListener;

        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;

        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
    }

完成了监听器类型的赋值后,我们分析继续下面的代码逻辑:

        if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

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

这里在if条件里面我们看到了一个属性的方法li.mOnTouchListener.onTouch(this, event),这就是我们在Activity中设置的setOnTouchListener中,重写的onTouch方法。当返回为true时,result = true,这时便不执行下面代码中的onTouchEvent(event)方法。result 为false时,才执行onTouchEvent(event)方法。这段关键性的代码中,对应了我之前所做的实验结果。

下面,我们继续分析方法View的onTouchEvent(MotionEvent event)的内部执行。

 /**
     * Implement this method to handle touch screen motion events.
     * <p>
     * If this method is used to detect click actions, it is recommended that
     * the actions be performed by implementing and calling
     * {@link #performClick()}. This will ensure consistent system behavior,
     * including:
     * <ul>
     * <li>obeying click sound preferences
     * <li>dispatching OnClickListener calls
     * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
     * accessibility features are enabled
     * </ul>
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn‘t respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }

        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don‘t have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress) {
                            // 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)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we‘re inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);
                    removeTapCallback();
                    removeLongPressCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    drawableHotspotChanged(x, y);

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            setPressed(false);
                        }
                    }
                    break;
            }

            return true;
        }

        return false;
    }

首先看这个方法的说明,实现这个方法来处理触摸屏幕的动作事件。如果这个方法被用来检测点击动作,它是建议执行和调用的操作。如果这个事件被处理,返回true,否则返回false。

现在我们来看逐一代码,第一个if语句块中,判断View的状态是否可用,如果不可用则设置为不可按压,否则为设置为可点击和可长按。然后下面在可点击和可长按的条件下,进行touch事件的逻辑处理。在这个if语句内部有switch条件判断,将分别对不同的事件进行处理,如MotionEvent.ACTION_UP,MotionEvent.ACTION_DOWN,MotionEvent.ACTION_CANCEL 和MotionEvent.ACTION_MOVE几个不同的事件。下面我们将逐一对其进行分析。

首先是MotionEvent.ACTION_UP中:

判断prepressed为true后,进入执行体;

设置setPressed(true, x, y);

判断mHasPerformedLongPress是否执行长按操作,如果mOnLongClickListener.onLongClick 返回true时,mHasPerformedLongPress = true,这时便不会执行performClick()方法。否则继续执行如下,判断mPerformClick为空,初始化一个实例。添加到消息队列中,如果添加失败则直接执行performClick()方法,否则在PerformClick对象的run中执行performClick()。查看一下performClick()方法,如下:

  /**
     * Call this view‘s OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        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);
        return result;
    }

如上,我们可以看到一个非常熟悉的方法onClick。在if中判断,如果我们给View设置了mOnClickListener的监听接口,在这里我们会回调mOnClickListener中的onClick方法。(原来点击事件的onClick方法是在ACTION_UP时,执行的)

接着,看到创建UnsetPressedState对象,然后执行UnsetPressedState对象中的run方法,我们进入这个方法查看,

  private final class UnsetPressedState implements Runnable {
        @Override
        public void run() {
            setPressed(false);
        }
    }

    /**
     * Sets the pressed state for this view.
     *
     * @see #isClickable()
     * @see #setClickable(boolean)
     *
     * @param pressed Pass true to set the View‘s internal state to "pressed", or false to reverts
     *        the View‘s internal state from a previously set "pressed" state.
     */
    public void setPressed(boolean pressed) {
        final boolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);

        if (pressed) {
            mPrivateFlags |= PFLAG_PRESSED;
        } else {
            mPrivateFlags &= ~PFLAG_PRESSED;
        }

        if (needsRefresh) {
            refreshDrawableState();
        }
        dispatchSetPressed(pressed);
    }

可以看到,这里面是用来取消mPrivateFlags 中的PFLAG_PRESSED标志,然后刷新背景。

ACTION_UP最后一步,removeTapCallback() 移除消息队列中的之前加入的所有回调操作。

接着分析MotionEvent.ACTION_DOWN中内部代码:

首先mHasPerformedLongPress = false,设置长按操作为false。

接着判断View是否处在可滑动的容器中,如果为false,则设置View的PRESSED状态和检查长按动作。

接着分析MotionEvent.ACTION_CANCEL的事件:

代码非常简单,设置PRESSED为false,移除所有的回调,移除长按的回调。

最后来分析MotionEvent.ACTION_MOVE的事件:

判断触摸点是否移出View的范围,如果移出了则执行removeTapCallback(),取消所有的回调。接着判断是否包含PRESSED标识,如果包含则执行方法removeLongPressCallback() 和 setPressed(false);

到这里我们可以知道,onTouchEvent方法中处理Touch事件的具体操作,并控制了View的点击事件。在如果在点击View时,想要长按和短按都产生效果,即setOnLongClickListener和setOnClickListener都能够执行的话,只需要在setOnLongClickListener的onLongClick方法中返回false,这时两个方法便都能执行。

至此关于View的Touch事件分发流程已经分析完成,下一篇将介绍ViewGroup的分发机制。

时间: 2024-10-07 10:37:39

Android 源码解析View的touch事件分发机制的相关文章

Android 源码分析(十一) 事件传递机制

一.介绍 Android三种事件类型:ACTION_DOWN,ACTOIN_MOVE,ACTION_UP. 事件传递的三个阶段: 分发(Dispatch) 方法:public boolean dispatchTouchEvent(MotionEvent ev) 拦截(Intercept) 方法:public boolean onInterceptTouchEvent(MotionEvent ev) 消费(Consume) 方法:public boolean onTouchEvent(Motion

Android查缺补漏(View篇)--事件分发机制源码分析

在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)--事件分发机制> ,先来看一下本篇的分析思路,一会儿会按照事件传递的顺序,针对以下几点进行源码分析: Activity对点击事件的分发过程 PhoneWindow是如何处理点击事件的 顶级View对点击事件的分发过程 View对点击事件的处理过程 Activity对点击事件的分发过程 通过上一篇博文中我们

Android View体系(八)从源码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller Android View体系(五)从源码解析View的事件分发机制 Android View体系(六)从源码解析Activity的构成 Android View体系(七)从源码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程,接下来我们

Android View体系(七)从源码解析View的measure流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller Android View体系(五)从源码解析View的事件分发机制 Android View体系(六)从源码解析Activity的构成 前言 在上一篇我们了解了Activity的构成后,开始了解一下View的工作流程,就是measure.layout和draw.measure

Android源码解析——LruCache

Android源码解析--LruCache LRU 在读LruCache源码之前,我们先来了解一下这里的Lru是什么.LRU全称为Least Recently Used,即最近最少使用,是一种缓存置换算法.我们的缓存容量是有限的,它会面临一个问题:当有新的内容需要加入我们的缓存,但我们的缓存空闲的空间不足以放进新的内容时,如何舍弃原有的部分内容从而腾出空间用来放新的内容.解决这个问题的算法有多种,比如LRU,LFU,FIFO等.需要注意区分的是LRU和LFU.前者是最近最少使用,即淘汰最长时间未

Android View体系(五)从源码解析View的事件分发机制

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller 前言 三年前写过事件分发机制的文章但是写的不是很好,所以重新再写一篇,关于事件分发机制的文章已经有很多,但是希望我这篇是最简洁.最易懂的一篇. 1.处理点击事件的方法 View的层级 我们知道View的结构是树形的结构,View可以放在ViewGroup中,这个ViewGro

Android源码解析——Toast

简介 Toast是一种向用户快速提供少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源码,并探明它的显示及隐藏机制. 源码解析 Toast 我们从Toast的最简单调用开始,它的调用代码是: Toast.makeText(context,"Show toast",Toast.LENGTH_LONG).show(); 在上面的代码中,我们是先调用Toast的静态方法来创建一个

android源码解析--Handler

转载自:http://blog.csdn.net/lilu_leo/article/details/8143205 开始,先看下android官方对于Handler的解释: [java] view plaincopy /** * A Handler allows you to send and process {@link Message} and Runnable * objects associated with a thread's {@link MessageQueue}.  Each 

android源码解析之(十五)--&gt;Activity销毁流程

继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A