博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义View笔记
阅读量:6530 次
发布时间:2019-06-24

本文共 3928 字,大约阅读时间需要 13 分钟。

自定义View

view的事件体系

  1. 基础
    1. view的位置由top, left, right, bottom决定,这些坐标都是相对view的父容器来说的,
      • 在View平移的过程中,top, left代表的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x, y, translationX, translationY
    2. MotionEvent和TouchSlop
      • 通过MotionEvent对象我们可以获得点击事件发生的x和y坐标,getX/getY, 和getRawX/getRawY,其中getX/getY返回的是相对于当前view左上角的x和y坐标,而getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标
    3. VelocityTracker,GestureDetector和Scroller
      • 使用GestureDetector的情景:如果只是监听滑动相关,自己在onTouchEvent中实现即可,如果要监听双击这种行为,才使用GestureDetector
  2. view的滑动
    1. 使用scrollTo/scrollBy
      • scrollTo实现了当前位置的绝对滑动,scrollBy实现了当前位置的相对滑动
      • scrollTo scrollBy 只能改变View内容的位置而不能改变View在布局中的位置
      • 关于mScrollXmScrollY, 位置改变需注意
    2. 通过动画给View施加平移效果
      • 主要操作View的translationXtranslationY属性
      • View动画是对View的影像做操作,并不能真正改变View的位置参数,包括宽高,如果希望动画后状态得以保留还必须将fillAfter设置为true
    3. 使用属性动画
    4. 荣国改变View的LayoutParams来使得View重新布局
  3. View的弹性滑动
    1. 使用scrollTo
      1. 使用Scroller
      2. 通过动画onAnimationUpdate方法:
        1. 模仿Scroller,通过改变百分比配合scrollTo方法来完成View的滑动,注意只能是View的内容而非View本身
    2. 使用延时策略
      1. 使用Handler#postDelayed
      2. 使用Thread#sleep

View的事件分发机制

  1. 点击事件的传递规则:

    • public boolean dispatchTouchEvent(MotionEvent ev) {    boolean consume = false;    if (onInterceptTouchEvent(ev)) {        consume = onToucnEvent(ev);    } else {        consume = child.dispatchTouchEvent(ev);    }    return consume;}复制代码
    • 当一个view需要处理事件时:

      • 优先级:OnTouchListener>onTouchEvent>OnclickListener
      • 传递顺序:Activity->Window->View
      • 如果底层View的onTouchEvent返回false,则父容器的onTouchEvent将会被调用,如果所有元素都不处理这个事件,那么Activity的onTouchEvent将会被调用

View的滑动冲突

view的工作流程

  1. ViewRoot和DecorView的概念

    1. View的三大流程均是通过ViewRoot来完成的。View的绘制流程从ViewRoot的performTraversals开始,依次调用performMeasure, performLayout, performDraw三个方法
  2. measure过程

    1. MeasureSpec

      1. MeasureSpc代表32位int值,高2位代表SpecMode,低30位代表SpecSize
      2. SpecMode类别:EXACTLY对应match_parent,AT_MOST对应wrap_content
      3. 对于普通的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定
    2. onMeasure

      1. setMeasuredDimension会设置view的宽高

      2. 直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent

        //只需要给View指定一个默认的内部宽高(mWidth, mHeight)protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);    if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(mWidth, mHeight);    } else if (widthSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(mWidth, heightSpecSize);    } else if (heightSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(widthSpecSize, mHeight);    }}复制代码
      3. measure完成后,通过getMeasuredWidth/Height方法就可以获得View的测量宽高,比较好的习惯是在onLayout方法中获取View的测量宽高或最终宽高

      4. 在activity中获得View的宽高

        • Activity/View#onWindowFocusChanged
        • view.post(runnable)
        • ViewTreeObserver
        • view.measure
  3. layout过程

  4. draw过程

    1. 步骤
      • 绘制背景background.draw(canvas)
      • 绘制自己onDraw
      • 绘制children(dispatchDraw)
      • 绘制装饰onDrawScrollBars
    2. 如果我们自定义控件继承ViewGroup并且本身并需要通过onDraw来绘制内容时,我们需要显式地关闭WILL_NOT_DRAW这个标记位

自定义View

  1. 分类:

    • 继承View重写onDraw方法,主要用于实现一些不规则效果
    • 继承ViewGroup派生特殊的Layout,主要用于实现自定义布局
    • 继承特定的View(比如TextView)
    • 继承特定的ViewGroup(比如LinearLayout)
  2. 需要实现:

    • 支持wrap_content

    • 支持padding:只要在onDraw方法中考虑一下padding即可,

    • 提供自定义属性:

      • 在values目录下创建自定义属性的XML,比如attrs.xml,

        复制代码

        格式有reference(是指资源id), dimension(指尺寸),string,integer,boolean

      • 在View的构造方法中解析自定义属性的值并做相应的处理

        public CirecleView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView);    mColor = a.getColor(styleable.CircleView_circle_color, Color.RED);    a.recycle();    init();}复制代码
      • 在布局文件中使用自定义属性

        注意:必须在布局文件中添加schemas声明:

        xmlns:app=http://schemas.android.com/apk/res-auto复制代码
    • 不要在View中使用Handler

    • View中如果有线程或者动画,需要在onDetachedFromWindow方法中及时停止,防止内存泄漏

    参考:《Android开发艺术探索》

转载于:https://juejin.im/post/5d0851bf6fb9a07ec07fc131

你可能感兴趣的文章
5-进一步研究使用libsvm的笔记
查看>>
android 成长日记 6.ListView详解
查看>>
String的intern()方法
查看>>
volatile变量的第二个语义是禁止指令重 排序优化
查看>>
老程序员学C# ------操作DataGridView控件详解
查看>>
安卓开源项目周报0301
查看>>
Citrix XenServer 申请免费 License
查看>>
Bash定制化
查看>>
旧博客文章索引
查看>>
记一下angular.js 1.x 关于post提交后台无法接受数据解决方案
查看>>
Silverlight4 OOB 自动更新
查看>>
CI Weekly #13 | 用更 Geek 的方式配置你的 CI 工作流
查看>>
追MM与设计模式(23种设计模式巧妙解析,趣味理解)
查看>>
我的友情链接
查看>>
第五章 shell函数的定义、执行、传参和递归函数
查看>>
gitinspector+jenkins 开发代码统计CI
查看>>
async 和 await 关键字
查看>>
写给女儿的话---小荷作文"万米写书"序言
查看>>
打开“话匣子”,让学生畅所欲言
查看>>
ubuntu gedit编辑器乱码问题
查看>>