自定义View一般可以分为四类:
1、继承View重写onDraw()方法
为了实现一些不规则的效果,需要重写onDraw()方法
这种方式需要自己支持wrap_content和padding
2、继承ViewGroup派生特殊的Layout
需要自己处理ViewGroup的测量、布局的过程
3、继承特定的View(比如TextView)
不需要自己支持wrap_content和padding
4、继承特定的ViewGroup(比如LiearLayout)
不需要自己处理ViewGroup的测量、布局的过程
一般情况下第二种方式能实现的效果,第四种也是可以的
注意事项:
1、让View支持wrap_content,重写onMeasure()
protected void
onMeasure
(
int
widthMeasureSpec
, int
heightMeasureSpec) {
super
.onMeasure(widthMeasureSpec
,
heightMeasureSpec)
;
int
widthSpecMode = MeasureSpec.
getMode
(widthMeasureSpec)
;
int
widthSpecSize = MeasureSpec.
getSize
(widthMeasureSpec)
;
int
heightSpecMode = MeasureSpec.
getMode
(heightMeasureSpec)
;
int
heightSpecSize = MeasureSpec.
getSize
(heightMeasureSpec)
;
if
(widthSpecMode == MeasureSpec.
AT_MOST
&& heightSpecMode == MeasureSpec.
AT_MOST
) {
setMeasuredDimension(
200
,
200
)
;
}
else if
(widthSpecMode == MeasureSpec.
AT_MOST
) {
setMeasuredDimension(
200
,
heightSpecSize)
;
}
else if
(heightSpecMode == MeasureSpec.
AT_MOST
) {
setMeasuredDimension(widthSpecSize
,
200
)
;
}
}
其他情况仍交由super.onMeasure()处理,当wrap_content的时候(即SpecMode为AT_MOST)给相应的宽或高设置一个固定值,如本例中的200
2、如果有必要,让View支持padding(layout_margin由父容器控制)
在onDraw()方法中,通过getPadding()获取padding值,然后在设置图形宽高等属性时考虑padding的值就行了,如:
width = getWidth - paddingLeft - paddingRight;
3、没有必要在View中使用Handler,因为View中提供了一系列的post方法
4、当Veiw变得不可见时,需要及时停止线程和动画,否则有可能会造成内存泄露
一般在onDetachedFromWindow()中进行处理
5、View带有滑动嵌套时,需要处理好滑动冲突
方法一(外部拦截法):重写onInterceptTouchEvent()
// 分别记录上次滑动的坐标(onInterceptTouchEvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercepted = false; int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: intercepted = false; break; case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; if (父容器需要当前点击事件) { //如:Math.abs(deltaX) > Math.abs(deltaY) intercepted = true; } else { intercepted = false; } break; } case MotionEvent.ACTION_UP: { intercepted = false; break; } } mLastXIntercept = x; mLastYIntercept = y; return intercepted; }
方法二(内部拦截法):对dispatchTouchEvent()进行重写,并且配合getParent().requestDisallowInterceptTouchEvent(true)
// 分别记录上次滑动的坐标(onInterceptTouchEvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { getParent().requestDisallowInterceptTouchEvent(true); break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; if (父容器需要当前点击事件) { //如:Math.abs(deltaX) > Math.abs(deltaY) getParent().requestDisallowInterceptTouchEvent(false); } break; } case MotionEvent.ACTION_UP: { getParent().requestDisallowInterceptTouchEvent(false); break; } } mLastXIntercept = x; mLastYIntercept = y; return super.dispatchTouchEvent(ev); }
转载请注明原文地址: https://ju.6miu.com/read-1125307.html