Android进阶-纯粹自定义控件二

原创 2015年11月19日 20:36:42

Android进阶-纯粹自定义控件二

本文来看一下自定义ViewGroup需要注意哪些。
以自定义的一个侧滑菜单为例。
图例:
这里写图片描述


  • 关键点

  • 既然是自定义的ViewGroup, 那么的话,控件的具体内容肯定不是要考虑的事情
  • 这个ViewGroup应考虑的是
    • 我们这个ViewGroup有何特点? -> 子View的行为
    • 如何完成自己的 onMeasure(), onLayout()方法,从而控制子View
    • 如何去实现子View的行为

侧滑菜单

  • 这个ViewGroup所具有的特点为:
    • 拥有两个子View,正常状态下,一个可见,一个不可见
    • 可以通过滑动,来控制子View的显示
    • 可以给外界暴露一个借口,来控制隐藏的View显示

实现关键点

  • 如何得到得到子View的引用,从而实现对子View的控制

    • 首先,在构造方法中是得不到的,构造知识把自己给弄出来了
    • 可以覆写protected void onFinishInflate()
      • 这个方法会在所有的子View被inflate之后调用
      • 因此可以通过ViewGroup.getChildAt(position)来得到子View的引用
  • onMeasure方法

    • protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    • widthMeasureSpec和heightMeasureSpec在前面已经说过,这是View的父View传递给他的测量值
    • 我们可以利用这两个参数,来对子View执行测量
    • 不过,在自定义ViewGroup方法时,一般重写这个方法,因为这个方法主要就是用来测量出(依据父View和布局文件中的参数)子View的宽高
    • 我们可以借用其他的ViewGroup的方法, 因此我们可以通过继承来实现(例如继承FrameLayout)
    • 来看一下系统是如何对View进行测量的(我们也可以这么干)
      • 系统测量依赖 MeasureSpec 的 public static int makeMeasureSpec (int size, int mode)方法
      • size代表在布局文件中的宽高的值, mode代表解析这个宽高的模式
      • mode可取: UNSPECIFIED(wrap_content) EXACTLY(精确规定了大小,例如多少dp) AT_MOST(比如math_parent)
      • 例如: int measureSpecWidth = MeasureSpec.makeMeasureSpec(childViewWidth, MeasureSpec.EXACTLY);
  • 如何使一个View隐藏,一个View展示

    • 这肯定是关于子View如何布局,即我们自定义的这个ViewGroup在最初对子View进行布局时, 一个充满屏幕(父View),一个在屏幕外面
    • 上面的实现应在onLayout()方法中

可以这样实现:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        menuView.layout(-menuWidth, 0, 0, menuView.getMeasuredHeight());  //在外面
        mainView.layout(0, 0, r, b); //充满父View
    }
  • 处理用户滑动,两个View显示的问题

    • 当用户向右滑动是,肯定是要是menuView显示出来

      • 有3中方法可以做到这一点(控制子View的移动显示)
        • layout(l, t, r, b) 很好理解,再布一次局就是了
        • offsetTopAndBottom()和offsetLeftAndRight()
        • scrollTo()
          • 将View的边框向(x, y)滑动, 会引起重绘, 这个x, y并没什么限制, 就向在一张大纸上滑动
          • getScrollX (), 得到已经滑动的距离
    • 使用scrollTo()方法来完成子View的滑动效果

      • 我们滑动的是ViewGroup的边框(屏幕的边框)
      • 由相对论,我们可以得出,我们向右滑动,右边的内容就显示出来了,想左滑动,左边的内容就显示出来了
      • 反正就是,你想把左边的隐藏的menuView给滑动出来, 调用scrollTo()时就得向左滑!(得给负值)
    • 即我们在触摸滑动中可以这样处理
      如下

      //向右滑slideLength 是正的, 向左滑slideLength是负的
      int newScrollX = getScrollX() - slideLength;      //上次滑动的距离 - 这次滑动的距离
      if(newScrollX<-menuWidth)
          newScrollX = -menuWidth;     //最大滑动距离, 不能超出menuView的宽
      if(newScrollX>0)newScrollX = 0;  //     不能向左滑
      scrollTo(newScrollX, 0);      
      
  • 暴露出给用户直接显示menuView和关闭menuView的接口

    • 其实这两个接口,就是能够使用户通过调用这两个接口来完成menuView的显示
    • 直接实现的话很简单, 就向上面的代码, 滑出来就是
    • 但是, 一次滑动到指定位置,太快,用户体验不好
    • 因此我们需要,让menuView在一定的时间内滑动出来哦
    • 实现这个问题,共有两种办法

