我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android View事件分发机制详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android View事件分发机制详解

准备了一阵子,一直想写一篇事件分发的文章总结一下,这个知识点实在是太重要了。

一个应用的布局是丰富的,有TextView,ImageView,Button等,这些子View的外层还有ViewGroup,如RelativeLayout,LinearLayout。作为一个开发者,我们会思考,当点击一个按钮,Android系统是怎样确定我点的就是按钮而不是TextView的?然后还正确的响应了按钮的点击事件。内部经过了一系列什么过程呢?

先铺垫一些知识能更加清晰的理解事件分发机制:
1. 通过setContentView设置的View就是DecorView的子view,即DecorView是父容器。
2. 点击屏幕时,在手指按下和抬起间,会产生很多事件,down…move…move…up,中间会有很多的move事件,这一系列的事件为一个事件序列
3. dispatchTouchEvent方法用于分发事件
4. onInterceptTouchEvent方法用于拦截事件
5. onTouchEvent方法用于处理事件

当一个点击事件(MotionEvent)产生后,事件最先传递给当前的界面(Activity),这点是很好理解的。 Activity再将事件传递给窗口(Window),然后Window将事件传递给顶级View(DecorView)。此时,事件已经到达了View了。之后顶级View就会按照事件分发机制去分发事件。具体是这样的:

对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这时它的 dispatchTouchEvent 方法就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用。如果这个ViewGroup的onInterceptTouchEvent方法返回false,就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件被最终处理。
如果一个View的onTouchEvent方法返回false,那么它的父容器的onTouchEvent方法会被调用,如果它的父容器的onTouchEvent方法还是返回false,那就继续往上抛,当所有的元素都不处理这个事件,那么这个事件会最终传递给Activity处理,即Activity的onTouchEvent方法会被调用。

好了,现在已经铺垫了基础,那么接下来就从源码的角度来分析事件分发机制。

当然是从Activity的dispatchTouchEvent方法开始分析。源码如下:


public boolean dispatchTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    onUserInteraction();
  }
  if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
  }
  return onTouchEvent(ev);
}

如果当前事件是down的话,就调用onUserInteraction方法,onUserInteraction是一个空方法,我们可以暂时不搭理。然后调用getWindow方法获取到当前Activity关联的Window,Window再调用superDispatchTouchEvent方法将事件传入进行分发。
如果superDispatchTouchEvent方法返回true的话, view已经处理了事件。整个事件循环结束。如果返回false,没有view处理这个事件。事件往上抛,那就Activity自己处理了,即Activity的onTouchEvent方法会被调用。

因为想要知道事件的整个分发过程,现在关注的是Window的superDispatchTouchEvent方法,那么就跟进去看看:

public abstract boolean superDispatchTouchEvent(MotionEvent event);

Window是一个抽象类,superDispatchTouchEvent是一个抽象的方法,那么我们必须要找到window的实现类才行,可是茫茫人海怎么找呢?看到window类的说明就明白了


* <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window

意思是Window存在唯一的实现是android.view.PhoneWindow

那么PhoneWindow里的superDispatchTouchEvent方法就是我们要找的信息,如下:


@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
  return mDecor.superDispatchTouchEvent(event);
}

直接将事件传递给了DecorView。这时事件已经是到达View了哦。

那么跟进DecorView的superDispatchTouchEvent方法看看,如下:


public boolean superDispatchTouchEvent(MotionEvent event) {
  return super.dispatchTouchEvent(event);
}

内部调用了父类的dispatchTouchEvent方法,那么DecorView的父类是什么呢?DecorView肯定是View的,那么刚才开篇提到,我们通过setContentView设置的View,是DecorView的子View。那么更加准确的说DecorView是一个ViewGroup。

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

可以看到DecorView是继承自FrameLayout,FrameLayout是ViewGroup,也就是说DecorView是一个ViewGroup。

那么现在只需要关注ViewGroup的dispatchTouchEvent方法。继续前进

ViewGroup的事件分发

ViewGroup的dispatchTouchEvent方法如下:


@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //代码省略
    // Check for interception.
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
      final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
      if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
      } else {
        intercepted = false;
      }
    } else {
      // There are no touch targets and this action is not an initial down
      // so this view group continues to intercept touches.
      intercepted = true;
    }
    //代码省略
    if (!canceled && !intercepted) {
    //代码省略
        final int childrenCount = mChildrenCount;
        if (newTouchTarget == null && childrenCount != 0) {
          final float x = ev.getX(actionIndex);
          final float y = ev.getY(actionIndex);
          // Find a child that can receive the event.
          // Scan children from front to back.
          final ArrayList<View> preorderedList = buildOrderedChildList();
          final boolean customOrder = preorderedList == null
              && isChildrenDrawingOrderEnabled();
          final View[] children = mChildren;
          for (int i = childrenCount - 1; i >= 0; i--) {
            final int childIndex = customOrder
                ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)
                ? children[childIndex] : preorderedList.get(childIndex);
            // If there is a view that has accessibility focus we want it
            // to get the event first and if not handled we will perform a
            // normal dispatch. We may do a double iteration but this is
            // safer given the timeframe.
            if (childWithAccessibilityFocus != null) {
              if (childWithAccessibilityFocus != child) {
                continue;
              }
              childWithAccessibilityFocus = null;
              i = childrenCount - 1;
            }
            if (!canViewReceivePointerEvents(child)
                || !isTransformedTouchPointInView(x, y, child, null)) {
              ev.setTargetAccessibilityFocus(false);
              continue;
            }
            newTouchTarget = getTouchTarget(child);
            if (newTouchTarget != null) {
              // Child is already receiving touch within its bounds.
              // Give it the new pointer in addition to the ones it is handling.
              newTouchTarget.pointerIdBits |= idBitsToAssign;
              break;
            }
            resetCancelNextUpFlag(child);
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
              // Child wants to receive touch within its bounds.
              mLastTouchDownTime = ev.getDownTime();
              if (preorderedList != null) {
                // childIndex points into presorted list, find original index
                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();
        }
        if (newTouchTarget == null && mFirstTouchTarget != null) {
          // Did not find a child to receive the event.
          // Assign the pointer to the least recently added target.
          newTouchTarget = mFirstTouchTarget;
          while (newTouchTarget.next != null) {
            newTouchTarget = newTouchTarget.next;
          }
          newTouchTarget.pointerIdBits |= idBitsToAssign;
        }
      }
    }
    // Dispatch to touch targets.
    if (mFirstTouchTarget == null) {
      // No touch targets so treat this as an ordinary view.
      handled = dispatchTransformedTouchEvent(ev, canceled, null,
          TouchTarget.ALL_POINTER_IDS);
    }
    //代码省略
    return handled;
}

代码比较长,一点一点分析,先看到一开始的判断

if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)

mFirstTouchTarget != null的意义是ViewGroup不拦截事件并将事件交由子元素处理,先这样记着,这从后面的addTouchTarget方法可以得出结论的。

然后又会来到这个if判断。


if (!disallowIntercept) {
  intercepted = onInterceptTouchEvent(ev);
  ev.setAction(action); // restore action in case it was changed
}

那我们看看disallowIntercept。而disallowIntercept的赋值过程中,有一个 FLAG_DISALLOW_INTERCEPT 标记位

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

这个 FLAG_DISALLOW_INTERCEPT 标记位是可以通过requestDisallowInterceptTouchEvent方法来设置的。

回到if (!disallowIntercept)的判断,进入这个if判断后,就会来到

intercepted = onInterceptTouchEvent(ev);

调用onInterceptTouchEvent方法,询问ViewGroup是否拦截事件。

读到这里,可以回忆下开篇时铺垫的结论,对于ViewGroup,点击事件产生后,首先会传递给它,这时它的 dispatchTouchEvent 方法就会被调用,接着会调用它的onInterceptTouchEvent方法,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用。如果返回false表示不拦截,通常ViewGroup也是不拦截事件的。

那现在先分析不拦截的情况,不拦截那就好办了的。经过一系列的判断,就会来到一个for循环遍历。

for (int i = childrenCount - 1; i >= 0; i--)

这时ViewGroup开始分发传递事件,遍历子元素了。

首先肯定需要过滤掉一些无关点击事件的子元素的,判断子元素是否能够接收点击事件,点击事件的坐标是否落在子元素区域内。


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

如果不能够接收点击事件或者点击事件的坐标没有落在子元素区域,就会跳出当前循环,继续遍历下一个子元素。这下就知道了Android系统为什么能够知道点击的是Button而不是TextView,其实内部就只是做了一个判断嘛。

那么继续分析,子元素符合以上两个条件后,就将事件传递给这个子元素。会来到了这个判断。

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))

执行dispatchTransformedTouchEvent方法,将子元素传进去。这个方法很重要,那么跟进看看



private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
    View child, int desiredPointerIdBits) {
  final boolean handled;
  // Canceling motions is a special case. We don't need to perform any transformations
  // or filtering. The important part is the action, not the contents.
  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;
  }
  //代码省略
}

我们看到child!=null的情况,如果子元素不为空,调用子元素的dispatchTouchEvent方法继续分发事件,同时返回处理结果布尔值,这时就将事件传递到了子View处理。完成了一轮的事件分发。这个方法先到这里就好。

