一、View的滑动:
1.获取最小滑动距离:

int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();



2.Scroller类(滑动控制):
(1)创建:

Scroller scroller = new Scroller(context);



(2)控制滑动:

//getScrollX()获取当前view的x坐标, 

mScroller.startScroll(getScrollX(), 0, 距离值, 0, Math.abs(delta));   //此句设置目标坐标值 

invalidate();  //此句真实移动



(3)实现computeScroll方法,进行实时移动(多次移动就形成了连续动画):

@Override 

public void computeScroll() {  //此方法会一直被调用,直到移到目标位置 

    if (scroller.computeScrollOffset()) { 

        //使用scrollTo移到目标位置,传入目标位置xy坐标;也可以使用scrollBy,scrollBy传入从原位置到目标位置的距离值 

        scrollTo(scroller.getCurrX(), scroller.getCurrY()); 

        postInvalidate(); //此句真实移动,会让View重绘,computeScroll方法一直被调用 

    } 

}



3.VelocityTracker类(获取x轴与y轴滑动速率,单位:像素/时间):

(1)创建: 

VelocityTracker vt = VelocityTracker.obtain(); 


(2)接管View的onTouchEvent事件: 

vt.addMovement(event); 


(3)计算速率: 

vt.computeCurrentVelocity(1000); 


(4)获取速率: 

float xV = vt.getXVelocity();  //x轴方向速率 

float yV = vt.getYVelocity();  //y轴方向速率 


(5)释放: 

vt.clear(); 

vt.recycle();



4.GestureDetector类(对一些手势的封装):

二、父View与子View嵌套滑动处理:

GestureDetector gd = new GestureDetector(new GestureDetector.OnGestureListener(){ 

    @Override 

    public boolean onDown(MotionEvent e) { //按下(ACTION_DOWN) 

        return false; 

    } 

    @Override 

    public void onShowPress(MotionEvent e) {//按下未松开或拖动(ACTION_DOWN) 

    } 

    @Override 

    public boolean onSingleTapUp(MotionEvent e) {//松开(ACTION_UP) 

        return false; 

    } 

    @Override 

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //按下并拖动(ACTION_MOVE) 

        return false; 

    } 

    @Override 

    public void onLongPress(MotionEvent e) { //长按 

    } 

    @Override 

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //快速滑动,velocityX为x轴的速率,velocityY为y轴的速率 

        return false; 

    } 

}); 

gd.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener(){ 

    @Override 

    public boolean onSingleTapConfirmed(MotionEvent e) { //单击 

        return false; 

    } 

    @Override 

    public boolean onDoubleTap(MotionEvent e) {  //双击 

        return false; 

    } 

    @Override 

    public boolean onDoubleTapEvent(MotionEvent e) { //表示发生了双击事件 

        return false; 

    } 

}); 

//处理长按后无法拖动的问题 

gd.isLongpressEnabled();


1.父View的onInterceptTouchEvent中处理ACTION_MOVE事件,对需要由父View拖动的情况返回true:


@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 

    switch (ev.getAction()) { 

        case MotionEvent.ACTION_MOVE: 

            if (条件) {   //需要由父View处理的条件 

                return true;  //由父View处理事件 

            } 

            break; 

    } 

    return false; 

}


2.由子View处理事件:


(1)父View的onInterceptTouchEvent中除ACTION_DOWN事件全部返回true:


@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 

    switch (ev.getAction()) { 

        case MotionEvent.ACTION_DOWN: 

            return false; 

    } 

    return true; 

}



(2)子View的dispatchTouchEvent方法中处理


@Override 

public boolean dispatchTouchEvent(MotionEvent ev) { 

    switch (ev.getAction()) { 

        case MotionEvent.ACTION_DOWN: 

            //让父view不拦截事件 

            parentView.requestDisallowInterceptTouchEvent(true); 

            break; 

        case MotionEvent.ACTION_MOVE: 

            if (条件) {   //父View需要处理事件的条件 

                    //让父view拦截事件 

                    parentView.requestDisallowInterceptTouchEvent(false); 

            } 

            break; 

    } 

    return false; 

}


三、View的测量模式:


SpecMode模式有3种:


UNSPECIFIED:


无大小限制,此值一般在系统内使用



EXACTLY:


精确大小,指定match_params和或数值



AT_MOST:


大小不确定,对应wrap_content



获取SpecMode模式:


