深入理解Android自定義View

一、Android控件架構(gòu)

Android的每個控件都是占一塊矩形的區(qū)域晕城,大致的分兩類泞坦,繼承View和ViewGroup,ViewGroup相當于一個容器砖顷,他可以管理多個子View贰锁,整個界面上的控件形成了一個樹形結(jié)構(gòu),也就是我們常說的控件樹滤蝠,上層控件負責下層控件的測量和繪制豌熄,并且傳遞交互事件,通過findviewbyid()這個方法來獲取物咳,其實就是遍歷查找锣险,在樹形圖的頂部都有一個ViewParent對象,這就是控制核心,所有的交互管理事件都是由它統(tǒng)一調(diào)度和分配芯肤,從而進行整個視圖的控制

image

通常情況下夯接,我們要顯示一個activity的視圖,需要使用setContentView()方法纷妆,那么這個方法到底做了些什么尼盔几?我們先來看看Android 體系的架構(gòu)圖

image

我們可以看到,每個activity都有一個window對象掩幢,在Android中逊拍,window對象通常由一個Phonewindow去實現(xiàn)的,phonewindow將一個DecorView設(shè)置為整個窗口的根View际邻,DecorView作為窗口界面的頂層視圖芯丧,封裝了一些通用的方法,可以說世曾,DecorView將要顯示的內(nèi)容都給了phonewindow缨恒,這里面所有的View監(jiān)聽,都是通過WindowManagerService來接收的轮听,通過相應的回調(diào)來OnClicListener骗露,在顯示上,他將屏幕分成了兩部分血巍,一個title一個content萧锉,看到這里,大家應該能看到一個熟悉的界面ContentView述寡,它是一個ID為content分framelayout柿隙,activity_main.xml就是設(shè)置在這個framelayout里面,我們可以看下圖:

image

而在代碼中當程序onCreate()時鲫凶,也就設(shè)置了layout,執(zhí)行完后禀崖,activitymanagerservice會直接調(diào)用onResume,這個時候系統(tǒng)會把整個DecorView添加到Phonewindow,然后顯示出來完成最后的繪制,所以是在onResume才完全初始化好View螟炫。


二波附、View的測量

