Android 自定义View 开发 入门与实践7 自定义 attr ViewGroup gestureDetector手势检测,window (manager)

自定义View 之设置自定义属性attr

在Values下面创建一个attr.xml文件



    
        
        
            
            
        
        
        
        
        
        
    

    
        
        
            
            
        
    


解释一下format对应的值:

"reference":某一资源id

"color":颜色值

"boolean":布尔值

"dimen":尺寸值

"float":浮点值

"integer":整型值

"string":字符串

"fraction":百分数

"enum":枚举值

"flag":位或运算


ViewGroup绘制流程

注意:View 及ViewGroup基本相同,只是在ViewGroup中不仅要绘制自己,还要绘制其中的子控件,而View 只要绘制自己就可以了

    绘制流程分为三步:测量,布局,绘制,分别对应onMeasure(),onLayout(),onDraw()

onMeasure():测量当前控件的大小,为正式布局提供建议(只是建议,用不用还要看onLayout()函数)

   主要是setMeasuredDimension(宽,高)设置

   三个mode

   Unspecified(未指定):父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小

   Exactly (完全):父元素 决定子元素的确切大小,子元素将被限定在给定的边界里而忽略他本身的大小

   At_most(至多):子元素至多达到指定大小的值

模式提取:

int mode = MeasureSpec.getMode(widthMeasureSpec);
        if(mode == MeasureSpec.UNSPECIFIED){ }
        if(mode == MeasureSpec.EXACTLY){ }
        if(mode == MeasureSpec.AT_MOST){ }

获取 宽高

 int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
 int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

onLayout(): 使用layout()函数对所有子控件进行布局

onDraw():根据布局的位置绘图


Android 自定义View 开发 入门与实践7 自定义 attr ViewGroup gestureDetector手势检测,window (manager)_第1张图片

public class MyLinearView extends ViewGroup {

    public MyLinearView(Context context) {
        this(context, null);
    }

    public MyLinearView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyLinearView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int mearsureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int mearsureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
        int mearsureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        int height = 0;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View childAt = getChildAt(i);
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
            //获得子控件的高度和宽度
            int childWidth = childAt.getMeasuredWidth();
            int childHeight = childAt.getMeasuredHeight();
            //得到最大宽度 , 并且累加高度

            height += childHeight;

            width = Math.max(childWidth, width);
        }

        int finalwidth = 0;
        int finalHeight = 0;
        if (mearsureWidthMode == MeasureSpec.EXACTLY) {
            //使用测量宽度
            finalwidth = mearsureWidth;
        } else {
            //使用计算的宽度
            finalwidth = width;
        }
        if (mearsureHeightMode == MeasureSpec.EXACTLY) {
            finalHeight = measureHeight;
        } else {
            finalHeight = height;
        }

        setMeasuredDimension(finalwidth, finalHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View childAt = getChildAt(i);
            int childWidth = childAt.getMeasuredWidth();
            int childHeight = childAt.getMeasuredHeight();

            childAt.layout(0, top, childWidth, top + childHeight);
            top += childHeight;
        }
    }


}
 

            

            

            

        

获取子控件Margin

如果要自定义ViewGroup支持子控件的layout_margin值,则自定义的ViewGroup类必须重写generateLayoutParams()函数,并且在该函数中返回一个ViewGroup.MarginLayoutParams派生类对象.

需要在修改一点

public class MyLinearView extends ViewGroup {

    public MyLinearView(Context context) {
        this(context, null);
    }

    public MyLinearView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyLinearView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int mearsureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int mearsureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
        int mearsureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        int height = 0;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View childAt = getChildAt(i);

            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();

            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
            //获得子控件的高度和宽度
            int childWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            int childHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
            //得到最大宽度 , 并且累加高度

            height += childHeight;

            width = Math.max(childWidth, width);
        }

        int finalwidth = 0;
        int finalHeight = 0;
        if (mearsureWidthMode == MeasureSpec.EXACTLY) {
            //使用测量宽度
            finalwidth = mearsureWidth;
        } else {
            //使用计算的宽度
            finalwidth = width;
        }
        if (mearsureHeightMode == MeasureSpec.EXACTLY) {
            finalHeight = measureHeight;
        } else {
            finalHeight = height;
        }

        setMeasuredDimension(finalwidth, finalHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View childAt = getChildAt(i);

            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();
            int childWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            int childHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;

            childAt.layout(0, top+layoutParams.topMargin, childWidth, top + childHeight);
            top += childHeight;
        }
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {

        return new MarginLayoutParams(p);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        MarginLayoutParams marginLayoutParams = new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        return marginLayoutParams;
    }

}

Android 自定义View 开发 入门与实践7 自定义 attr ViewGroup gestureDetector手势检测,window (manager)_第2张图片


实现FlowLayout 布局

Android 自定义View 开发 入门与实践7 自定义 attr ViewGroup gestureDetector手势检测,window (manager)_第3张图片

public class FlowLayout extends ViewGroup {

    public FlowLayout(Context context) {
        this(context, null);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量模式
        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);

