手写Android事件分发

2023-11-03

Android事件分发原理搞清楚可以辅助我们解决很多实际项目中遇到的事件冲突等问题

1.进入正题之前,问大家几个事件相关的问题?

标签: dispatchTouchEvent()


Q1: Android点击事件传递规则是怎样的?(下面几步仔细阅读2遍,有助于加深对事件传递的理解)

A1:

Step1: 当点击事件产生后,会由 Activity 首先来处理

	/**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        // 如果没有任何View处理触摸事件,则转交给Activity的onTouchEvent自己处理
        return onTouchEvent(ev);
    }

	/**
     * Called when a touch screen event was not handled by any of the views
     * under it.  This is most useful to process touch events that happen
     * outside of your window bounds, where there is no view to receive it.
     *
     * @param event The touch screen event being processed.
     *
     * @return Return true if you have consumed the event, false if you haven't.
     * The default implementation always returns false.
     */
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

Step2: 上述步骤1传递给 PhoneWindowgetWindow().superDispatchTouchEvent(ev),再传递给 DecorView,最后传递给顶层的ViewGroup

	// This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    ...
    ...

	// PhoneWindow 的 superDispatchTouchEvent 方法
 	@Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

	// DecorView 的 superDispatchTouchEvent 方法
	// DecorView 继承 FrameLayout (FrameLayout extends ViewGroup)
	public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

Step3: 对于根 ViewGroup,点击事件首先传递给它的 dispatchTouchEvent() 方法
,如果该 ViewGrouponInterceptTouchEvent() 方法返回true,则表示它要拦截这个事件,这个事件就会交给它的 onTouchEvent() 方法处理。如果 onInterceptTouchEvent() 方法返回 false,则表示它不拦截这个事件,则这个事件会交给它的子元素的 dispatchTouchEvent() 来处理

	// ViewGroup 中 dispatchTouchEvent 方法
	@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(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;
            }
            ......
            // 如果该 `ViewGroup` 的 `onInterceptTouchEvent()` 方法返回true,则表示它要拦截这个事件,这个事件就会交给它的 `onTouchEvent()` 方法处理
            if (!canceled && !intercepted) {
            	......
            }
 		}
        return handled;
    }

Step4: 依步骤3传递,如此反复下去。如果传递给底层的View,View是没有子View的不再需要拦截了,就会调用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 (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
			// 我们平常设置的 setOnTouchListener方法优先级高于 onTouchEvent
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
		......
        return result;
    }

	/**
     * Interface definition for a callback to be invoked when a touch event is
     * dispatched to this view. The callback will be invoked before the touch
     * event is given to the view.
     */
    public interface OnTouchListener {
        /**
         * Called when a touch event is dispatched to a view. This allows listeners to
         * get a chance to respond before the target view.
         *
         * @param v The view the touch event has been dispatched to.
         * @param event The MotionEvent object containing full information about
         *        the event.
         * @return True if the listener has consumed the event, false otherwise.
         */
        boolean onTouch(View v, MotionEvent event);
    }