我們想要繪制一個View,首先還是得知道這個View的大小不恭,系統(tǒng)是如何把他繪制出來的叶雹,在Android中,我們要想繪制一個View换吧,就必須要知道這個View的大小折晦,然后告訴系統(tǒng),這個過程在onMeasure()中進行

 /**
     * 測量
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

Android給我們提供了一個設(shè)計短小精悍的類——MeasureSpec類沾瓦,通過他來幫助我們測量View, MeasureSpec是一個32位的int值满着,其中高2位為測量模式谦炒,低30為測量的大小,在計算中使用位運算時為了提高并且優(yōu)化效率风喇。

測量模式:

  • EXACTLY:表示父視圖希望子視圖的大小應該是由specSize的值來決定的宁改,系統(tǒng)默認會按照這個規(guī)則來設(shè)置子視圖的大小,開發(fā)人員當然也可以按照自己的意愿設(shè)置成任意的大小魂莫。
  • AT_MOST:表示子視圖最多只能是specSize中指定的大小还蹲,開發(fā)人員應該盡可能小得去設(shè)置這個視圖,并且保證不會超過specSize耙考。系統(tǒng)默認會按照這個規(guī)則來設(shè)置子視圖的大小谜喊,開發(fā)人員當然也可以按照自己的意愿設(shè)置成任意的大小。
  • UNSPECIFIED:表示開發(fā)人員可以將視圖按照自己的意愿設(shè)置成任意的大小倦始,沒有任何限制斗遏。這種情況比較少見,自定義View的時候才會用到鞋邑。

View默認的onMeasure只支持EXACTLY模式诵次,所以如果在自定義控件的時候不重寫這個方法的話,也就只能使用EXACTLY模式了枚碗,控件可以響應你制定的具體的寬高值或者match_parent屬性逾一,如果我們自定義View要讓他支持 wrap_content,那就必須重寫onMeasure指定wrap_content時的大小视译。

通過MeasureSpec這個類嬉荆,我們就獲取到了View的測量模式和繪制的大小,有了這些信息我們就可以控制View顯示的大小了酷含,接下來,我們可以看一個簡單的小例子汪茧,我們重寫onMeasure這個方法。

 /**
     * 測量
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

查看super.onMeasure()這個方法舱污,可以發(fā)現(xiàn)呀舔,系統(tǒng)最終還是會調(diào)用setMeasuredDimension()這個方法將測量的寬高設(shè)置進去從而完成測量工作

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
{  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

通過源碼我們知道媚赖,我們自定義的高寬去設(shè)置捻撑,下面我們通過這個例子,來講一下自定義的測量值

第一步,我們從MeasureSpec類中提取出具體的測量模式和大小

 int specMode = MeasureSpec.getMode(measureSpec);
 int specSize = MeasureSpec.getSize(measureSpec);

然后我們根據(jù)模式給出不同的測量值鳖枕,當specMode為EXACTLY時耕魄,直接使用指定的specSize,當為其他兩種模式地時候,我們就需要一個默認的值了考润,特別是指定包裹內(nèi)容的時候罚舱,即AT_MOST模式包个,measureWidth()方法是這樣的:

private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;

        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

寬高的設(shè)置方法是一樣的,所以州既,當我們在布局中設(shè)置match_parent他就鋪滿容器了序臂,要是設(shè)置wrap_content构订,就是包裹內(nèi)容,如果不設(shè)置的話,那他就只有200的大小了


三、View的繪制

當我們用wrap_content方法測量完成之后卑吭,我們就該重寫onDraw()方法來繪制了,這個應該大家都很熟悉吧豆赏,首先我們要知道2D繪制的一些相關(guān)API

Canvas:顧名思義,畫布的意思富稻,而onDraw()就一個參數(shù)掷邦,就是Canvas了,我們要在其他地方繪制的話椭赋,就需要new對象了

Canvas canvas = new Canvas(Bitmap);

這個Bitmap是和Canvas緊密聯(lián)系的抚岗,這個過程我們稱之為裝載畫布,這個bitmap用來儲存畫布的一些像素信息哪怔,而且我們可以用canvas.drawxxx()來繪制相關(guān)的基礎(chǔ)圖形宣蔚,詳情可以看下

//繪制直線
canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint);

//繪制矩形
canvas.drawRect(float left, float top, float right, float bottom, Paint paint);

//繪制圓形
canvas.drawCircle(float cx, float cy, float radius, Paint paint);

//繪制字符
canvas.drawText(String text, float x, float y, Paint paint);

//繪制圖形
canvas.drawBirmap(Bitmap bitmap, float left, float top, Paint paint);

四向抢、自定義View

自定義View一直是個難點,Android自帶的控件很難滿足我們的需求胚委,所欲我們需要重寫控件或者自定義一個View挟鸠,但是一般強大的View,都還是存在少許的bug的亩冬,而且現(xiàn)在Android ROM的多樣性艘希,適配問題也越來越麻煩了。

在View中通常有以下比較重要的回調(diào)方法:

  • onFinishInflate()
//從XML加載組件后回調(diào)
    @Override
    protected void onFinishInflate() {
        // TODO Auto-generated method stub
        super.onFinishInflate();
    }
  • onSizeChanged()
//組件大小改變時回調(diào)
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
    }
  • onMeasure()
// 回調(diào)該方法進行測量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  • onLayout()
// 回調(diào)該方法來確定顯示的位置
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        // TODO Auto-generated method stub
        super.onLayout(changed, left, top, right, bottom);
    }
  • onTouchEvent()
// 監(jiān)聽到觸摸時間時回調(diào)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        return super.onTouchEvent(event);
    }
  • onDraw()
// 繪圖
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
    }

上面的方法并不需要全部寫出來硅急,看個人需要覆享,一般我們實現(xiàn)自定義控件有三種方法:

  • 對現(xiàn)有的控件進行擴展
  • 通過組件來實現(xiàn)新的控件
  • 重寫View來實現(xiàn)全新的控件

1.對現(xiàn)有組件進行拓展

image

我們來分析一下這個效果,其實就是兩層的繪制营袜,我們依然要使用onDraw()撒顿,程序super.onDraw(canvas);方法來實現(xiàn)原生控件的功能,但是在調(diào)用super.onDraw(canvas)之前和之后都可以實現(xiàn)自己的邏輯

   @Override
    protected void onDraw(Canvas canvas) {
        //在回調(diào)父類之前荚板,實現(xiàn)自己的邏輯凤壁,對textview來說就是繪制文本內(nèi)容前
        super.onDraw(canvas);
         //在回調(diào)父類之后,實現(xiàn)自己的邏輯啸驯,對textview來說就是繪制文本內(nèi)容后
     }
 //實例化畫筆1
        paint1 = new Paint();
        //設(shè)置顏色
        paint1.setColor(getResources().getColor(android.R.color.holo_blue_light));
        //設(shè)置style
        paint1.setStyle(Paint.Style.FILL);

        //同上
        paint2 = new Paint();
        paint2.setColor(Color.YELLOW);
        paint2.setStyle(Paint.Style.FILL);

最重要的部分就是什么時候調(diào)用super了客扎,然后我們開始繪制

 //繪制外層
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint1);
        //繪制內(nèi)層
        canvas.drawRect(10, 10, getMeasuredWidth() - 10, getMeasuredHeight() - 10, paint2);

        canvas.save();
        //繪制文字前平移10像素
        canvas.translate(10, 0);
        //父類完成方法
        super.onDraw(canvas);
        canvas.restore();
image

這個可以利用LinearGradient,Shader罚斗,Matrix徙鱼,來完成,來實現(xiàn)一個閃閃發(fā)光的閃動效果针姿,我們充分的利用Shader渲染器袱吆,來設(shè)置一個不斷變化的LinearGradient,首先我們要在onSizeChanged()方法中完成一些初始化操作

**
 * 文本漸變
 * Created by lgl on 16/3/4.
 */
