自定义view

view

Posted by Cc1over on July 23, 2018

自定义view的步骤:

  • 自定义属性的声明与获取
  • 测量onMeasure
  • 布局onLayout(ViewGroup)
  • 绘制onDraw
  • onTouchEvent
  • onInterceTouchEvent(ViewGroup)

1)自定义属性声明与获取

1) 分析需要的自定义属性
2) 在res/values/attrs.xml定义声明
3) 在layout xml文件中进行使用
4) 在view的构造方法中进行获取

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <declare-styleable name="CustomTitleView">
        <attr name="titleText" format="string" />
        <attr name="titleTextColor" format="color" />
        <attr name="titleTextSize" format="dimension" />
    </declare-styleable>
 
</resources>

    public ViewDemo(Context context, AttributeSet attrs) {
        super(context, attrs);
        /**
         * TypedArray:对象描述类似数组的一个潜在的二进制数据的缓冲区(官方描述)
         * 就是系统在默认的资源文件R.styleable中去获取相关的配置。
         * 如果appearance不为空,它就会去寻找获取相关属性
         * 也就是冲我们自定属性样式中,来引用你需要的某条属性
        */
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.Myview);
        int colors =typedArray.getColor(R.styleable.Myview_rect_color,0xffff0000);//给他赋值一个红色
        setBackgroundColor(colors);

        typedArray.recycle();
    }

2)测量onMeasure

1) EXACTLY,AT_MOST,UNSPECIFIED
2) MeasureSpec
3) setMeasuredDimension
4) requestLayout()

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();
            if(mode==View.MeasureSpec.AT_MOST){
                result = Math.min(result,size);
            }
        }
        return result;
    }

3)布局onLayout(ViewGroup)

1) 决定子view的位置
2) 尽可能将onMeasure中的一些操作移动到此方法中
3) requestLayout()

 private void onLayout(boolean change,int l,int t,int r,int b){
         final int childCount = getChildCount();
         for(int i=0;i<childCount;i++){
             final View child = getChildAt();
             if(child.getVisibility()==View.GONE){
                 continue;
             }
             left = caculateChildLeft(); // 计算childview左上角x坐标
             top = caculateChildTop(); // 计算childview左上角y坐标
             child.layout(left,top,left+cWidth,top+cWidth);
         }
    }

4)绘制onDraw

1) 绘制内容区域
2) invalidate(),postInvalidate()
3) Canvas.drewXXX
4) translate、rotate、scale、skew
5) save()、restore()

 protected synchoronized void onDraw(Canvas canvas){
     // 使用canvas相关API绘制anything you want
 }

5)onTouchEvent

1) ACTION_DOWN、 ACTION_MOVE、 ACTION_UP
2) ACTION_POINTER_DOWN、 ACTION_POINTER_UP
3) parent.requestDisallowIntercepetTouchEvent(true);
4) VelocityTracker

public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //进行一些初始化赋值的操作
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                // 如果需要进行加速度判断
                int initialVelocity = (int) veloctyTracker.getYVelocity(mActivePointerId);
                // 释放各种资源,重置变量
                break;
            case MotionEvent.ACTION_CANCEL:
                // 释放各种资源,重置变量
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                // 如果支持多指,在此设置activePointer
                final int index = ev.getActionIndex();
                mLastMotionY = (int) ev.getY(index);
                mActivePointerId = ev.getPointerId(newPointerIndex);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                if (pointerId == mActivePointerId) {
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastMotionY = (int) ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                    if(mVelocityTracker!=null){
                        mVelocityTracker.clear();
                    }
                }
                break;
        }
        return true;
    }

6)onInterceptTouchEvent

1)ACTION_DOWN、 ACTION_MOVE、 ACTION_UP
2)ACTION_POINTER_DOWN、 ACTION_POINTER_UP 3)决定是否拦截该手势

  @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    break;
                }
                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop
                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
                    mIsBeingDragged = true;
                    mLastMotionY = y;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    mNestedYOffset = 0;
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_DOWN: {
                    break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                break;
        }
        return mIsBeingDragged;
    }