交互之0

 

"":

交互(触控):2维坐标系 + 时间 (A/NotificationBar)

dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下来就按照Activity -> ViewGroup -> View 。
手指触摸到屏幕时会触发一个 Action_Down 类型的事件,当前页面的 Activity 会首先走Activity 的 dispatchTouchEvent() 方法,内部:调用 getWindow.superDispatchTouchEvent()。

  • 如果上一步返回 true,直接返回 true;否则就 return 自己的 onTouchEvent()。
    这个逻辑很好理解,getWindow().superDispatchTouchEvent() 如果返回 true 代表当前事件已经被处理,无需调用自己的 onTouchEvent;否则代表事件并没有被处理,需要 Activity 自己处理,也就是调用自己的 onTouchEvent。

  • Activity#boolean dispatchTouchEvent(){

            if(getWindow().superDispatchTouchEvent()){//实际调用了mDecor.superDispatchTouchEvent(event){if(onInterceptTouchEvent()//内部views#dispatchTouchEvent()中直接 return 了自己的 onTouchEvent()){return true;//默认的 ViewGroup 都是不拦截的;}else return  onTouchEvent();}

  •             return true;

            }else{

                return  onTouchEvent();

        }

    }

getWindow()方法返回了一个 Window 类型的对象,这个我们都知道,在 Android 中,PhoneWindow 是Window 的唯一实现类。所以这句本质上是调用了`PhoneWindow中的superDispatchTouchEvent()。
而在 PhoneWindow 的这个方法中实际调用了mDecor.superDispatchTouchEvent(event)。这个 mDecor 就是 DecorView,它是 FrameLayout 的一个子类,在 DecorView 中的 superDispatchTouchEvent() 中调用的是 super.dispatchTouchEvent()。到这里就很明显了,DecorView 是一个 FrameLayout 的子类,FrameLayout 是一个 ViewGroup 的子类,本质上调用的还是 ViewGroup的dispatchTouchEvent()。
事件已经从 Activity 传递到了 ViewGroup,接下来我们来分析下 ViewGroup 中的这几个事件处理方法。
在 ViewGroup 中的 dispatchTouchEvent()中的逻辑大致如下:

  • 通过 onInterceptTouchEvent() 判断当前 ViewGroup 是否拦截事件,默认的 ViewGroup 都是不拦截的;

  • 如果拦截,则 return 自己的 onTouchEvent();

  • 如果不拦截,则根据 child.dispatchTouchEvent()的返回值判断。如果返回 true,则 return true;否则 return 自己的 onTouchEvent(),在这里实现了未处理事件的向上传递。

通常情况下 ViewGroup 的 onInterceptTouchEvent()都返回 false,也就是不拦截。这里需要注意的是事件序列,比如 Down 事件、Move 事件……Up事件,从 Down 到 Up 是一个完整的事件序列,对应着手指从按下到抬起这一系列的事件,如果 ViewGroup 拦截了 Down 事件,那么后续事件都会交给这个 ViewGroup的onTouchEvent。如果 ViewGroup 拦截的不是 Down 事件,那么会给之前处理这个 Down 事件的 View 发送一个 Action_Cancel 类型的事件,通知子 View 这个后续的事件序列已经被 ViewGroup 接管了,子 View 恢复之前的状态即可。
这里举一个常见的例子:在一个 Recyclerview 钟有很多的 Button,我们首先按下了一个 button,然后滑动一段距离再松开,这时候 Recyclerview 会跟着滑动,并不会触发这个 button 的点击事件。这个例子中,当我们按下 button 时,这个 button 接收到了 Action_Down 事件,正常情况下后续的事件序列应该由这个 button处理。但我们滑动了一段距离,这时 Recyclerview 察觉到这是一个滑动操作,拦截了这个事件序列,走了自身的 onTouchEvent()方法,反映在屏幕上就是列表的滑动。而这时 button 仍然处于按下的状态,所以在拦截的时候需要发送一个 Action_Cancel 来通知 button 恢复之前状态。
事件分发最终会走到 View 的 dispatchTouchEvent()中。在 View 的 dispatchTouchEvent() 中没有 onInterceptTouchEvent(),这也很容易理解,View 不是 ViewGroup,不会包含其他子 View,所以也不存在拦截不拦截这一说。忽略一些细节,View 的 dispatchTouchEvent()中直接 return 了自己的 onTouchEvent()。如果 onTouchEvent()返回 true 代表事件被处理,否则未处理的事件会向上传递,直到有 View 处理了事件或者一直没有处理,最终到达了 Activity 的 onTouchEvent() 终止。
这里经常有人问 onTouch 和 onTouchEvent 的区别。首先,这两个方法都在 View 的 dispatchTouchEvent()中,是这么一个逻辑:

  • 如果 touchListener 不为 null,并且这个 View 是 enable 的,而且 onTouch 返回的是 true,满足这三个条件时会直接 return true,不会走 onTouchEvent()方法。

  • 上面只要有一个条件不满足,就会走到 onTouchEvent()方法中。所以 onTouch 的顺序是在 onTouchEvent 之前的。

 

触控三阶段时序:Down(时长):   Move(时长):Up(点击完成标志):
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false;    if(onInterceptTouchEvent(ev)) {         consume = onTouchEvent(ev);    } else {         consume = child.dispatchTouchEvent(ev);     }  return consume; }//源自 android开发艺术探索//一时刻一个容器消费事件  弊如同向嵌套滑动(NestedScrolling(子View驱动): NestedScrollingChild NestedScrollingParent NestedScrollingChildHelper NestedScrollingParentHelper)  

CoordinatorLayout:加强版FrameLayout,便于直接子View间实现“观察者”模式public class FBehavior extends CoordinatorLayout.Behavior<TextView> {//交由Behavior管理:观察者设置app:layout_behavior=".FBehavior";layout_anchor设为被观察者的id,layout_anchorGravity具体反应

  public FBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
  @Override
  public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) { 
    return dependency instanceof  Button;//确定被观察者
  }
  @Override
  public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child,
        View dependency) {//观察dependency并反应
    child.setX(dependency.getX() + 100);
 
    return true;
  }
}

 

容器:(绝/相对)位置:位移       容积
内容:形态:旋转,(A)RGB,缩放,平移, 错切(skew),透视(3D)

 

可交互状态(事件):绝对相触面积+动画播放状态

 

焦点:V:requestFocus和clearFocus, onFocusChange,onFocusChangeListener;

ViewGroup, 如果只需要在子View获取焦点时得到通知, requestChildFocus,onRequestFocusInDescendants可控制某些情景下ViewGroup焦点
,控制焦点移动focusSearch;FocusFinder工具         beforeDescendants:viewgroup会优先其子类控件而获取到焦点  afterDescendants:viewgroup只有当其子类控件不需要焦点时才获取焦点  blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

 

eg:识别点击v里任何区域:drawPath标记区域,创建一全局Region(区域)再Region.setPath为可识别区域:class RegionClickV extends View {
    Paint mPaint;
    Region globalRegion;
    Region circleRegion1;
    Region circleRegion2;
    Path circlePath1;
    Path circlePath2;
    public RegionClickView(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.GRAY);

        circlePath1 = new Path();
        circlePath2 = new Path();

        circleRegion1 = new Region();
        circleRegion2 = new Region();

        circlePath1.addRect(100, 100, 300,300, Path.Direction.CW);//值域

        circlePath2.addRect(-100,-100,100,100, Path.Direction.CW);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        globalRegion = new Region(0, 0, w, h);
        circleRegion1.setPath(circlePath1, globalRegion);
        circleRegion2.setPath(circlePath2, globalRegion);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {//return m(Scale)GestureDetector.onTouchEvent(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                int x = (int) event.getX();
                int y = (int) event.getY();

                if (circleRegion1.contains(x,y)){
                   //somewhere do sth.
                }
                if (circleRegion2.contains(x,y)){
                    //
                }                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(circlePath1,mPaint);
        canvas.drawPath(circlePath2,mPaint);
    }
}

 

child.getHitRect(new Rect()).bottom += 100; //child.get,,,绘制完再执行   new Rect().setEmpty(//reset)
parent.setTouchDelegate(new TouchDelegate(rc, child)); //将自身某局域接收到的点击分发给child,若范围超出Parent,则超出部分无效,一个Parent默认只能设置一个View的TouchDelegate

 

EditText焦点:
。android:windowSoftInputMode="stateHidden";
。EditText.clearFocus();
。强制隐藏Android输入法窗口:
EditText edt = (EditText)findViewById(R.id.edt);  
InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);  
imm.hideSoftInputFromWindow(edt.getWindowToken(), 0);
。EditText始终不弹出虚拟键盘:
EditText.setInputType(InputType.TYPE_NULL);
。让EditText自动获得焦点并弹出软键盘,在设置了EditText自动获得焦点后,软件盘不会弹出。
注:此时是由于刚跳到一个新的界面,界面未加载完全而无法弹出软键盘。此时应该适当的延迟弹出软键盘,如500毫秒(保证界面的数据加载完成,如果500毫秒仍未弹出,则延长至1000毫秒)。
Timer timer = new Timer();  
timer.schedule(new TimerTask() {  
    public void run() {  
        InputMethodManager inputManager = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);  
        inputManager.showSoftInput(editText, 0);  
    }  
}, 500);再给activity配置加入属性:
android:windowSoftInputMode="adjustResize"

 

updating

位置,跟手dragChildInParentHelper=ViewDragHelper.create(this::vp, sensitivity, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) {//想好捕获谁了吗 return true; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { } @Override public int clampViewPositionVertical(View child, int top, int dy) {  }  @Override public void onViewReleased(View releasedChild, float xvel, float yvel) {eg. dragChildInParentHelper.settleCapturedViewAt(x, y);invalidate();}@Override public void onEdgeDragStarted(int edgeFlags, int pointerId){//dragChildInParentHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);

dragChildInParentHelper.captureChildView(mEdgeTrackerView, pointerId); }}); 

 

接管@Override public boolean onInterceptTouchEvent(MotionEvent event) { return dragChildInParentHelper.shouldInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) {dragChildInParentHelper.processTouchEvent(event); return true; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值