public class CosuTwoTextView extends TextView {

    int mViewWidth = 0;
    private Paint mPaint;
    private LinearGradient mLinearGradient;
    private Matrix matrix;
    private int mTranslate;

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mViewWidth == 0) {
            mViewWidth = getMeasuredWidth();
            if (mViewWidth > 0) {
                //獲取畫筆對象
                mPaint = getPaint();
                //渲染器
                mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{Color.BLUE, 0xffffffff, Color.BLUE},
                        null, Shader.TileMode.CLAMP);
                mPaint.setShader(mLinearGradient);
                //矩陣
                matrix = new Matrix();
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (matrix != null) {
            mTranslate += mViewWidth + 5;
            if (mTranslate > 2 * mViewWidth / 5) {
                mTranslate = -mViewWidth;
            }
            matrix.setTranslate(mTranslate, 0);
            mLinearGradient.setLocalMatrix(matrix);
            //每隔100毫秒閃動一下
            postInvalidateDelayed(100);
        }
    }
}

2.復合控件

創(chuàng)建一個復核人控件可以很好的創(chuàng)建出具有重要功能的控件集合距淫,這種方式經(jīng)常需要繼承一個合適的ViewGroup绞绒,再給他添加指定功能的控件,從而組成一個新的合適的控件榕暇,通過這種方式創(chuàng)建的控件蓬衡,我們一般都會給他指定的一些屬性,讓他具有更強的擴展性彤枢,下面就以一個TopBar為例子狰晚,講解如何創(chuàng)建復合控件

(1)屬性定義