        //测量宽高
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        int lineWidth = 0;
        int lineHeight = 0;
        int height = 0;
        int width = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);

            MarginLayoutParams lp = (MarginLayoutParams) childAt.getLayoutParams();
            int childWidth = childAt.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = childAt.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            if (lineWidth + childWidth > measureWidth) {
                height += lineHeight;
                //因为当前行放不下当前控件,而将此控件调到下一行,所以将此控件的高度和宽度初始化给lineHeight,lineWidth
                lineHeight = childHeight;
                lineWidth = childWidth;
            } else {
                //否则累加值lineWidth , lineHeight 并取最大高度
                lineHeight = Math.max(lineHeight, childHeight);
                lineWidth += childWidth;
            }

            //因为最后一行是不会超出width范围的,所以需要单独处理
            if (i == childCount - 1) {
                height += lineHeight;
                width += Math.max(width, lineWidth);
            }
            //当属性是MeasureSpec.EXACTLY时,那么它的高度是确定的
            //当属性是wrap_content时,由于是通过内部控价的大小来最终确定他的大小的,所以它的大小是不确定的
            //此时对应的属性是MeasureSpec.At_most,这就需要我们自己计算他应当的大小,并设置进去

            int finalWidth = 0;
            if (measureWidthMode == MeasureSpec.EXACTLY) {
                finalWidth = measureWidth;
            } else {
                finalWidth = width;
            }


            int finalHeight = 0;
            if (measureHeightMode == MeasureSpec.EXACTLY) {
                finalHeight = measureHeight;
            } else {
                finalHeight = height;
            }

            setMeasuredDimension(finalWidth, finalHeight);
        }

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        int lineWidth = 0;
        int lineHeight = 0;
        int top = 0;
        int left = 0;
        for (int i = 0; i < count; i++) {
            View childAt = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) childAt.getLayoutParams();
            int childWidth = childAt.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = childAt.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            if (childWidth + lineWidth > getMeasuredWidth()) {
                //如果换行,则当前控件将放到下一行,从最左边开始,所以left就是0;
                //而top则需要加上 上一行的行高,才是这个控件的top坐标
                top += lineHeight;
                left = 0;
                //同样,重新初始化lineHeight 和 lineWidth
                lineHeight = childHeight;
                lineWidth = childWidth;
            } else {
                lineHeight = Math.max(lineHeight, childHeight);
                lineWidth += childWidth;
            }

            //计算childView 的 left top right bottom
            int lc = left + lp.leftMargin;
            int tc = top + lp.topMargin;

            int rc = lc + childAt.getMeasuredWidth();
            int bc = tc + childAt.getMeasuredHeight();
            childAt.layout(lc, tc, rc,bc);
            //将left 作为下一个子控件的起始点
            left += childWidth;
        }

    }


    /******************* 设置Margin必须重写底下三个方法 ********************/
    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {

        return new MarginLayoutParams(p);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }


}
 int childCount = mViewBinding.flowlayout.getChildCount();

        for (int i = 0; i < childCount; i++) {
            View childAt = mViewBinding.flowlayout.getChildAt(i);
            DevShapeUtils.shape(DevShape.RECTANGLE)
                    .radius(6)
                    .solid(R.color.black)
                    .into(childAt);
        }

13.控件高级属性

gestureDetector手势检测

概述:当用户触摸屏幕的时候,会产生许多手势,如down,up,scroll,fling等

一般用onTouch(MotionEvent)

如果是复杂的手势就用GestureDetector

   分为俩个接口

onGestureListener     onDoubleTapListener

一个外部类

SimpleOnGestureListener

public class GestureVew implements GestureDetector.OnGestureListener {

    //用户按下屏幕就会触发
    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    //如果按下的时间超过瞬间,而且在按下的时候没有松开或者拖动的,触发
    @Override
    public void onShowPress(MotionEvent e) {

    }

    //一次单独的轻击抬起操作,如果在按下去还有其他操作就不行
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    //在屏幕上拖动事件,无论是用手拖动View,还是以抛的动作滚动,都会多次触发这个函数,在ACTION_MOVE动作发生时就会触发该函数
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    } 
    
    //长按触摸屏,超过一定时长,就会触发这个函数
    @Override
    public void onLongPress(MotionEvent e) {

    }

    //滑屏,用户按下触摸屏,快速移动后松开,由一个MotionEvent ACTION_DOWN ,多个ACTION_MOVE 一个ACTION_UP触发
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }
}

使用:

<1>.创建onGestureListener()监听函数

GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {

            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {

            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return false;
            }
        };

或者直接类 implements

GestureDetector.OnGestureListener

<2>.创建GestureDetector

GestureDetector gestureDetector = new GestureDetector(this, onGestureListener);

<3>.拦截onTouch


    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

<4>.绑定控件

mViewBinding.tv.setOnTouchListener(this);

https://zhangqifan.blog.csdn.net/article/details/87855781


13.2Window 与 WindowManager

补充几个知识点:

构建其LayoutParams时,Flag和Type 比较重要

Flag:用来控制Window 的显示特性

表示此window不需要获取焦点,不接收各种输入事件,此标记会同时启用FLAG_BOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window

   FLAG_NOT_FOCUSABLE = 0x00000008


自己Window区域内的事件自己处理;自己Window区域外的事件传递给底层Window处理,一般这个选项回默认开启,否则其他Window无法收到事件

   FLAG_NOT_TOUCH_MODAL = 0x00000020


可以让Window显示在锁屏上

   FLAG_SHOW_WHEN_LOCKED = 0X00080000



Type参数是int类型的,表示Window的类型,Window有是那种类型

应用Window 层级范围 1 - 99

子Window的层级范围 1000-1999

系统Window的层级范围 2000-2999

最好加个权限,适用于系统类型的Window

WindowManager 常用的三个方法

addView updateView removeView


腾讯电脑管家的小火箭

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Android 自定义View 开发 入门与实践7 自定义 attr ViewGroup gestureDetector手势检测,window (manager))