自定义view注意事项

237 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

自定义ViewGroup

generateLayoutParams(AttributeSet)
注意此方法存在重载方法generateLayoutParams(params)(这个是用于在addView函数中,当

checkLayoutParams(params)返回false时会通过generateLayoutParams(params)重新生成新的params),还有一个generateDefaultParams(),当 generateLayoutParams(AttributeSet)生成的params为null时,会通过这个函数生成一个默认的params,作用不同,需要区分。

当viewGroup自定义view,需要对直接子view的LayoutParams做处理并生成新的LayoutParams可以覆盖此方法

原因:inflate时,在addView()之前会通过该viewGroup的generateLayoutParams(AttributeSet)生成该子view的LayoutParams并作为参数传递给addView,像FrameLayout都覆盖了此方法。

出错处:继承ViewGroup时,没有覆盖此方法就在measure中调用measureChildwWithMargins导致报错,因为measureChildwWithMargins会把子view的Layoutparams强转为Layoutparams的子类MarginLayoutParams,但是由于generateLayoutParams(params)默认返回LayoutLarams,导致强转失败。

解决办法就是使generateLayoutParams(params)返回MarginLayoutParams类型。

只能操作直接子view的layoutParams,孙子view不可以

onMeasure

如果在onmeasure不对子view的margin做处理,也就是不调用measureChildwWithMargins而调用measureChildren,会导致子view设置margin失效。

ViewGroup处理子view滑动时,可以使用ViewDragHelper。
使用方法:ViewDragHelper.create()创建ViewDragHelper并添加回调函数
         在回调函数中可以处理事件的响应包括滑动的距离
         在onTouchEvent中通过ViewDragHelper.processTouchEvent交由ViewDragHelper处理
         如果滑动后会在回调函数中调用onViewPositionChanged可以在这个函数里面继续滑动或者做其他事情
         onInterCeptTouchEvent可以交由ViewDragHelper.shouldInterceptTouchEvent处理

自定义View

在处理Move事件时,如果只是简单的将getX,getY处理滑动距离,会导致响应Move事件的view移动时产生抖动。

解决办法:
1.将getX,getY换成getRawX,getRawY(返回屏幕绝对坐标);
2.有可能是Move事件中没有记录当前滑动到的地方,
比如:
switch (ev.getActionMasked())
{
    case MotionEvent.ACTION_DOWN:{
        lastx = (int)ev.getX();
    }
    case MotionEvent.ACTION_MOVE:
    {
        int dx = (int)ev.getX()-lastx;
        ViewCompat.offsetLeftAndRight(getChildAt(0),dx);
        //这里需要更新当前移动到的位置,方便下次Move事件到来时,从新位置开始计算
        lastx += dx;
    }
}
同时,移动View尽量选择ViewCompat.offsetLeftAndRight和ViewCompat.offsetTopAndBottom
虽然View.setTranslationY()和View.setTranslationY()也可以移动,但这个函数没有改变viewleft、top、right的值,对view做动画时可以选择这个函数,如果只是单纯的移动view则使用ViewCompat.offsetLeftAndRight和ViewCompat.offsetTopAndBottom
不要使用setLayoutParams的方式改变view的方式,这种性能比ViewCompat.offsetTopAndBottom低,因为它会导致重绘的发生。

 invalidate()会导致draw()的发生,但是不一定会导致onDraw(),因为内部有判断是否发生onDraw(),但是类似ViewCompat.offsetTopAndBottom不会导致draw()的发生

computeScroll() 每次绘制时(执行draw())都会调用这个函数。

谷歌这样解释这个函数的:在必要时更新其 mScrollX 和 mScrollY 的值。如果正在使用Scroller对象对滚动进行动画处理,通常会这样做。