自定义View的一些注意点

View的坐标系

View坐标系

自定义View重写OnTouchEvent(MotionEvent event)方法后Android Studio提示重写performClick(方法)

我们在重写OnTouchEvent(MotionEvent event)方法时,Android Studio总是提示我们“Custom View XXXX overrides onTouchEvent but no performClick more…”, 分析performClick()源码后我们知道:

 public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

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

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }

performClick() 方法中实际上会执行CustomView.setOnClickListener(){}中的事件(前提是我们设置了setOnClickListener()),而我们重写OnTouchEvent后,setOnClickListener可能会得不到执行,当然,我们发现自己即使不关心Android Studio给出的这个警告,仍然运行正常,我们仔细想想也符合逻辑:既然自定义View重写了OnTouchEvent()方法,那么就不应该再设置setOnClickListener监听事件,Android Studio给出提示只是为了当开发者重写onTouchEvent方法后,又在外部给这个自定义View设置了监听事件,如何能执行到监听事件(setOnClickListener是事件分发的最后一层)。
其实消不消除这个waring都没什么问题,我们在这里说下如何消除这个waring, 首先在控件中重写performClick方法,然后在onTouchEvent中的某个Action行为中执行performClick(),如下所示,

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                performClick();
                break;
        }

        return false;
    }

    @Override
    public boolean performClick() {
        return super.performClick();
    }

不过,我们还发现,我们即使重写了onTouchEvent方法,依旧可以执行到setOnClickListener级别(我在上面也说了,它并不是执行不到,只是在某些情况下执行不到), 我们在onTouchEvent方法末尾一般会返回父类该方法执行的结果。

@Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

我们分析View的onTouchEvent源代码得知,

public boolean onTouchEvent(MotionEvent event) {
switch (action) {
                case MotionEvent.ACTION_UP:
                     if (mPerformClick == null) {
                              mPerformClick = new PerformClick();
                        }
                    if (!post(mPerformClick)) {
                             performClickInternal();
                         }
                 }
}

private final class PerformClick implements Runnable {
        @Override
        public void run() {
            recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
            performClickInternal();
        }
    }


 private boolean performClickInternal() {
        // Must notify autofill manager before performing the click actions to avoid scenarios where
        // the app has a click listener that changes the state of views the autofill service might
        // be interested on.
        notifyAutofillManagerOnClick();

        return performClick();
    }

在父类的Up事件中,同样也会执行performClick(),进而我们就看到了,我们设置的自定义View的setOnClickListener也执行到了。
个人总结:自定义的View如果在onTouchEvent(MotionEvent event)中没有使用过super.onTouchEvent(event)代码的话(在一些文章中也见过直接根据情况返回true/false的),这时候如果也在外部(比如Activity)给这个自定义View设置了setOnClickListener,那么我们就需要在onTouchEvent中根据具体情况来决定把performClick()代码放在哪个位置合适(一般也是放在UP事件中),然后在控件中重写performClick()。不建议在onTouchEvent(MotionEvent event)方法中performClick()与super.onTouchEvent(event)都出现。

Paint关于PorterDuffXfermode的几个问题

PorterDuffXfermode类似数学中关于图形之间的交集,并集,差集问题,需要注意的是:dst是下层,先画上去的图形;src是上层,后画上的图形;用句谚语来形容的话就是“后来者居上”。
首先看下google给出的官方样例,
在这里插入图片描述
但我们真正使用的时候回发现实际情况与此图并不完全符合,具体情况可以查看此博客:https://blog.csdn.net/wingichoy/article/details/50534175,
这里我们主要说下结论:
首先PorterDuffXfermode会受硬件加速的影响,建议自定义View如果使用PorterDuffXfermode关闭硬件加速;
其次,google给出的官方图成立的前提是存在两个bitmap

PorterDuffXfermode的各种模式详解https://www.jianshu.com/p/0f64daf202f2
###ViewGroup 中 onInterceptTouchEvent()的几条结论。

  • down事件首先传递到onInterceptTouchEvent()方法;
  • 如果该ViewGroup的onInterceptTouchEvent()方法在接收到down事件并处理完成后return false,则内部View也将会获取到down事件,后续的move,up事件也将继续先传递到该ViewGroup中,之后一样传递给最终的目标childView的onTouchEvent()处理。
  • 如果该ViewGroup的onInterceptTouchEvent()接收down事件并处理完成后return true,则表明子View不需要接收接收事件,后续的move,up事件也不再传递给onInterceptTouchEvent(), 该事件接着交给ViewGroup自身的onTouchEvent()方法去执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中自定义View可以通过以下步骤实现: 1. 创建一个继承自View或其子类的类,例如继承自View的MyView类。 2. 在该类的构造函数中初始化一些属性,例如: ``` public MyView(Context context) { super(context); //初始化一些属性 mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL); } ``` 3. 重写该类的onMeasure方法,以便在该View被加入到View hierarchy时能正确地测量它的大小,例如: ``` @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //测量宽度 int width = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { mWidth = width; } else { mWidth = DEFAULT_WIDTH; } //测量高度 int height = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { mHeight = height; } else { mHeight = DEFAULT_HEIGHT; } setMeasuredDimension(mWidth, mHeight); } ``` 4. 重写该类的onDraw方法,以便在该View被绘制时能够绘制一些内容,例如: ``` @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制一个矩形 canvas.drawRect(0, 0, mWidth, mHeight, mPaint); } ``` 5. 在布局文件中使用该自定义View,例如: ``` <com.example.myview.MyView android:id="@+id/myView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> ``` 注意:以上仅为自定义View的基本步骤,具体实现方式可能根据需求有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值