自定义ViewGroup

自定义ViewGroup
  ViewGroup就是对子View的管理
  自定义ViewGroup需要重写onMeasure()方法对子View测量,重写onLayout()方法来确定子View的位置,重写onTouchEvent()方法响应事件

测量子View的方法
  measureChild(childView, widthMeasureSpec, heightMeasureSpec);

布局中设置ViewGroup的高度
  MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
  mlp.height = mScreenHeight * childCount;
  setLayoutParams(mlp);

响应事件这里使用了滑动
  Scroller计算坐标的工具类,它的响应过程是
  scrollTo()/scrollBy() ---> invalidate()/postInvalidate() ---> computeScroll()
  这个过程是没有动画效果的,只是更新了坐标位置
  使用动画的滑动,是通过startScroll,系统不断回调computeScroll,在回调中不断执行scrollTo,形成动画效果

完整代码

/*
 *
 * 1、首先测量子View
 *      onMeasure中进行
 * 2、View的位置摆放
 *      这里每个子View占一屏,在onLayout中进行
 *      在初始化中我们得到屏幕的相关参数
 * 3、滑动监听事件
 *      重写onTouchEvent方法,这里使用了Scroller的方法来滑动
 *
 */
public class MyScrollView extends ViewGroup{

    private final int mScreenHeight;
    private final Scroller mScroller;
    private int mLastY;
    private int mStart;
    private int mEnd;

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        mScreenHeight = dm.heightPixels;
        // Scroller是一个计算坐标的工具类。其实Scroller与View的滑动是没有关系的,它只是计算在动画执行某个时间所在的某个位置的坐标,这样就形成了坐标路线,再view根据坐标路线循环invalidate在界上新显示,就形成了我们看到的平滑移动了
        mScroller = new Scroller(context);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int count = getChildCount();
        for(int i=0;i<count;i++){
            View childView = getChildAt(i);
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        // 设置ViewGroup的高度
        MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
        mlp.height = mScreenHeight * childCount;
        setLayoutParams(mlp);
        for(int i=0;i<childCount;i++){
            View child = getChildAt(i);
            if(child.getVisibility()!=View.GONE){
                child.layout(l, i*mScreenHeight, r , (i+1)*mScreenHeight);
            }
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                mStart = getScrollY();
                break;
            case MotionEvent.ACTION_MOVE:
                if(!mScroller.isFinished()){
                    // 停止动画(就是设置一个标记而已,不再计算坐标的变化值)
                    mScroller.abortAnimation();
                }
                // 滑动的距离,scrollY不能小于0,也不能大于最后一屏高度
                int dy = mLastY - y;
                if(getScrollY() < 0){
                    dy = 0;
                }
                if(getScrollY() > getHeight() - mScreenHeight){
                    dy = 0;
                }
                // scrollTo()/scrollBy() ---> invalidate()/postInvalidate() ---> computeScroll()
                scrollBy(0, dy);
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                mEnd = getScrollY();
                int dScrollY = mEnd - mStart;
                if(dScrollY > 0){
                    // 向下
                    if(dScrollY < mScreenHeight / 3){
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    }else{
                        mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);
                    }
                }else{
                    // 向上
                    if(-dScrollY < mScreenHeight / 3){
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    }else{
                        mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight-dScrollY);
                    }
                }
                break;
        }
        postInvalidate();
        return true;
    }


    /**
     * startScroll()只是开始动画,如果computeScroll()方法不实现则没有任何效果,值是为了给后面视图移动添加一些动画效果
     * Scroller.computeScrollOffset()方法是判断scroller的移动动画是否完成,当你调用startScroll()方法的时候这个方法返回的值一直都为true
     * 如果采用其它方式移动视图比如:scrollTo()或 scrollBy时那么这个方法返回false
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if(mScroller.computeScrollOffset()){
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }
}

 

XML中使用自定义控件

    <me.zcy.androidheroes.MyScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/test1" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/test2" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/test3" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/test4" />

    </me.zcy.androidheroes.MyScrollView>

  

效果图

  

posted @ 2015-10-25 15:19  轻云沉峰  阅读(173)  评论(0)    收藏  举报