安卓复习之旅—自定义view(二)

今天来看一下继承自ViewGroup的自定义view;看看效果先:
这里写图片描述
有点像垂直方向的viewpager吧,下面来一步一步实现它吧。
step1声明需要的一些成员变量

    // 屏幕高度
    private int mScreenHeight;
    private int mScrollStart;
    private int mScrollEnd;
    // 移动时的Y距离
    private int mLastY;
    // 滚动辅助类
    private Scroller mScroller;
    private boolean isScrolling;
    // 加速度检测
    private VelocityTracker mVelocityTracker;
    private int currentPage = 0;
    private onPageChangeListener mOnPageChangeListener;
    private static final String TAG = "VerticalLinearLayout";

其中VelocityTracker 主要用跟踪触摸屏事件(flinging事件和其他gestures手势事件)的速率。用addMovement( MotionEvent)函数将Motion event加入到VelocityTracker类实例中.你可以使用getXVelocity() 或getXVelocity()获得横向和竖向的速率到速率时,但是使用它们之前请先调用computeCurrentVelocity(int) 来初始化速率的单位 。
onPageChangeListener 是自定义的回调接口

/**
     * 设置回调接口
     */
    public void SetOnPageChangeListener(onPageChangeListener listener) {
        this.mOnPageChangeListener = listener;
    }

    public interface onPageChangeListener {
        void onPageChange(int currentPage);
    }

step2 在构造方法中初始化成员变量

// 获取屏幕的高度
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        mScreenHeight = dm.heightPixels;
        Log.e(TAG, "mScreenHeight=  " + mScreenHeight);
        mScroller = new Scroller(context);

step3在onMeasure()方法中计算每个子view的尺寸

    int count = getChildCount();
        for (int i = 0; i < count; i++) {
            Log.e(TAG, "count=  " + count);
            View childView = getChildAt(i);
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }

step4在onLayout()中确定子view的位置

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        if (changed) {
            int count = getChildCount();
            // 设置主布局的高度         
            MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
            mlp.height = mScreenHeight * count;
            setLayoutParams(mlp);

            // 设置每个子布局的位置
            for (int i = 0; i < count; i++) {
                View childView = getChildAt(i);
                if (childView.getVisibility() != View.GONE) {
                    childView.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight);
                }
            }
        }
    }

onLayout的几个参数说明: 1)参数changed表示view有新的尺寸或位置; 2)参数l表示相对于父view的Left位置; 3)参数t表示相对于父view的Top位置; 4)参数r表示相对于父view的Right位置; 5)参数b表示相对于父view的Bottom位置。.
MarginLayoutParams是继承自ViewGroup.LayoutParams
子类有 FrameLayout.LayoutParams,LinearLayout.LayoutParams, RelativeLayout.LayoutParams
step5 处理触摸事件

    // 如果正在滑动,不做处理,调用父类的触摸事件
        Log.e(TAG, "isScrolling=" + isScrolling);
        if (isScrolling) {
            return super.onTouchEvent(event);
        }
        initVelocityTracker(event);

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // Return the scrolled top position of this view
            mScrollStart = getScrollY();

            mLastY = (int) event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            if (!mScroller.isFinished()) {
                // Stops the animation.
                mScroller.abortAnimation();
            }
            int dy = mLastY - (int) event.getY();
            int scrollY = getScrollY();
            // 已经到达顶端
            if (dy < 0 && scrollY + dy < 0) {
                dy = -scrollY;//此时dy=0
            }
            // 已经到达底部
            if (dy > 0 && scrollY + dy > getHeight() - mScreenHeight) {
                dy = getHeight() - mScreenHeight - scrollY;//此时dy=0
            }
            scrollBy(0, dy);
            mLastY = (int) event.getY();
            break;
        case MotionEvent.ACTION_UP:
            mScrollEnd = getScrollY();
            Log.e(TAG, "mScrollStart=" + mScrollStart);
            Log.e(TAG, "mScrollEnd=" + mScrollEnd);
            int dScrollY = mScrollEnd - mScrollStart;

            if (dScrollY > 0) {// 往上滑动
                if (shouldScrollToNext(dScrollY)) {
                    mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);
                } else {
                    mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                }

            } else {// 往上滑
                if (shouldScrollToPre(dScrollY)) {

                    mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - dScrollY);
                } else {
                    mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                }

            }
            isScrolling = true;
            postInvalidate();
            //回收资源
            recycleVelocity();
            break;

        }
        return true;

step6需要重写computeScroll()方法
为了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该 方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐,手动使View/ViewGroup偏移至该处。

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        super.computeScroll();
        // If it returns true, the animation is not yet finished.\

        Log.e(TAG, "mScroller.computeScrollOffset()=" + mScroller.computeScrollOffset());
        if (mScroller.computeScrollOffset()) {// 滑动结束的时候调用scrollTo()
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        } else {
            int position = getScrollY() / mScreenHeight;
            Log.e(TAG, "position=" + position + ",currentPage=" + currentPage);

            if (position != currentPage) {
                if (mOnPageChangeListener != null) {
                    currentPage = position;
                    mOnPageChangeListener.onPageChange(currentPage);
                }
            }
            isScrolling = false;
        }
    }

一些小方法:

    /**
     * 释放资源
     */
    private void recycleVelocity() {
        // TODO Auto-generated method stub
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    /**
     * 初始化加速度追踪器
     * 
     * @param event
     */
    private void initVelocityTracker(MotionEvent event) {
        // TODO Auto-generated method stub

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
    }

    /**
     * 滑动到下一页
     * 
     * @param dScrollY
     * @return
     */
    private boolean shouldScrollToNext(int dScrollY) {
        // TODO Auto-generated method stub
        return dScrollY > mScreenHeight / 2 || Math.abs(getVelocity()) > 600;
    }

    /**
     * 滑动到上一页
     * 
     * @param dScrollY
     * @return
     */
    private boolean shouldScrollToPre(int dScrollY) {
        // TODO Auto-generated method stub
        return -dScrollY > mScreenHeight / 2 || Math.abs(getVelocity()) > 600;
    }

    /**
     * 获取Y方向的加速度
     * @return
     */
    private double getVelocity() {
        // TODO Auto-generated method stub
        mVelocityTracker.computeCurrentVelocity(1000);
        return (int) mVelocityTracker.getYVelocity();
    }

至此,基本工作就完成了;
看看MainActivity

mMianLayout = (VerticalLinearLayout) findViewById(R.id.id_main_ly);
        mMianLayout.SetOnPageChangeListener(this);


    @Override
    public void onPageChange(int currentPage) {
        // TODO Auto-generated method stub
        Toast.makeText(MainActivity.this, "第" + (currentPage + 1) + "页", Toast.LENGTH_SHORT).show();
    }

下载地址:http://download.csdn.net/my

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值