欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

菜鸟进阶笔记|初探自定义View|05|自定义ViewGroup

程序员文章站 2022-06-08 17:36:12
...

类似ScrollView的滑动,并附加黏性效果

自定义View中代码如下:

public class MyScrollView extends ViewGroup {
    private static final String TAG = "MyScrollView";
    private Scroller mScroller;
    private int mScreenHeight;    //获取屏幕高度
    int mLastY = 0;
    int mStart = 0;

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        mScreenHeight = wm.getDefaultDisplay().getHeight(); //记录设备屏幕高度
    }

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 在ViewGroup能够滚动之前,需要先放置好它的子View。使用遍历的方式来通知子View对自身进行测量
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @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);
            //要求其子View根据此View测量自己
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    /**
     * 接下来就要对子View进行放置位置的设定。我们让每个子View都显示完整的一屏,方便后续操作。
     * 在放置子View前,需要确定整个ViewGroup的高度。
     * 在获取了整个ViewGroup的高度之后,就可以通过遍历来设定每个子View需要放置的位置了
     * 直接通过调用子View的Layout()方法,并将具体的位置座位参数传递进去即可,代码如下所示。
     * 在本例中由于让每个子View都占一屏的高度,因此整个ViewGroup的高度即子View的个数乘以屏幕的高度。
     *
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @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(   //给子View设置位置
                        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(); //停止动画
                }
                int dy = mLastY - y;
                if (getScrollY() < 0) {
                    dy = 0;
                }
                if (getScrollY() > mScreenHeight) {
                    dy = 0;
                }
                scrollBy(0, dy);
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                //当手指离开后,如果手指滑动超过一定距离,则平滑到下一个View,如果小于一定距离,则回滚到原来的位置。
                int 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;
        }
        invalidate();
        return true;
    }

    //该回调在执行invalidate()方法时会执行
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {  //判断是否已终止滚动
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }
}

在xml中如下使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.myview.myview.MyScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary" />
    </com.example.myview.myview.MyScrollView>
</LinearLayout>

就不贴效果图啦,跑一下就知道了(#^.^#)

相关标签: 自定义ViewGroup