Step5: 一般情况下最终会调用View的 onTouchEvent() 方法

	// 1. 一般View 长按监听在 点击监听之前被触发

	// 2. 长按监听源代码是在点击按压超过500ms时,默认为长按事件

	// 3. 

	/**
     * 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 action = event.getAction();
		......
		switch (action) {
         case MotionEvent.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();
                      }
                  }
               }      
           break;
           case MotionEvent.ACTION_DOWN:
			// 按下,处理长按事件
			......
           break;
           case MotionEvent.ACTION_MOVE:
			// 	移动,检测触摸是否划出了控件区域,移除响应事件
           break;
           case MotionEvent.ACTION_CANCEL:
			//  
			......	
           break;
		}

	}


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


事件分发小结

1. 一个事件序列,从手指触摸屏幕到手指离开屏幕,在这个过程中,以DOWN事件开始,中间含有不定数的MOVE事件,以UP事件结束
2. 正常情况下,一个事件序列,只能被一个View拦截并且消耗
3. 某个View一旦决定拦截,那么这个事件序列都将由它的onTouchEvent处理,并且它的onInterceptTouchEvent不会再调用
4. 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中其他事件都不会再交给它处理。并且重新交由它的父元素处理(父元素onTouchEvent被调用)
5. 事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子View中干预父元素的事件分发过程,但ACTION_DOWN除外
6. ViewGroup默认不拦截任何事件,即onInterceptTouchEvent默认返回falseView没有onInterceptTouchEvent方法,一旦有点击事件,传递给它,那么它的onTouchEvent方法就会被调用
7. ViewonTouchEvent默认会消耗事件(返回true),除非它是不可点击的(clickablelongClickable同时为false),View的longClickable默认都为false,clickable要分情况,比如Button的clickable默认为true,TextView的clickable默认为false


Q2: Android事件分发类型有哪几个状态
A2: onTouchDown,onTouchMove,onTouchCancel,onTouchUp


Q3: requestDisallowInterceptTouchEvent属性的作用?
A3: 禁止或允许父View拦截自己的点击事件

	 // 不允许父View 拦截点击事件  
	 // false则反之
	 getParent().requestDisallowInterceptTouchEvent(true);

2. 在琢磨以上几个问题之后,我们通过下面几段伪代码,来模式触摸事件的传递流程

以下通过Java层代码模拟Android中事件分发流程,有助于理解Android事件分发机制,希望能帮助到大家(创建6个java文件,然后以java工程运行打印日志学习)

如果你能静下心来,读完下面6个.java文件代码,我相信你能收获很多

2.1 假Activity
public class Activity {

    public static void main(String[] arg) {

        // 顶级容器ViewGroup(构造函数传递左上,右下坐标)
        ViewGroup viewGroup = new ViewGroup(0, 0, 1080, 1920);
        viewGroup.setName("顶级容器");

        // 二级容器ViewGroup,也是定义两个坐标
        ViewGroup viewGroup1 = new ViewGroup(0, 0, 500, 500);
        viewGroup1.setName("第二级容器");

        // 模拟初始化View以及放置在ViewGroup层级中
        View view = new View(0, 0, 200, 200);
        view.setName("子View");

        viewGroup1.addView(view);
        viewGroup.addView(viewGroup1);

        viewGroup.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println("顶级的OnTouch事件");
                return false;
            }
        });

        viewGroup1.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println("第二级容器的OnTouch事件");
                return false;
            }
        });

//        view.setOnClickListener(new OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                System.out.println("子iew的onClick事件");
//            }
//        });
//
        view.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println("子view的OnTouch事件");
                return false;
            }
        });

        // 模拟事件分发(点击里面View坐标点为:(100,100))
        MotionEvent motionEvent = new MotionEvent(100, 100);
        motionEvent.setActionMasked(MotionEvent.ACTION_DOWN);

        // 顶级容器分发
        viewGroup.dispatchTouchEvent(motionEvent);
    }
}

2.2 假ViewGroup
public class ViewGroup extends View {

    // 子View个数
    private View[] mChildren = new View[0];

    public ViewGroup(int left, int top, int right, int bottom) {
        super(left, top, right, bottom);
    }

    List<View> childList = new ArrayList<>();


    public void addView(View view) {
        if (view == null) {
            return;
        }
        childList.add(view);
        mChildren = childList.toArray(new View[childList.size()]);
    }

    private TouchTarget mFirstTouchTarget;

    // 事件分发的入口
    public boolean dispatchTouchEvent(MotionEvent event) {
        //
        System.out.println(name + " dispatchTouchEvent ");

        boolean handled = false;
        boolean intercepted = onInterceptTouchEvent(event);

        // TouchTarget  模式 内存缓存   move up
        TouchTarget newTouchTarget = null;
        int actionMasked = event.getActionMasked();

        if (actionMasked != MotionEvent.ACTION_CANCEL && !intercepted) {
            // 不拦截情况下,开始处理Down事件
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                final View[] children = mChildren;
                //  遍历ViewGroup中所有子View
                for (int i = children.length - 1; i >= 0; i--) {
                    View child = mChildren[i];
                    // View能够接收到事件
                    if (!child.isContainer(event.getX(), event.getY())) {
                        continue;
                    }
                    // 能够接受事件  child   分发给他
                    if (dispatchTransformedTouchEvent(event, child)) {
                        // View[]  采取了 Message 的方式进行  链表结构
                        handled = true;
                        newTouchTarget = addTouchTarget(child);
                        break;
                    }
                }
            }
            // 当前的ViewGroup  dispatchTransformedTouchEvent
            if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(event, null);
            }
        }
        return handled;
    }

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


    // 回收池策略·
    private static final class TouchTarget {

        public View child;//当前缓存的View

        // 回收池(一个对象)
        private static TouchTarget sRecycleBin;

        private static final Object sRecycleLock = new Object[0];

        public TouchTarget next;

        // size
        private static int sRecycledCount;

        // up事件
        public static TouchTarget obtain(View child) {
            TouchTarget target;
            synchronized (sRecycleLock) {
                if (sRecycleBin == null) {
                    target = new TouchTarget();
                } else {
                    target = sRecycleBin;
                }
                sRecycleBin = target.next;
                sRecycledCount--;
                target.next = null;
            }
            target.child = child;
            return target;
        }

        public void recycle() {

            if (child == null) {
                throw new IllegalStateException("已经被回收过了");
            }
            synchronized (sRecycleLock) {

                if (sRecycledCount < 32) {
                    next = sRecycleBin;
                    sRecycleBin = this;
                    sRecycledCount += 1;
                }
            }
        }
    }

    //分发处理 子控件  View
    private boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {
        boolean handled = false;
        // 当前View消费了
        if (child != null) {
            handled = child.dispatchTouchEvent(event);
        } else {
            handled = super.dispatchTouchEvent(event);
        }
        return handled;
    }

    /**
     * @param ev
     * @return 是否拦截点击事件
     */
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

}