我們需要給他定義一些屬性,這樣的話缴啡,我們需要在values下新建一個attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TopBar">
        <attr name="title" format="string" />
        <attr name="titleTextSize" format="dimension" />
        <attr name="titleTextColor" format="color" />
        <attr name="leftTextColor" format="color" />
        <attr name="leftBackground" format="reference|color" />
        <attr name="leftText" format="string" />
        <attr name="rightTextColor" format="color" />
        <attr name="rightBackground" format="reference|color" />
        <attr name="rightText" format="string" />
    </declare-styleable>
</resources>

我們在代碼中是可以用< declare-styleable >標簽去聲明一些屬性的壁晒,然后name相當于ID讓我們的類可以找到,业栅,確定好之后秒咐,我們新建一個類谬晕,就叫TopBarView

public class TopBarView extends ViewGroup {

    private int mLeftTextColor;
    private Drawable mLeftBackground;
    private String mLeftText;

    private int mRightTextColor;
    private Drawable mRightBackgroup;
    private String mRightText;

    private float mTitleSize;
    private int mTitleColor;

    private String mTitle;

    //帶參構(gòu)造方法
    public TopBarView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //通過這個方法,你可以從你的attrs.xml文件下讀取讀取到的值存儲在你的TypedArray
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
        //讀取出相應的值設(shè)置屬性
        mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);
        mLeftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
        mLeftText = ta.getString(R.styleable.TopBar_leftText);

        mRightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);
        mRightBackgroup = ta.getDrawable(R.styleable.TopBar_rightBackground);
        mRightText = ta.getString(R.styleable.TopBar_rightText);

        mTitleSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 10);
        mTitleColor = ta.getColor(R.styleable.TopBar_titleTextColor, 0);

        mTitle = ta.getString(R.styleable.TopBar_title);

        //獲取完TypedArray的值之后携取,一般要調(diào)用recyle方法來避免重復創(chuàng)建時候的錯誤
        ta.recycle();
    }

(2)組合控件

實際上攒钳,這個模板也是由左右兩邊的兩個Button和中間的TextView組成的,既然這樣歹茶,我們就可以把這些屬性加給他們了,這里就可以直接定義了夕玩,完整賦值:

 mLeftButton = new Button(context);
        mRightButton = new Button(context);
        mTitleView = new TextView(context);


        //為創(chuàng)建的元素賦值
        mLeftButton.setTextColor(mLeftTextColor);
        mLeftButton.setBackground(mLeftBackground);
        mLeftButton.setText(mLeftText);

        mRightButton.setTextColor(mRightTextColor);
        mRightButton.setBackground(mRightBackgroup);
        mRightButton.setText(mRightText);

        mTitleView.setText(mTitle);
        mTitleView.setTextColor(mTitleColor);
        mTitleView.setTextSize(mTitleSize);
        mTitleView.setGravity(Gravity.CENTER);
         // 為組件元素設(shè)置相應的布局元素
        mLeftParams = new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);
        mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
        // 添加到ViewGroup
        addView(mLeftButton, mLeftParams);

        mRightParams = new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);
        mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
        addView(mRightButton, mRightParams);

        mTitlepParams = new LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);
        mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
        addView(mTitleView, mTitlepParams);

        // 按鈕的點擊事件,不需要具體的實現(xiàn)惊豺,
        // 只需調(diào)用接口的方法燎孟,回調(diào)的時候,會有具體的實現(xiàn)
        mRightButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mListener.rightClick();
            }
        });

        mLeftButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mListener.leftClick();
            }
        });

(3)引用UI模板

xmlns:android="http://schemas.android.com/apk/res/android"

而我們自定義屬性的haunted尸昧,也是需要自己的命名空間

xmlns:custom="http://schemas.android.com/apk/res/custom"
 <com.lgl.viewdemo.CosuTwoTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:leftText="測試標題"
        custom:leftBackground = "#222222"
        custom:leftTextSize="18sp"/>

這樣以此類推揩页,就是我們使用自定義屬性的方法了

3.重寫View來實現(xiàn)全新的控件

