android自定义粘性控件,综合使用measure,layout,onTouchEvent,onInterceptTouchEvent等方法
来源:互联网 发布:jquery数组长度 编辑:程序博客网 时间:2024/05/01 10:07
粘性控件
- 产生粘性控件很简单,我们只需要进行相关的measure和layout并进行事件拦截,和事件处理即可
- 显示measure方法,measure方法比较复杂,一般我们会根据父容器的测量算子和自身的layoutParams共同决定,这样为了方便起见,全都是让父容器的测量算子决定的
测量代码
`@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub // super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); int height = MeasureSpec.getSize(heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); }}`
- 测量的时候先确定自身的大小,之后再确定view的大小(当然也可以先确定view的大小,之后再确定自己的大小,适用于包裹内容的)
- setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec)); 这样代码是确定自身的大小。我们直接使用父容器传递过来的参数进行相关高度和宽度的确定
`private int measureWidth(int widthMeasureSpec) {int mode = MeasureSpec.getMode(widthMeasureSpec);int size = MeasureSpec.getSize(widthMeasureSpec);int result = 0;if (mode == MeasureSpec.EXACTLY) { result = size;} else { result = 400; Context ctx = getContext(); if (ctx instanceof Activity) { Activity a = (Activity) ctx; result = a.getWindowManager().getDefaultDisplay().getWidth(); } if (mode == MeasureSpec.AT_MOST) { result = Math.min(result, size); Log.e("", size + ""); }}return result;}`
- 上面代码是通过测量算子的mode和size,共同确定width
高度也类似
` private int measureHeight(int heightMeasureSpec) {int mode = MeasureSpec.getMode(heightMeasureSpec);int size = MeasureSpec.getSize(heightMeasureSpec);int result = 0;if (mode == MeasureSpec.EXACTLY) { result = size;} else { result = 400; int count = getChildCount(); if (count > 0) { View view = getChildAt(0); view.measure(0, MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.EXACTLY)); result = view.getMeasuredHeight(); } if (mode == MeasureSpec.AT_MOST) { // 当为warp_content的时候 给一个最小的默认值 result = Math.min(result, size); }}return result;}`
- 为什么我们需要确定我们自身的宽度和高度呢?是因为,如果我们不修改测量算子的话,那么测量的模式是MEASURE_ATMOST,size是父容器给我们传递的宽度和高度,因此,我们想手动的修改的话,就需要修改测量算子值(mode和size)
下面代码是测量孩子的
`@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // TODO Auto-generated method stub // super.measureChild(child, parentWidthMeasureSpec, // parentHeightMeasureSpec); int height = MeasureSpec.getSize(parentHeightMeasureSpec); child.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));}`
- 孩子的测量算子宽度使用的是matchParent和EXACTLY。高度我们使用的是父容器的高度,也就是我们粘性控件的高度,mode也是EXACTLY
自身的高度和孩子的高度都测量完了,剩下的我们就是layout了
`protected void onLayout(boolean changed, int l, int t, int r, int b) {// zhihou zaizheli xunhuan diaoyong childde layoutint count = getChildCount();for (int i = 0; i < count; i++) { View child = getChildAt(i); child.layout(0, i * child.getMeasuredHeight(), getMeasuredWidth(), (i + 1) * child.getMeasuredHeight());}totalHeight = (getChildCount() - 1) * getMeasuredHeight();}`
- 代码很简单,就是遍历孩子,调用layout方法
接下来是事件拦截
`@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_MOVE) { y = (int) ev.getRawY(); return true; } return super.onInterceptTouchEvent(ev);}`
- 这里拦截了所有的move事件
我们在ontouchEvent中处理
`public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: y = (int) event.getRawY(); break;case MotionEvent.ACTION_MOVE: int min = (int) (event.getRawY() - y); scrollTo(0, Math.min(totalHeight, Math.max(0, getScrollY() - min))); y = (int) event.getRawY(); break;case MotionEvent.ACTION_UP: scroller.startScroll(0, getScrollY(), 0, getPosition() * getMeasuredHeight() - getScrollY(), Math .abs(getPosition() * getMeasuredHeight() - getScrollY())); postInvalidate(); break;}return true;}
`
我们使用的是Scroller完成的滚动,因此需要重写computeScroll
` @Overridepublic void computeScroll() { // TODO Auto-generated method stub super.computeScroll(); if (scroller.computeScrollOffset()) { scrollTo(0, scroller.getCurrY()); postInvalidate(); }}`
接下来,我们计算我们当前显示的是第几个view
`private int getPosition() { return (int) (getScrollY() / (getMeasuredHeight() + 0.0f) + 0.5f);}`
在布局加载完的时候给我们的控件添加点击监听
`@Overrideprotected void onFinishInflate() { // TODO Auto-generated method stub super.onFinishInflate(); int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).setOnClickListener(this); }}`
对外面提供我们自己定义的监听接口
`public interface OnClickItemListener { public void OnClickItem(View view);}public void setOnClickItemListener(OnClickItemListener listener) { this.listener = listener;}`
在onClick的时候做出相应
` @Overridepublic void onClick(View v) { int position = getPosition(); View child = getChildAt(position); if (listener != null) { listener.OnClickItem(child); }}
`
源码如下
`package com.example.viewgroup1;import android.app.Activity;import android.content.Context;import android.support.v4.view.ViewConfigurationCompat;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.Scroller;import android.view.View.OnClickListener;public class MyViewGroup extends ViewGroup implements OnClickListener {private int y;private int totalHeight;private Scroller scroller;private OnClickItemListener listener;public MyViewGroup(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); scroller = new Scroller(getContext());}public MyViewGroup(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub}public MyViewGroup(Context context) { this(context, null); // TODO Auto-generated constructor stub}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub // super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); int height = MeasureSpec.getSize(heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); }}private int measureHeight(int heightMeasureSpec) { int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); int result = 0; if (mode == MeasureSpec.EXACTLY) { result = size; } else { result = 400; int count = getChildCount(); if (count > 0) { View view = getChildAt(0); view.measure(0, MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.EXACTLY)); result = view.getMeasuredHeight(); } if (mode == MeasureSpec.AT_MOST) { // 当为warp_content的时候 给一个最小的默认值 result = Math.min(result, size); } } return result;}private int measureWidth(int widthMeasureSpec) { int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); int result = 0; if (mode == MeasureSpec.EXACTLY) { result = size; } else { result = 400; Context ctx = getContext(); if (ctx instanceof Activity) { Activity a = (Activity) ctx; result = a.getWindowManager().getDefaultDisplay().getWidth(); } if (mode == MeasureSpec.AT_MOST) { result = Math.min(result, size); Log.e("", size + ""); } } return result;}@Overrideprotected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // TODO Auto-generated method stub // super.measureChild(child, parentWidthMeasureSpec, // parentHeightMeasureSpec); int height = MeasureSpec.getSize(parentHeightMeasureSpec); child.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { // zhihou zaizheli xunhuan diaoyong childde layout int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.layout(0, i * child.getMeasuredHeight(), getMeasuredWidth(), (i + 1) * child.getMeasuredHeight()); } totalHeight = (getChildCount() - 1) * getMeasuredHeight();}@Overridepublic boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: y = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: int min = (int) (event.getRawY() - y); scrollTo(0, Math.min(totalHeight, Math.max(0, getScrollY() - min))); y = (int) event.getRawY(); break; case MotionEvent.ACTION_UP: scroller.startScroll(0, getScrollY(), 0, getPosition() * getMeasuredHeight() - getScrollY(), Math .abs(getPosition() * getMeasuredHeight() - getScrollY())); postInvalidate(); break; } return true;}private int getPosition() { return (int) (getScrollY() / (getMeasuredHeight() + 0.0f) + 0.5f);}@Overridepublic void computeScroll() { // TODO Auto-generated method stub super.computeScroll(); if (scroller.computeScrollOffset()) { scrollTo(0, scroller.getCurrY()); postInvalidate(); }}// 点击item的监听public interface OnClickItemListener { public void OnClickItem(View view);}public void setOnClickItemListener(OnClickItemListener listener) { this.listener = listener;}@Overrideprotected void onFinishInflate() { // TODO Auto-generated method stub super.onFinishInflate(); int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).setOnClickListener(this); }}@Overridepublic void onClick(View v) { int position = getPosition(); View child = getChildAt(position); if (listener != null) { listener.OnClickItem(child); }}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_MOVE) { y = (int) ev.getRawY(); return true; } return super.onInterceptTouchEvent(ev);}}
`
- 源码下载 http://download.csdn.net/detail/u013356254/9476805
总结,写自定义view的时候比较复杂的是测量和事件冲突
0 0
- android自定义粘性控件,综合使用measure,layout,onTouchEvent,onInterceptTouchEvent等方法
- 自定义控件中onInterceptTouchEvent()和onTouchEvent()用法
- ------------------Android中对GridView, ListView等滚动控件的Touch事件onInterceptTouchEvent,onTouchEvent了解
- Android中对GridView, ListView等滚动控件的Touch事件onInterceptTouchEvent,onTouchEvent了解
- Android中对GridView, ListView等滚动控件的Touch事件onInterceptTouchEvent,onTouchEvent理解
- android onInterceptTouchEvent()和onTouchEvent
- Android onInterceptTouchEvent setOnChangedListener onTouchEvent
- android onInterceptTouchEvent onTouchEvent
- android onInterceptTouchEvent和onTouchEvent
- Android onInterceptTouchEvent()和onTouchEvent()
- Android-onInterceptTouchEvent()和onTouchEvent()
- 【Android】【触摸】onInterceptTouchEvent(),onTouchEvent()
- 【android】onInterceptTouchEvent与onTouchEvent
- Android View measure (二) 自定义UI控件measure相关
- 探讨Android ViewGroup(Layout)和View中onInterceptTouchEvent和onTouchEvent调用关系详解
- android onTouchEvent和onInterceptTouchEvent区别
- Android-onInterceptTouchEvent()和onTouchEvent()总结
- Android中onInterceptTouchEvent与onTouchEvent
- Migrate Win32 C/C++ applications to Linux on POWER, Part 3: Semaphores
- Migrate Win32 C/C++ application to Linux on POWER, Part 2: Mutexes
- linux 线程创建函数pthread_create的几个传参方式
- 三种方式求最大公约数
- Migrating Win32 C/C++ applications to Linux on POWER, Part 1: Process, thread
- android自定义粘性控件,综合使用measure,layout,onTouchEvent,onInterceptTouchEvent等方法
- JavaSe基础(11)-- static
- Android之多线程断点下载
- select、poll、epoll之间的区别总结
- Install Skype 4.3 on Fedora 21-20-CentOS-RHEL-SL7-6.6
- 《倡议书——节约用电,从我做起》
- 解决SQL server2008 error40-无法连接到服务器问题
- 设计模式记录学习---后续
- 中国计算机学会推荐国际学术会议和期刊目录