2.3 假View
public class View {

    public String name;

    @Override
    public String toString() {
        return "" + name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private int left;
    private int top;
    private int right;
    private int bottom;

    private OnTouchListener mOnTouchListener;
    private OnClickListener onClickListener;

    public void setOnTouchListener(OnTouchListener mOnTouchListener) {
        this.mOnTouchListener = mOnTouchListener;
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public View() {
    }

    public View(int left, int top, int right, int bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    /**
     * @param x
     * @param y
     * @return 是否处于View点击区域内
     */
    public boolean isContainer(int x, int y) {
        if (x >= left && x < right && y >= top && y < bottom) {
            return true;
        }
        return false;
    }

    // 接受分发的代码
    public boolean dispatchTouchEvent(MotionEvent event) {
        System.out.println(name + " dispatchTouchEvent ");
        // 消费
        boolean result = false;
        if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        if (!result && onTouchEvent(event)) {
            result = true;
        }

        return result;
    }

    private boolean onTouchEvent(MotionEvent event) {
        System.out.println(name + " onTouchEvent ");

        if (onClickListener != null) {
            onClickListener.onClick(this);
            return true;
        }
        return false;
    }

}
2.4 假MotionEvent
public class MotionEvent {

    public static final int ACTION_DOWN = 0;
    public static final int ACTION_UP = 1;
    public static final int ACTION_MOVE = 2;
    public static final int ACTION_CANCEL = 3;

    private int actionMasked;
    private int x;
    private int y;

    public MotionEvent() {
    }

    public MotionEvent(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getActionMasked() {
        return actionMasked;
    }

    public void setActionMasked(int actionMasked) {
        this.actionMasked = actionMasked;
    }
}
2.5 假OnClickListener
public interface OnClickListener {

    void onClick(View v);

}
2.6 假OnTouchListener
public interface OnTouchListener {

    boolean onTouch(View v, MotionEvent event);

}


3. 通过以上伪代码,我们熟悉了事件传递流程,在实际开发中,我们通常通过以下2种策略来解决事件冲突

3.1 外部拦截法


	// 重写ViewGroup的以下方法,
	@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    	boolean intercepted = false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downPoint.x = ev.getX();
                downPoint.y = ev.getY();
                intercepted = false; // 必须为false,否则后续的MOVE,UP不在传递给子View
                break;
            case MotionEvent.ACTION_MOVE:
                CLog.i(TAG, "onTouchEvent ACTION_MOVE ev.getX(): " + ev.getX());
                if (父容器需要当前点击事件) {
                    intercepted = true;
                } else {
                   intercepted = false;
                }
               break;
            case MotionEvent.ACTION_UP:
            	intercepted = false; // 必须为false,否则会影响子View的onClick是否被触发
               break;
        }
        return intercepted;
    }
    // 大家可以参照ScrollView的源码来进一步熟悉外部拦截法的实际应用(以下这段为ScrollView源码片段)
	@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onMotionEvent will be called and we do the actual
         * scrolling there.
         */

        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }

        if (super.onInterceptTouchEvent(ev)) {
            return true;
        }