重寫View主要自定義類繼承View類,分別重寫里面的onMeasure烹俗、onDraw等方法爆侣。

image
public class CircleView extends View {

    //圓的長度
    private int mCircleXY;
    //屏幕高寬
    private int w, h;
    //圓的半徑
    private float mRadius;
    //圓的畫筆
    private Paint mCirclePaint;
    //弧線的畫筆
    private Paint mArcPaint;
    //文本畫筆
    private Paint mTextPaint;
    //需要顯示的文字
    private String mShowText = "劉桂林";
    //文字大小
    private int mTextSize = 50;
    //圓心掃描的弧度
    private int mSweepAngle = 270;


    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //獲取屏幕高寬
        WindowManager wm = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);
        w = wm.getDefaultDisplay().getWidth();
        h = wm.getDefaultDisplay().getHeight();


        init();
    }

    private void init() {
        mCircleXY = w / 2;
        mRadius = (float) (w * 0.5 / 2);

        mCirclePaint = new Paint();
        mCirclePaint.setColor(Color.BLUE);

        mArcPaint = new Paint();
        //設(shè)置線寬
        mArcPaint.setStrokeWidth(100);
        //設(shè)置空心
        mArcPaint.setStyle(Paint.Style.STROKE);
        //設(shè)置顏色
        mArcPaint.setColor(Color.BLUE);

        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(mTextSize);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //繪制矩形
        RectF mArcRectF = new RectF((float) (w * 0.1), (float) (w * 0.1), (float) (w * 0.9), (float) (w * 0.9));
        //繪制圓
        canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);
        //繪制弧線
        canvas.drawArc(mArcRectF, 270, mSweepAngle, false, mArcPaint);
        //繪制文本
        canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + (mTextSize / 4), mTextPaint);
    }
}

我們還可以設(shè)置一些其他的狀態(tài),設(shè)置他的弧度幢妄,我們寫一個對外的方法

public void setSweepValues(float sweepValues){
        if(sweepValues !=- 0){
            mSweepAngle = sweepValues;
        }else{
            //如果沒有兔仰,我們默認設(shè)置
            mSweepAngle = 30;
        }
        //記得刷新哦
        invalidate();
    }

五、事件攔截機制分析

這章講的是一個事件攔截機制的一些基本概念蕉鸳,乎赴,當Android系統(tǒng)撲捉到用戶的各種輸入事件之后,如何準確的傳遞給真正需要這個事件的控件尼潮尝?其實Android提供了一套非常完善的事件傳遞榕吼,處理機制,來幫助開發(fā)者完成準確的事件分配和處理

要想了解攔截機制勉失,我們首先要知道什么事觸摸事件羹蚣,一般MotionEvent提供的手勢,我們常用的幾個DOWN,UP,MOVE什么的

在MotionEvent中封裝了很多東西乱凿,比如獲取坐標點event.getX()和getRawX()獲取

但是我們這次講的是事件攔截顽素,那什么才是事件攔截,打個比方View和ViewGroup都要這個事件徒蟆,這里就產(chǎn)生了事件攔截戈抄,其實就是一層層遞減的意義,一般ViewGroup我們需要重寫三個方法

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

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

而我們的View就只要兩個了

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

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

得到的點擊事件Log

image

從此圖可以看出后专,onInterceptTouchEvent事件的執(zhí)行順序是由父控件到子控件,并且優(yōu)先于自己控件的onTouchEvent方法執(zhí)行,onTouchEvent事件執(zhí)行的順序正好相反由子控件到父控件绊诲。注意由于此時都返回了false琴儿,是沒有哪一個view來處理此次的touch事件的各個ACTION的给梅,這也是為什么onTouchEvent為什么會一直傳遞到A的原因厅瞎。所以ACTION_MOVE和ACTION_UP等事件得不到相應(處理)又跛,此種情況下即使你在D的onTouchEvent方法里面寫了如下代碼傲武,也不會得到執(zhí)行币厕。


image