再看回ViewGroup的dispatchTouchEvent方法,如果dispatchTransformedTouchEvent方法返回true的话,这时事件已经传递给子元素处理,ViewGroup已经不管这个事件了。
那么就会进入if语句,最后会来到addTouchTarget方法,这个方法之前是提到过的,用于mFirstTouchTarget标记位的赋值。

那跟进这个方法看看



private TouchTarget addTouchTarget(View child, int pointerIdBits) {
  TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
  target.next = mFirstTouchTarget;
  mFirstTouchTarget = target;
  return target;
}

其实就是让mFirstTouchTarget指向子元素。

执行完这个addTouchTarget方法后,最终会到break语句,那么就会跳出整个for循环体。ViewGroup结束分发过程!

又回到dispatchTransformedTouchEvent方法,如果dispatchTransformedTouchEvent方法返回false,那么if语句的一大段代码都不执行了,而是回到for循环继续遍历子元素进行分发。如此重复完成事件的传递过程。

现在分析ViewGroup拦截事件的情况,如果ViewGroup拦截事件的话,那么就会进入以下这个判断


if (mFirstTouchTarget == null) {
  // No touch targets so treat this as an ordinary view.
  handled = dispatchTransformedTouchEvent(ev, canceled, null,
  TouchTarget.ALL_POINTER_IDS);
}

注意到dispatchTransformedTouchEvent方法的第三个参数child传入的是null,那么就是在dispatchTransformedTouchEvent方法中走以下的语句


if (child == null) {
  handled = super.dispatchTouchEvent(event);
}

而ViewGroup是继承自View的,那么就是ViewGroup自己处理事件了。这点我们以下分析了View的事件分发过程就能搞明白了。

以上就是ViewGroup的事件分发

那么现在分析已经将事件传递给了子View的情况,View继续调用dispatchTouchEvent方法,那我们看看View的dispatchTouchEvent方法。

View的事件分发

View的dispatchTouchEvent方法源码如下:



public boolean dispatchTouchEvent(MotionEvent event) {
//代码省略
  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;
}

相比于ViewGroup的dispatchTouchEvent方法,View的dispatchTouchEvent方法代码量少了。也相对简单些了。
首先会来到如下判断:


if (li != null && li.mOnTouchListener != null
    && (mViewFlags & ENABLED_MASK) == ENABLED
    && li.mOnTouchListener.onTouch(this, event))

li变量在哪里被赋值的呢?通常是在setOnClickListener方法或setOnTouchListener方法的时候。


public void setOnClickListener(@Nullable OnClickListener l) {
  if (!isClickable()) {
    setClickable(true);
  }
  getListenerInfo().mOnClickListener = l;
}
public void setOnTouchListener(OnTouchListener l) {
  getListenerInfo().mOnTouchListener = l;
}

而这个getListenerInfo()如下:


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

ListenerInfo是一个内部类,里面存放的是各种监听事件的引用。

之后会判断如下条件:

li.mOnTouchListener != null

同理只要setOnTouchListener方法设置了,这个引用就不空。这些都是好理解的。那关键到了,

li.mOnTouchListener.onTouch(this, event)

到了最后一个条件。这个onTouch方法是我们去实现的,它也返回一个布尔值,如果返回true的话,那么就会进入这个if判断最终返回true,跳出整个方法,那么我们可以看到接下来的onTouchEvent方法是不会得到执行的。
也就是onTouch的执行在onTouchEvent之前。那么如果我们也调用了setOnClickListener方法监听点击事件的话,onClick方法是在哪里调用的呢?我们有理由相信是在onTouchEvent方法里调用的。那么就跟进看看。


public boolean onTouchEvent(MotionEvent event) {
  //代码省略
  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 (!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();
              }
            }
          }
       //代码省略 
        break;
    }
    return true;
  }
  return false;
}

只要CLICKABLE或LONG_CLICKABLE不空, 就会处理这个事件,然而怎么保证CLICKABLE或LONG_CLICKABLE不空呢?其实细心的你会发现,刚才上面贴出的setOnClickListener源代码中,会将CLICKABL属性设置会true


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

这样就能进入if判断去处理这个事件了,之后就会来到performClick()方法,应该就是它了,跟进去看看吧。


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;
}

熟悉接口回调机制的你,一定也读懂了performClick()方法的源码,

li.mOnClickListener.onClick(this);

是在执行这行代码时,调用了我们熟悉的onClick方法

以上就是View的事件分发机制。

此时已经将事件分发机制分析完了,由于我的技术的原因,驾驭的不好,有些关键点还是没分析清楚,但我相信学完了这篇文章能让我和你都对事件分发机制的实现有一个大致的认识,有这个已经可以了,之后还可以一点点去强化锻炼,深入理解事件分发机制。才能为自定义控件铺垫良好的基础。