        /*
         * Don't try to intercept touch if we can't scroll anyway.
         */
        if (getScrollY() == 0 && !canScrollVertically(1)) {
            return false;
        }

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */
				......
                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
                    mIsBeingDragged = true;
                    mLastMotionY = y;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    mNestedYOffset = 0;
                    if (mScrollStrictSpan == null) {
                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                    }
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_DOWN: {
                final int y = (int) ev.getY();
                if (!inChild((int) ev.getX(), (int) y)) {
                    mIsBeingDragged = false;
                    recycleVelocityTracker();
                    break;
                }
				......
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                /* Release the drag */
                mIsBeingDragged = false;
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
                    postInvalidateOnAnimation();
                }
                stopNestedScroll();
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }
        /*
        * The only time we want to intercept motion events is if we are in the
        * drag mode.
        */
        return mIsBeingDragged;
    }

3.2 内部拦截法


	@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
     switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downPoint.x = ev.getX();
                downPoint.y = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                CLog.i(TAG, "onTouchEvent ACTION_MOVE ev.getX(): " + ev.getX());
                if (TextUtils.equals("0", mPos) && downPoint.x < ev.getX()) {
                    return true;
                }
        }
        return super.dispatchTouchEvent(ev);
    }
 
参考
  1. 解惑requestDisallowInterceptTouchEvent
  2. Android TouchEvent之requestDisallowInterceptTouchEvent
  3. 一文解决Android View滑动冲突
  4. Android 经典笔记之四: 事件冲突解决思路与方案
  5. ViewConfiguration 实例获取
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

手写Android事件分发 的相关文章

  • 使用 PutDataMapRequest 后,Wearable.DataApi.getDataItem() 的 Uri 是什么?

    我正在测试可穿戴数据层 API如中所述安卓教程 http developer android com training wearables data layer index html 有一个基于底层 APIDataItem http dev
  • 使用 ActiveAndroid 库存储 HashMap

    我有一堂课 Table name Control public class Control extends Model Column private String name Column private Map
  • GCM 无法唤醒设备

    我正在开发 GCM 应用程序 当设备不空闲时 即按电源按钮休眠 一切正常 但是 当我在设备空闲时发送消息时 设备不会唤醒 我做了以下事情 已验证服务器应用程序中的delay while idle未设置为true 每条消息使用不同的折叠键 多
  • 视图无法解析为类型

    这里的视图似乎有什么问题 我该如何解决它 错误 视图无法解析为类型 public void onItemClick AdapterView
  • 通过意图使用多个电话号码添加新联系人

    我想使用添加新联系人ContactsContract Intents Insert 但问题是我不知道可以有多少个电话号码 据我了解 我只能使用三个电话号码PHONE SECONDARY PHONE TERTIARY PHONE常数 有什么办
  • Auto-value-gson出现接口错误,注册一个InstanceCreator?

    我有一个如下所示的接口类 public interface Species String name And a Human实现的类 AutoValue使用类型适配器 AutoValue public abstract class Human
  • 多种语言的多种字体

    我最近在开发应用程序时遇到了一种情况 我必须在文本视图中显示不同的语言 目前我正在展示一些使用字体 字体像这样 Typeface tf Typeface createFromAsset this getAssets DroidHindi t
  • 带有图层列表的自定义背景以显示对角线?

    我只想创建一个自定义背景 但我不知道如何使用 xml 而不是图像来做到这一点 这是 XML
  • Android中如何使用JNI获取设备ID?

    我想从 c 获取 IMEIJNI 我使用下面的代码 但是遇到了未能获取的错误cls 它总是返回NULL 我检查了环境和上下文 它们都没有问题 为什么我不能得到Context班级 我在网上搜索了一下 有人说我们应该使用java lang Ob
  • Android studio - 如何保存先前活动中选择的数据

    这是我的代码片段 这Textview充当按钮并具有Onclicklistner在他们 当cpu1000时Textview单击它会导致cpu g1000其代码如下所示的类 public class Game 1000 extends AppC
  • 警报对话框中的 Webview 不显示内容

    我正在开发一个 Android 应用程序 我需要在网络视图和警报对话框上显示一个网站 该站点显示在网络视图中 但不显示在警报对话框中 到目前为止 这是我的代码 WebView WebView myWebView WebView v find
  • 蓝牙发送和接收文本数据

    我是 Android 开发新手 我想制作一个使用蓝牙发送和接收文本的应用程序 我得到了有关发送文本的所有内容逻辑工作 但是当我尝试在手机中测试它时 我看不到界面 这是Main Activity Code import android sup
  • Android 4.4 Kitkat 自定义视图操作栏未填充整个宽度

    我试图拥有一个带有自定义视图的简单操作栏 但我得到以下结果 为了演示 我创建了一个带有黄色背景颜色的简单 xml 它应该占据整个宽度 这是 XML
  • 找不到数据库路径是不可能的

    我对 android 开发很陌生 现在我正在尝试通过扩展 SQLiteOpenHelper 的类创建数据库 我确信数据存储在我的 Nexus 7 我用来测试应用程序的设备 上的某个位置 但是我找不到数据库的路径 我四处寻找其他类似的问题 所
  • 在android中从SD卡上传图像到facebook

    我无法从 SD 卡上传 Facebook 上的图像 我使用了下面的代码 但它没有给我错误 但同时它没有上传图像 byte data null try FileInputStream fis new FileInputStream filep
  • React Native HTTPS Api 调用在 IOS 中有效,但在 Android 中无效

    所以基本上我所做的就是简单地对启用了 HTTPS 的 UAT 服务器进行简单的 Axios 调用 我已经在 IOS 中测试了整个应用程序 API 调用工作正常 但一旦我在 Android 中测试了相同的应用程序 在真正的 Android 设
  • 如何使用 isInEditMode() 在编辑器中查看自定义视图的布局

    我必须编辑一个具有自定义视图的软件 当我尝试编辑布局 xml 时 Eclipse 告诉我 在自定义视图中使用 View isInEditMode 可以在显示时跳过代码 在日食中 但我不知道how and where我必须使用isInEdit
  • Android View Canvas onDraw 未执行

    我目前正在开发一个自定义视图 它在画布上绘制一些图块 这些图块是从多个文件加载的 并将在需要时加载 它们将由 AsyncTask 加载 如果它们已经加载 它们只会被绘制在画布上 这工作正常 如果加载了这些图片 AsyncTask 就会触发v
  • View.post(),以及当Runnables被执行时

    我最初的问题是需要知道我的根的高度和宽度View这样我就可以进行程序化的布局更改 就我的目的而言 我不一定需要在onCreate 对于我来说 以编程方式添加我的孩子就足够了View根布局完成后 因此我很乐意使用onWindowFocusCh
  • 从 JavaScript 中的 OnClientClick 事件中阻止 C# 中的 asp:Button OnClick 事件?

    我有一个asp Button在我的网页上 它调用 JavaScript 函数和代码隐藏方法 后者进行调用以导航到另一个页面 在 JavaScript 函数中 我正在检查条件 如果不满足这个条件 我想中止导航 以便OnClick方法未被调用