如果A的InterceptTouchEvent返回了true甘畅,其余的仍然返回false埂蕊,那么執(zhí)行輸出的log為:


image

image

可以發(fā)現(xiàn)此時A攔截了此次Touch事件,事件不再向A的子控件B C D傳遞疏唾。此時所有的action事件比如手指移動事件ACTION_MOVE或者ACTION_UP事件啦等等事件都交給A的onTouchEvent方法去處理(當然這是在onTouchEvent方法返回true的情況下蓄氧,如果返回false經(jīng)過測試時不會相應這些action的)。B,C ,D控件是的事件處理攔截方法和事件處理方法是無法得到執(zhí)行的槐脏。

只有B的onIntercepteTouchEvent事件返回了true的情況下喉童,打印的log為

image
image

此時由B攔截了此次Touch事件,并不會向C D子控件傳遞顿天;同樣的由于onTouchEvent事件返回為false,所以此次事件的event.getAction()的各種action都不會得到處理堂氯。

同理可知,C控件的onIntercept方法返回了true的情況下牌废,其余的仍然返回false的情況下咽白,輸出log為

image
image

下面說說各個view的onTouchEvent返回true的情況

由于onTouchEvent事件是從子控件到父控件傳遞的,當D的onTouchEvent返回true的時候鸟缕,經(jīng)測試輸出效果如下

image
image

部分圖片引用網(wǎng)上的資源晶框,如有侵權(quán)請告知。
csdn博客:http://blog.csdn.net/aaaaa_sean_m/article/details/74066896

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叁扫,一起剝皮案震驚了整個濱河市三妈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌莫绣,老刑警劉巖畴蒲,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異对室,居然都是意外死亡模燥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門掩宜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔫骂,“玉大人,你說我怎么就攤上這事牺汤×尚” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長补胚。 經(jīng)常有香客問我码耐,道長,這世上最難降的妖魔是什么溶其? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任骚腥,我火速辦了婚禮,結(jié)果婚禮上瓶逃,老公的妹妹穿的比我還像新娘束铭。我一直安慰自己,他們只是感情好厢绝,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布契沫。 她就那樣靜靜地躺著,像睡著了一般代芜。 火紅的嫁衣襯著肌膚如雪埠褪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天挤庇,我揣著相機與錄音钞速,去河邊找鬼。 笑死嫡秕,一個胖子當著我的面吹牛渴语,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昆咽,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼驾凶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掷酗?” 一聲冷哼從身側(cè)響起调违,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泻轰,沒想到半個月后技肩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡浮声,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年虚婿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泳挥。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡然痊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屉符,到底是詐尸還是另有隱情剧浸,我是刑警寧澤锹引,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站辛蚊,受9級特大地震影響粤蝎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袋马,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秸应。 院中可真熱鬧虑凛,春花似錦、人聲如沸软啼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祸挪。三九已至锣披,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贿条,已是汗流浹背雹仿。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留整以,地道東北人胧辽。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像公黑,于是被迫代替她去往敵國和親邑商。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,734評論 25 707
  • 6凡蚜、View的繪制 (1)當測量好一個View之后人断,我們就可以簡單的重寫 onDraw()方法,并在 Canvas...
    b5e7a6386c84閱讀 1,890評論 0 3
  • 今年的10小長假朝蜘,是有意義的恶迈,和有回憶的,在過一年芹务,你問我蝉绷,你去年的今天在干什么,我就可以翻看簡書或思維導圖的存底...
    幸福時光馬金榮閱讀 329評論 0 1
  • 經(jīng)常聽單位一個小領(lǐng)導提起自己以前的故事,她年輕的時候不太會讀書佳晶,長相也一般桅狠,讀書的時候可能就是愛玩愛鬧吧,早早就跟...
    小丸子ariel閱讀 1,242評論 3 12
  • 假期第二天 跑到公園哂太陽 陽光療愈 一有機會 便讓自己在陽光下透明 mei說朋友圈見到的只是碎片 與真實的人們無...
    三月品閱讀 151評論 0 0