zoukankan      html  css  js  c++  java
  • Android 自定义ViewGroup

      前面几节,我们重点讨论了自定义View的三板斧,这节我们来讨论自定义ViewGroup,为什么要自定义ViewGroup,其实就是为了更好的管理View。

      自定义ViewGroup无非那么几步:

      Ⅰ、重写OnMeasure()方法,测试子控件的大小。

      Ⅱ、重写onLayout()方法,计算子控件的布局。

      Ⅲ、在onDraw()方法中,绘制子控件,可有可无。

      Ⅳ、监听onTouch事件,响应屏幕触摸事件。

      相应思维导图如下所示:

      连篇累牍的说了这么多,我们通过一个小案例来理解这个自定义ViewGroup把,看看如何实现ViewGroup。

        简单的黏性ScrollView

      简单概述

      这是一个原生scrollView效果非常类似的效果,他可以像scrollView一样上下滑动的效果,不过我们增加了一个黏性效果。何为黏性效果了?即当一个子View向上滑动大于一定距离的时候,它将自动向上滑动,显示下一个子View。同理,如果一个子View滑动距离小于某一个距离,它将滚回到原始的位置。

      实现思路

      投篮要找角度,控件要找思路。我们来分析要实现此自定义ViewGroup的基本思路了:

      Ⅰ、在OnMeasure()方法中,对每个子控件的大小进行测量了。

      Ⅱ、在OnLayout()方法中,对每个要显示控件的位置进行计算。

      Ⅲ、紧接着,就是在OnTouchEvent()方法,监听着手势触摸事件,判断它是上滑还是下滑,判断它的滑动距离是否大于我们设定的值,如果大于这个值,就将它移动到下一个子View,否则,滚回到原来的位置。

      有了这样的思路之后,我们只需要所做的是按部就班实现代码编写

      具体实现

      第一步、进行一些变量的初始化,代码如下:

        private void init(Context context) {
            WindowManager manager = (WindowManager) context
                    .getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics displayMetrics = new DisplayMetrics();
            manager.getDefaultDisplay().getMetrics(displayMetrics);
            mScreenHeight = displayMetrics.heightPixels;
            mScroller = new Scroller(context);
        }

      获取屏幕高度作为每个控件的高度,将scroller控件进行初始化。

       第二步 、实现控件的测量,代码如下:

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

      我们看到每个子控件的大小与父控件的大小保持一致,这样才能形成滚动的效果。

      第三步、将子控件从上到下依次排列开来,代码如下所示:

    @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int count = getChildCount();
            MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
            layoutParams.height = mScreenHeight * count;
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() == View.VISIBLE) {
                    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();
                }
                int dy = y - mLastY;
                if (getScrollY()< 0) {
                    dy = 0;
                } else if (getScrollY()> getHeight() - mScreenHeight) {
                    dy = 0;
                }
                scrollBy(0, dy);
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                mEnd = getScrollY();
                int delta = mEnd - mStart;
                if (delta > 0) {
                    if (delta < mScreenHeight / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -delta);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, mScreenHeight
                                - delta);
                    }
                } else {
                    if (Math.abs(delta) < mScreenHeight / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -delta);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight
                                - delta);
                    }
                }
                break;
            default:
                break;
            }
    
            return true;
        }

      事后总结

      其实,在这个事件监听中就做了三件事件

      ①、根据手势按下、抬起的距离进行判断,判断手势到底是上滑还是下滑。

      ②、如果手势滑动的距离,小于小于相应的阈值(这里为屏幕高度的三分之一)以后,就滚回到原来的位置,否则自动滑入下一个子View。

      ③、在手指移动事件,使这个控件能够随着手势的滑动而自由的移动。但是,我们要做好相应临界值判断,判断其是否小于0或者大于屏幕高度,就不进行滑动。

      最终效果 

      这个控件最终运行的效果为:

     

       这就是,我对自定义viewGroup控件的一定总结。本人才疏学浅,恳请大家指教。

  • 相关阅读:
    npm安装一直报错Error: Cannot find module 'lru-cache'
    vue使用ref获取元素
    WinForm 简易仿360界面控件
    【解决方案】macOS 打开微信视频电话其他应用音量变小问题
    ES服务的搭建(八)
    ES安装
    缓存一致性问题(七)
    多级缓存架构(六)
    掌握SKU和SPU关系及表设计(三)
    架构的搭建(一)
  • 原文地址:https://www.cnblogs.com/manuosex/p/5307271.html
Copyright © 2011-2022 走看看