随机推荐

  • SQL练习

    学生选课表的50个SQL语句 1 查询001课程比002课程成绩高的所有学生的学号 select a s id from select s id score from sc where c id 001 a select s id scor
  • python自动化办公(三十二)pyinstaller.exe打包成exe程序,运行后ModuleNotFoundError或FileNoFounderError:no such file or **

    目录 一 打包Tkinter 二 下载pyinstaller 三 pycharm终端运行pyinstaller 四 pyinstaller exe直接运行Cmd命令打包
  • Docker中web项目的部署以及访问

    1 将应用打包成demo war 2 编写Dockerfile 构建镜像 Dockerfile FROM 包含tomcat的基础镜像 COPY demo war usr local tomcat webapps COPY apple app
  • 八皇后[n皇后]问题 python 算法的理解

    八皇后 n皇后 问题表述为 在8 8格的国际象棋上摆放8个皇后 使其不能互相攻击 即任意两个皇后都不能处于同一行 同一列或同一斜线上 问有多少种摆法 答案是92种 可以看看遍历过程 方便理解 对于递归queen A cur 1 的理解 cu
  • vue根据路由隐藏侧边栏

    项目要求某模块显示侧边栏 某模块隐藏侧边栏 所有模块统一引用了一个layout组件 所以在路由里设置一个自定义属性 在layout组件里监听路由 判断自定义属性值来隐藏侧边栏 An highlighted block path equipm
  • macos配置vscode支持c++11/17标准

    目录 简介 需求 步骤 步骤1 步骤2 步骤3 步骤4 步骤5 结语 简介 Hello 非常感谢您阅读海轰的文章 倘若文中有错误的地方 欢迎您指出 昵称 海轰 标签 程序猿 C 选手 学生 简介 因C语言结识编程 随后转入计算机专业 获得过
  • Looper和Handler

    Looper用于在android线程中进行消息处理 默认情况下 一个线程并不和任何Looper绑定 当我们调用Looper prepare 时 如果当前线程还没有和任何Looper绑定 那么将创建一个Looper让它和当前线程绑定 当我们调
  • Mysql数据库简单配置

    1 将安装包下载到本地文件路径 按照自己的情况 2 配置ini文件 放在mysql安装目录 没有文件名 解决方法 3 终端切换目录到安装目录下的bin目录下 建议配置环境变量 后面直接通过命令开启服务 直接双击path也可以进入 然后点击新
  • module “**.vue“ has not default

    module vue has not default 这个问题造成的原因是因为你在vue config js中设置了happyPackMode选项 如下所示 config module rule ts use ts loader loade
  • 初识注解

    注解的英文单词 Annotation 3 有一个public修饰的 入口 4 且该public修饰的类名必须与文件名相同 5 并且一个源文件可以只有非public类 package com kuang Annotation 测试元注解 im
  • 用一个函数实现用选择法对5个整数按升序排序

    用一个函数实现用选择法对5个整数按升序排序 选择法思想 先选出5个数中最小的数 把它和score 0 交换 这样a 0 就是5个数中最小的数了 再在剩下4个数 score 1 到score 4 中选出最小的数 把它和score 1 交换 这
  • kafka基本知识

    kafka 消息队列是什么 解决什么样的问题 有什么常见的应用场景 MQ message queue 消息队列是本质上是队列 先进先出的数据结构 生产者将消息放到队列上 消费者通过 消息的消费者通过拉取或者订阅推送的机制来获取消息 解决的问
  • 梯度消失和梯度爆炸及解决方法

    原文链接 感谢原作者 一 为什么会产生梯度消失和梯度爆炸 目前优化神经网络的方法都是基于BP 即根据损失函数计算的误差通过梯度反向传播的方式 指导深度网络权值的更新优化 其中将误差从末层往前传递的过程需要链式法则 Chain Rule 的帮
  • Python读写EXCEL文件常用方法

    python读写excel的方式有很多 不同的模块在读写的讲法上稍有区别 这里我主要介绍几个常用的方式 用xlrd和xlwt进行excel读写 用openpyxl进行excel读写 用pandas进行excel读写 一 数据准备 为了方便演
  • (五)pandas-修改数据

    pandas修改数据可以通过以下几种方式 1 通过切片定位到数据位置 然后直接赋值 2 mask where 两个函数 3 replace函数 4 apply函数 以下图df为例 1 切片方式 切片方式用于通过下标 标签直接定位到指定位置
  • 高性能Key/Value存储引擎levelDB, rocksDB, TiDB,InnoDB

    高性能存储引擎levelDB rocksDB TiDB InnoDB 1 简单介绍 1 1 LevelDB LevelDB是Google开源的持久化KV单机数据库 具有很高的随机写 顺序读 写性能 但是随机读的性能很一般 也就是说 Leve
  • 在windows系统中使用Ceres非线性优化库:(一)安装Ceres库

    一 安装Ceres库 1 用vcpkg安装Ceres库 1 1 安装vcpkg 1 2 安装Ceres 1 3 配置Ceres 2 用Virtual Studio安装Ceres库 2 1 下载ceres windows 2 2 打开或升级解
  • mysql查询每个学生最高分_mysql查询各班最高分学生的信息

    学生表student 班级表class 课程表subject 成绩表score 一 查询各班最高分学生的信息 1 从成绩表score中查询每个学生的总成绩并按降序排列 select sc stu id sum sc score sumsco
  • 小程序图片懒加载放在服务器,【小程序】使用uni-app搭建小程序环境---图片懒加载...

    延迟加载的理念 页面初始化时 暂不加载处于屏幕可见区域之外的图片 该方案会有如下几大好处 n加快页面渲染速度 n提升页面滚动性能 n默认不下载屏幕外的图片 减少网络流量 主标题 列表二级标题 exportdefault data varim
  • 手写Android事件分发

    Android事件分发原理搞清楚可以辅助我们解决很多实际项目中遇到的事件冲突等问题 1 进入正题之前 问大家几个事件相关的问题 标签 dispatchTouchEvent Q1 Android点击事件传递规则是怎样的 下面几步仔细阅读2遍