您可能感兴趣的文章:Android View 事件分发机制详解30分钟搞清楚Android Touch事件分发机制Android事件分发机制(上) ViewGroup的事件分发Android View的事件分发机制谈谈对Android View事件分发机制的理解Android事件分发机制(下) View的事件处理android事件分发机制的实现原理Android事件分发机制的详解Android从源码的角度彻底理解事件分发机制的解析(上)Android从源码的角度彻底理解事件分发机制的解析(下)


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android View事件分发机制详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android View 事件分发机制详解

Android开发,触控无处不在。对于一些 不咋看源码的同学来说,多少对这块都会有一些疑惑。View事件的分发机制,不仅在做业务需求中会碰到这些问题,在一些面试笔试题中也常有人问,可谓是老生常谈了。我以前也看过很多人写的这方面的文章,不是说
2022-06-06

Android View事件分发机制详解

准备了一阵子,一直想写一篇事件分发的文章总结一下,这个知识点实在是太重要了。 一个应用的布局是丰富的,有TextView,ImageView,Button等,这些子View的外层还有ViewGroup,如RelativeLayout,Lin
2022-06-06

Android View的事件分发机制

一.Android View框架提供了3个对事件的主要操作概念。 1、事件的分发机制,dispatchTouchEvent。主要是parent根据触摸事件的产生位置,以及child是否愿意负责处理该系列事件等状态,向其child分发事件的机
2022-06-06

Android View的事件分发机制简单理解

View的事件分发机制1、前言2、基础2.1 MotionEvent类的基本用法。2.2事件分发的3个重要方法2.3分发事件的组件3.点击事件的传递规则3.1自上而下的分发事件3.2自下而上的消耗事件4.流程图5.结论 1、前言 在开发过程
2022-06-06

Android事件分发机制(下) View的事件处理

综述在上篇文章Android中的事件分发机制(上)——ViewGroup的事件分发中,对ViewGroup的事件分发进行了详细的分析。在文章的最后ViewGroup的dispatchTouchEvent方法调用dispatchTransfo
2022-06-06

Android事件的分发机制详解

在分析Android事件分发机制前,明确android的两大基础控件类型:View和ViewGroup。View即普通的控件,没有子布局的,如Button、TextView. ViewGroup继承自View,表示可以有子控件,如Linea
2022-06-06

Android事件分发机制的详解

Android事件分发机制我们只考虑最重要的四个触摸事件,即:DOWN,MOVE,UP和CANCEL。一个手势(gesture)是一个事件列,以一个DOWN事件开始(当用户触摸屏幕时产生),后跟0个或多个MOVE事件(当用户四处移动手指时产
2023-05-30

谈谈对Android View事件分发机制的理解

最近因为项目中用到类似一个LinearLayout中水平布局中,有一个TextView和Button,然后对该LinearLayout布局设置点击事件,点击TextView能够触发该点击事件,然而奇怪的是点击Button却不能触发。然后go
2022-06-06

Android 事件分发机制 讲解

1、分发事件的组件 分发事件的组件,也称为分发事件者,包括Activity、ViewGroup和View。它们三者的一般结构为:从上图中可以看出,Activity包括了ViewGroup,ViewGroup又可以包含多个View。 2、分发
2022-06-06

Android自定义View事件分发流程详解

这篇文章主要为大家介绍了Android自定义View事件分发流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-02

Android事件分发机制

事件分发流程相关 一个事件发生后,首先从Acrtivity开始传递,然后一层一层往下传,从上往下调用dispatchTouchEvent方法传递事件: Activity——>PhoneWindow——>DecorView——>ViewGro
2022-06-06

Android事件分发机制(上) ViewGroup的事件分发

综述Android中的事件分发机制也就是View与ViewGroup的对事件的分发与处理。在ViewGroup的内部包含了许多View,而ViewGroup继承自View,所以ViewGroup本身也是一个View。对于事件可以通过View
2022-06-06

深入浅析Android项目中的 View事件分发机制

本篇文章为大家展示了深入浅析Android项目中的 View事件分发机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。具体方法如下:public class MyButton extends But
2023-05-31

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

文章目录1. 分发顺序2.源码分析2.1 Activity中的分发流程dispatchTouchEventonTouchEvent总结2.2 ViewGroup中的分发流程dispatchTouchEventonInterceptTouch
2022-06-06

Android View 绘制机制的详解

View 绘制机制一、 View 树的绘图流程当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理.绘制是从根节点开始,对布局树进行 measure 和 draw。整个 View 树
2023-05-31

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录