int specMode = MeasureSpec.getMode(spce);



获取宽高值


int specSize = MeasureSpec.getSize(spce);



四、自定义View:


1.继承View,重写onDraw方法,显示内容,并且处理有padding值的情况:


protected void onDraw(Canvas canvas){ 

    super.onDraw(canvas); 

    int leftPd = getPaddingLeft(); 

    int rightPd = getPaddingRight(); 

    int topPd = getPaddingTop(); 

    int bottomPd = getPaddingBottom(); 

    int width = getWidth() - leftPd - rightPd;  //实际宽度需要减去left与right的padding值 

    int height = getHeight() - topPd - bottomPd;  //实际高度需要减去top与bottom的padding值 

    //绘制内容,比如canvas.drawLine() 

    ... 

}




2.重写onMeasure,处理宽度与高度为wrap_content的情况:


@Override 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 

    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);  //获取宽度SpecMode模式 

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);  //获取宽度Size大小 

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);  //获取高度SpecMode模式 

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);  //获取高度Size大小 

    /* 

    * 处理View使用wrap_content的情况,给个默认宽度与高度值,不处理时wrap_content和match_parent效果一样 

    */ 

    if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){  //宽高都为wrap_content 

        setMeasuredDimension(自定义大小, 自定义大小); 

    } else if(widthMode == MeasureSpec.AT_MOST){      //宽都为wrap_content 

        setMeasuredDimension(自定义大小, heightSize); 

    } else if(heightMode == MeasureSpec.AT_MOST){     //高都为wrap_content 

        setMeasuredDimension(widthSize, 自定义大小); 

    } 

}


四、自定义ViewGroup:


1.重写onMeasure方法,为子View测量宽度与高度,为自身测量大小:


@Override 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 

    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

    // 内部会循环调用子View的measure,测量子View大小 

    measureChildren(widthMeasureSpec, heightMeasureSpec); 

    //以下为自身测量大小 

    int widthMode = MeasureSpec.getMode(widthMeasureSpec); // 获取宽度SpecMode模式 

    int widthSize = MeasureSpec.getSize(widthMeasureSpec); // 获取宽度Size大小 

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 获取高度SpecMode模式 

    int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 获取高度Size大小 

    int childCount = getChildCount(); 

    View v = null; 

    if (childCount > 0) { 

        v = getChildAt(0); 

    } 

    if (v != null) { 

        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { // 宽高都为wrap_content,全用子View的 

            setMeasuredDimension(v.getMeasuredWidth() * childCount, v.getMeasuredHeight()); 

        } else if (widthMode == MeasureSpec.AT_MOST) { // 宽为wrap_content 

            setMeasuredDimension(v.getMeasuredWidth() * childCount, heightSize); //宽度为子View之和,高度为原有的值 

        } else if (heightMode == MeasureSpec.AT_MOST) { // 高为wrap_content 

            setMeasuredDimension(widthSize, v.getMeasuredHeight()); //宽度用原有的,高度为子View的高度 

        } 

    } 

}




2.重写onLayout方法,为子View布局位置与大小:


@Override 

protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) { 

    int childCount = getChildCount();  //取出子View个数 

    int left = 0; 

    for (int i = 0; i < childCount; i++) { 

        View v = getChildAt(i); 

        if (v.getVisibility() != View.GONE) {  //只布局显示的 

            int widthMeasured = v.getMeasuredWidth(); 

            v.layout(left, 0, left + widthMeasured, v.getMeasuredHeight()); 

            left = left + widthMeasured; //按左右布局子View 

        } 

    } 

}

五、View的自定义属性:


1.在values下创建attrs.xml文件(文件名可以任意):


<?xml version="1.0" encoding="utf-8"?> 

<resources> 

    <declare-styleable name="属性集合名"> 

        <attr name="属性名1" format="color" />   //color表示颜色属性(reference为资源id,还有基本数据类型等) 

    </declare-styleable name=""> 

</resources>


2.在xml中使用自定义属性:


<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto" //此句必须,这里以app为前缀,可以为别的名称 

    ... > 

    <com...View  

        app:属性名1="@color/theme_green" //为自定义属性设值 

        ... /> 

</RelativeLayout>


3.在代码中获取布局中的自定义属性:


TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.属性集合名); 

int color = ta.getColor(styleable.属性名1, 默认值); 

ta.recycle();