1)使用自定义动画,

/**
 * 这个动画让指定view在一段时间内scrollTo到指定位置
 */
    public class ScrollAnimation extends Animation{

        private View view;
        private int targetScrollX;
        private int startScrollX;
        private int totalValue;

        public ScrollAnimation(View view, int targetScrollX) {
            super();
            this.view = view;
            this.targetScrollX = targetScrollX;

            startScrollX = view.getScrollX();
            totalValue = this.targetScrollX - startScrollX;

            int time = Math.abs(totalValue);  
            setDuration(time);      //实现,依据滑动的距离,来决定滑动动画时间的长与短
        }
    /**
     * 在指定的时间内一直执行该方法,直到动画结束
     * interpolatedTime:0-1  标识动画执行的进度或者百分比
     * 当前的值 = 起始值 + 总的差值*interpolatedTime
     */
        @Override
        protected void applyTransformation(float interpolatedTime,
                Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            int currentScrollX = (int) (startScrollX + totalValue*interpolatedTime);
            view.scrollTo(currentScrollX, 0);
        }
    }

2)利用Scroller, 它可以模拟一个移动的过程,但并不会是View真正移动

    private void closeMenu(){
        scroller.startScroll(getScrollX(), 0, 0-getScrollX(), 0, 400);
        invalidate();
    }

    private void openMenu(){
        scroller.startScroll(getScrollX(), 0, -menuWidth-getScrollX(), 0, 400);
        invalidate();
    }
/**
 * Scroller不主动去调用这个方法
 * 而invalidate()可以掉这个方法
 * invalidate->draw->computeScroll
 */
    @Override
    public void computeScroll() {  //这个方法
        super.computeScroll();
        if(scroller.computeScrollOffset()){//返回true,表示动画没结束, 当计算的滑动的值到我们模拟滑动的地方就停止
            scrollTo(scroller.getCurrX(), 0);
            invalidate();   
        }
    }
版权声明:本文为博主原创文章,未经博主允许不得转载。

Android进阶——自定义View之继承系统控件实现自带删除按钮动画效果和软键盘自动悬浮于文本框下方

继承系统现有控件扩展实现类似IOS风格的EditText,可以设置抖动动画和自带删除小图标的UI效果,*当输入时自动添加上删除按钮和当输入为空的时候点击按钮触发非空的抖动动画,其他的和普通的EdiTe...

Android进阶——自定义View之扩展系统控件的另一种思路实现渐变文字动画的TextView

重写系统控件TextView的成员方法扩展新的实现带边框和渐变动画的自定义TextView...

Android自定义控件进阶

上回我们已经实现了一个简单的自定义控件(不知道的朋友戳这里) 只是这里的控件还不能给它添加点击事件,也不能给他人方便的调用 接下来就给大家介绍一下解决的办法:接口回调 1.首先需要在我们的Topb...
  • hssg380
  • hssg380
  • 2015年03月24日 18:51
  • 274

android UI进阶之自定义组合控件

搬家后的博客链接: IT客栈 www.itkezhan.org 好久没写博客了。实在是忙不过来,不过再不总结总结真的不行了。慢慢来吧,有好多需要去总结的,博客里还是记录ui方面的...

Android高手进阶教程(二十七)之---基于ViewFlipper实现的自定义新手指引控件.

第一步:新建Android工程ViewFlipperDemo:第二步:新建AdverView.java代码如下:package com.tutor.viewflipper; import andr...

Android进阶自定义控件之滑动开关

自定义开关控件 Android自定义控件一般有三种方式 1、继承Android固有的控件,在Android原生控件的基础上,进行添加功能和逻辑。 2、继承ViewGroup,这类自定义控件是可以...

Android进阶之自定义控件一

Android进阶之自定义控件 自定义控件是判断Android工程师是否是高级工程师的一项基础指标,如果你想拿到更高的薪水,那就必须的完全掌握这项技能。如何判断自己是否完全掌握自定义控件?其实很简单...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android进阶-纯粹自定义控件二
举报原因:
原因补充:

(最多只允许输入30个字)