自定义View实战一:基础知识

相关视频:

Android面试解密-自定义View

更多视频

关于view的绘制流程的文章:

Android中View绘制流程浅析

Android应用层View绘制流程与源码分析

相关文章:

Android自定义控件三部曲文章索引(启舰大神)

自定义控件高手(系列文章9篇)

自定义View基础 - 最易懂的自定义View原理系列(1)

自定义View Measure过程 - 最易懂的自定义View原理系列(2)

自定义View Layout过程 - 最易懂的自定义View原理系列(3)

自定义View Draw过程- 最易懂的自定义View原理系列(4)

一、为什么要自定义控件?

  • 特定的显示风格
  • 处理特有的用户交互
  • 优化我们的布局
  • 封装

二、如何自定义控件?

  • (一)、自定义属性的声明与获取;
  • (二)、测量onMeasure;
  • (三)、布局onLayout(ViewGroup)
  • (四)、绘制onDraw;
  • (五)、onTouchEvent;
  • (六)、onInterceptTouchEvent(ViewGroup);

(二)、测量onMeasure;

1、测量模式有三种:EXACTLY、AT_MOST、UNSPECIFIED

EXACTLY:我们设置一个明确的值,比如一个TextView,我们宽高都设置为100dp或者match_parent。
AT_MOST:至多不能超过某个值,一般当我们给一个view设置wrap_content时,有两点值得注意:
            首先,它的内容就是自适应,其次,它的测量模式就是AT_MOST,表示它的最大值不能超过父控件;
UNSPECIFIED:没有限制,view想要多大就可以,一般用于ListView和ScrollView中。在ScrollView中,它的子view
            高度就没有限制,想要多高就有多高;

2、MeasureSpec:可以理解为一个辅助类,封装了Mode和Size

private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        int mode = View.MeasureSpec.getMode(heightMeasureSpec);
        int size = View.MeasureSpec.getSize(heightMeasureSpec);

        if (mode == View.MeasureSpec.EXACTLY) {
            result = size;
        } else {
            //计算自身需要的高度
            result = getNeedHeight() + getPaddingTop() + getPaddingBottom();
            //最大值不能超过size的大小
            if (mode == View.MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    private int getPaddingBottom() {
        return 0;
    }

    private int getPaddingTop() {
        return 0;
    }

    private int getNeedHeight() {
        return 0;
    }

3、setMeasureDimension(result)

4、requestLayout

(三)、布局onLayout(ViewGroup)

如果自定义的是View,不用考虑这个步骤。

1、决定子View的位置;

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt();

            if (child.getVisibility() == GONE) {

            }

            //计算childView layout的左上角x坐标
            left = caculateChildLeft();
            //计算childView layout的左上角y坐标
            top = caculateChildTop();

            int cWidth = 0;
            int cHeight = 0;
            child.layout(left, top, left + cWidth, top + cHeight);
        }
    }

    private int caculateChildLeft() {
        return 0;
    }

    private int caculateChildTop() {
        return 0;
    }

    private View getChildAt() {
        return null;
    }

    private int getChildCount() {
        return 0;
    }

2、尽可能将onMeasure中的一些操作移动到此方法中;

3、requestLayout() 

(四)、绘制onDraw

  •     1、绘制内容区域:onDraw主要是绘制内容区域,像背景的话,系统已经绘制过了,无需考虑
  •     2、invalidate(),postInvalidata()
  •     3、Canvas.drawXXX
  •     4、translate、rotate、scale、skew
  •     5、save()、restore()
     

(五)、onTouchEvent

float mLastMotionY = 0;
    float mActivePointerId = 0;
    float pointerId = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        VelocityTracker velocityTracker = VelocityTracker.obtain();
        velocityTracker.addMovement(event);

        final int action = event.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                //进行一些初始化、赋值等的操作
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                //如果需要进行速度判断
                int initialVelocity = (int) velocityTracker.getYVelocity(1);
                //释放各种资源、重置变量
                break;
            case MotionEvent.ACTION_CANCEL:
                //释放各种资源、重置变量
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                //如果支持多指,在此设置activePointer
                final int index = event.getActionIndex();
                mLastMotionY = event.getY(index);
                mActivePointerId = event.getPointerId(index);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                //如果支持的是多指且抬起的是activePointer,则重新选择一个手指为活跃的手指
                if (pointerId == mActivePointerId) {
                    int pointIndex = 0;
                    final int newPointerIndex = pointIndex == 0 ? 1 : 0;
                    mLastMotionY = event.getY(newPointerIndex);
                    mActivePointerId = event.getPointerId(newPointerIndex);
                    if (velocityTracker != null) {
                        velocityTracker.clear();
                    }
                }
                break;
        }
        return true;
    }

(六)、onInterceptTouchEvent(ViewGroup)

private int mActivePointerId = 0;
    public static final int INVALID_POINTER = 1;
    float mLastMotionY = 0;
    private int mTouchSlop = 2;
    private boolean mIsBeginDragged = false;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE:
                final int acitivePointerId = mActivePointerId;
                if (acitivePointerId == INVALID_POINTER) {
                    break;
                }
                final int pointerIndex = ev.findPointerIndex(acitivePointerId);
                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = (int) Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop) {
                    mIsBeginDragged = true;
                    mLastMotionY = y;
                }
                break;
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return mIsBeginDragged;
    }

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值