天天看點

安卓複習之旅—自定義view(二)

今天來看一下繼承自ViewGroup的自定義view;看看效果先:

安卓複習之旅—自定義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 = ;
    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 = ; 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 = ; i < count; i++) {
                View childView = getChildAt(i);
                if (childView.getVisibility() != View.GONE) {
                    childView.layout(l, i * mScreenHeight, r, (i + ) * 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 <  && scrollY + dy < ) {
                dy = -scrollY;//此時dy=0
            }
            // 已經到達底部
            if (dy >  && scrollY + dy > getHeight() - mScreenHeight) {
                dy = getHeight() - mScreenHeight - scrollY;//此時dy=0
            }
            scrollBy(, 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 > ) {// 往上滑動
                if (shouldScrollToNext(dScrollY)) {
                    mScroller.startScroll(, getScrollY(), , mScreenHeight - dScrollY);
                } else {
                    mScroller.startScroll(, getScrollY(), , -dScrollY);
                }

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

                    mScroller.startScroll(, getScrollY(), , -mScreenHeight - dScrollY);
                } else {
                    mScroller.startScroll(, getScrollY(), , -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(, 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 /  || Math.abs(getVelocity()) > ;
    }

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

    /**
     * 擷取Y方向的加速度
     * @return
     */
    private double getVelocity() {
        // TODO Auto-generated method stub
        mVelocityTracker.computeCurrentVelocity();
        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 + ) + "頁", Toast.LENGTH_SHORT).show();
    }
           

下載下傳位址:http://download.csdn.net/my