android自定义粘性控件,综合使用measure,layout,onTouchEvent,onInterceptTouchEvent等方法

来源:互联网 发布:jquery数组长度 编辑:程序博客网 时间:2024/05/01 10:07

粘性控件

  • 产生粘性控件很简单,我们只需要进行相关的measure和layout并进行事件拦截,和事件处理即可
  • 显示measure方法,measure方法比较复杂,一般我们会根据父容器的测量算子和自身的layoutParams共同决定,这样为了方便起见,全都是让父容器的测量算子决定的
  • 测量代码

    `@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    // TODO Auto-generated method stub    // super.onMeasure(widthMeasureSpec, heightMeasureSpec);    setMeasuredDimension(measureWidth(widthMeasureSpec),            measureHeight(heightMeasureSpec));    int height = MeasureSpec.getSize(heightMeasureSpec);    int count = getChildCount();    for (int i = 0; i < count; i++) {        measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);    }}`
  • 测量的时候先确定自身的大小,之后再确定view的大小(当然也可以先确定view的大小,之后再确定自己的大小,适用于包裹内容的)
  • setMeasuredDimension(measureWidth(widthMeasureSpec),
    measureHeight(heightMeasureSpec));
  • 这样代码是确定自身的大小。我们直接使用父容器传递过来的参数进行相关高度和宽度的确定

     `private int measureWidth(int widthMeasureSpec) {int mode = MeasureSpec.getMode(widthMeasureSpec);int size = MeasureSpec.getSize(widthMeasureSpec);int result = 0;if (mode == MeasureSpec.EXACTLY) {    result = size;} else {    result = 400;    Context ctx = getContext();    if (ctx instanceof Activity) {        Activity a = (Activity) ctx;        result = a.getWindowManager().getDefaultDisplay().getWidth();    }    if (mode == MeasureSpec.AT_MOST) {        result = Math.min(result, size);        Log.e("", size + "");    }}return result;}`
  • 上面代码是通过测量算子的mode和size,共同确定width
  • 高度也类似

    `   private int measureHeight(int heightMeasureSpec) {int mode = MeasureSpec.getMode(heightMeasureSpec);int size = MeasureSpec.getSize(heightMeasureSpec);int result = 0;if (mode == MeasureSpec.EXACTLY) {    result = size;} else {    result = 400;    int count = getChildCount();    if (count > 0) {        View view = getChildAt(0);        view.measure(0, MeasureSpec.makeMeasureSpec(                Integer.MAX_VALUE >> 2, MeasureSpec.EXACTLY));        result = view.getMeasuredHeight();    }    if (mode == MeasureSpec.AT_MOST) {        // 当为warp_content的时候 给一个最小的默认值        result = Math.min(result, size);    }}return result;}`
  • 为什么我们需要确定我们自身的宽度和高度呢?是因为,如果我们不修改测量算子的话,那么测量的模式是MEASURE_ATMOST,size是父容器给我们传递的宽度和高度,因此,我们想手动的修改的话,就需要修改测量算子值(mode和size)
  • 下面代码是测量孩子的

    `@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec,        int parentHeightMeasureSpec) {    // TODO Auto-generated method stub    // super.measureChild(child, parentWidthMeasureSpec,    // parentHeightMeasureSpec);    int height = MeasureSpec.getSize(parentHeightMeasureSpec);    child.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT,            MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height,            MeasureSpec.EXACTLY));}`
  • 孩子的测量算子宽度使用的是matchParent和EXACTLY。高度我们使用的是父容器的高度,也就是我们粘性控件的高度,mode也是EXACTLY
  • 自身的高度和孩子的高度都测量完了,剩下的我们就是layout了

    `protected void onLayout(boolean changed, int l, int t, int r, int b) {// zhihou zaizheli xunhuan diaoyong childde layoutint count = getChildCount();for (int i = 0; i < count; i++) {    View child = getChildAt(i);    child.layout(0, i * child.getMeasuredHeight(), getMeasuredWidth(),            (i + 1) * child.getMeasuredHeight());}totalHeight = (getChildCount() - 1) * getMeasuredHeight();}`
  • 代码很简单,就是遍历孩子,调用layout方法
  • 接下来是事件拦截

    `@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {    if (ev.getAction() == MotionEvent.ACTION_MOVE) {        y = (int) ev.getRawY();        return true;    }    return super.onInterceptTouchEvent(ev);}`
  • 这里拦截了所有的move事件
  • 我们在ontouchEvent中处理

     `public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:    y = (int) event.getRawY();    break;case MotionEvent.ACTION_MOVE:    int min = (int) (event.getRawY() - y);    scrollTo(0, Math.min(totalHeight, Math.max(0, getScrollY() - min)));    y = (int) event.getRawY();    break;case MotionEvent.ACTION_UP:    scroller.startScroll(0, getScrollY(), 0, getPosition()            * getMeasuredHeight() - getScrollY(), Math            .abs(getPosition() * getMeasuredHeight() - getScrollY()));    postInvalidate();    break;}return true;}

    `

  • 我们使用的是Scroller完成的滚动,因此需要重写computeScroll

    `   @Overridepublic void computeScroll() {    // TODO Auto-generated method stub    super.computeScroll();    if (scroller.computeScrollOffset()) {        scrollTo(0, scroller.getCurrY());        postInvalidate();    }}`
  • 接下来,我们计算我们当前显示的是第几个view

    `private int getPosition() {    return (int) (getScrollY() / (getMeasuredHeight() + 0.0f) + 0.5f);}`
  • 在布局加载完的时候给我们的控件添加点击监听

        `@Overrideprotected void onFinishInflate() {    // TODO Auto-generated method stub    super.onFinishInflate();    int count = getChildCount();    for (int i = 0; i < count; i++) {        getChildAt(i).setOnClickListener(this);    }}`
  • 对外面提供我们自己定义的监听接口

    `public interface OnClickItemListener {    public void OnClickItem(View view);}public void setOnClickItemListener(OnClickItemListener listener) {    this.listener = listener;}`
  • 在onClick的时候做出相应

      ` @Overridepublic void onClick(View v) {    int position = getPosition();    View child = getChildAt(position);    if (listener != null) {        listener.OnClickItem(child);    }}

    `

  • 源码如下

     `package com.example.viewgroup1;import android.app.Activity;import android.content.Context;import android.support.v4.view.ViewConfigurationCompat;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.Scroller;import android.view.View.OnClickListener;public class MyViewGroup extends ViewGroup implements OnClickListener {private int y;private int totalHeight;private Scroller scroller;private OnClickItemListener listener;public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {    super(context, attrs, defStyle);    scroller = new Scroller(getContext());}public MyViewGroup(Context context, AttributeSet attrs) {    this(context, attrs, 0);    // TODO Auto-generated constructor stub}public MyViewGroup(Context context) {    this(context, null);    // TODO Auto-generated constructor stub}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    // TODO Auto-generated method stub    // super.onMeasure(widthMeasureSpec, heightMeasureSpec);    setMeasuredDimension(measureWidth(widthMeasureSpec),            measureHeight(heightMeasureSpec));    int height = MeasureSpec.getSize(heightMeasureSpec);    int count = getChildCount();    for (int i = 0; i < count; i++) {        measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);    }}private int measureHeight(int heightMeasureSpec) {    int mode = MeasureSpec.getMode(heightMeasureSpec);    int size = MeasureSpec.getSize(heightMeasureSpec);    int result = 0;    if (mode == MeasureSpec.EXACTLY) {        result = size;    } else {        result = 400;        int count = getChildCount();        if (count > 0) {            View view = getChildAt(0);            view.measure(0, MeasureSpec.makeMeasureSpec(                    Integer.MAX_VALUE >> 2, MeasureSpec.EXACTLY));            result = view.getMeasuredHeight();        }        if (mode == MeasureSpec.AT_MOST) {            // 当为warp_content的时候 给一个最小的默认值            result = Math.min(result, size);        }    }    return result;}private int measureWidth(int widthMeasureSpec) {    int mode = MeasureSpec.getMode(widthMeasureSpec);    int size = MeasureSpec.getSize(widthMeasureSpec);    int result = 0;    if (mode == MeasureSpec.EXACTLY) {        result = size;    } else {        result = 400;        Context ctx = getContext();        if (ctx instanceof Activity) {            Activity a = (Activity) ctx;            result = a.getWindowManager().getDefaultDisplay().getWidth();        }        if (mode == MeasureSpec.AT_MOST) {            result = Math.min(result, size);            Log.e("", size + "");        }    }    return result;}@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec,        int parentHeightMeasureSpec) {    // TODO Auto-generated method stub    // super.measureChild(child, parentWidthMeasureSpec,    // parentHeightMeasureSpec);    int height = MeasureSpec.getSize(parentHeightMeasureSpec);    child.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT,            MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height,            MeasureSpec.EXACTLY));}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {    // zhihou zaizheli xunhuan diaoyong childde layout    int count = getChildCount();    for (int i = 0; i < count; i++) {        View child = getChildAt(i);        child.layout(0, i * child.getMeasuredHeight(), getMeasuredWidth(),                (i + 1) * child.getMeasuredHeight());    }    totalHeight = (getChildCount() - 1) * getMeasuredHeight();}@Overridepublic boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {    case MotionEvent.ACTION_DOWN:        y = (int) event.getRawY();        break;    case MotionEvent.ACTION_MOVE:        int min = (int) (event.getRawY() - y);        scrollTo(0, Math.min(totalHeight, Math.max(0, getScrollY() - min)));        y = (int) event.getRawY();        break;    case MotionEvent.ACTION_UP:        scroller.startScroll(0, getScrollY(), 0, getPosition()                * getMeasuredHeight() - getScrollY(), Math                .abs(getPosition() * getMeasuredHeight() - getScrollY()));        postInvalidate();        break;    }    return true;}private int getPosition() {    return (int) (getScrollY() / (getMeasuredHeight() + 0.0f) + 0.5f);}@Overridepublic void computeScroll() {    // TODO Auto-generated method stub    super.computeScroll();    if (scroller.computeScrollOffset()) {        scrollTo(0, scroller.getCurrY());        postInvalidate();    }}// 点击item的监听public interface OnClickItemListener {    public void OnClickItem(View view);}public void setOnClickItemListener(OnClickItemListener listener) {    this.listener = listener;}@Overrideprotected void onFinishInflate() {    // TODO Auto-generated method stub    super.onFinishInflate();    int count = getChildCount();    for (int i = 0; i < count; i++) {        getChildAt(i).setOnClickListener(this);    }}@Overridepublic void onClick(View v) {    int position = getPosition();    View child = getChildAt(position);    if (listener != null) {        listener.OnClickItem(child);    }}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {    if (ev.getAction() == MotionEvent.ACTION_MOVE) {        y = (int) ev.getRawY();        return true;    }    return super.onInterceptTouchEvent(ev);}}

    `

  • 源码下载 http://download.csdn.net/detail/u013356254/9476805

总结,写自定义view的时候比较复杂的是测量和事件冲突

0 0
原创粉丝点击