Android自定义ViewGroup
来源:互联网 发布:中国临床试验数据库 编辑:程序博客网 时间:2024/06/05 01:52
Android自定义ViewGroup
以Dome为例,来一步一步的讲解如何自定义ViewGoup,先看下面Demo,此Demo模仿ViewPage的滑动。
从demo中可以看出,红、蓝、黄分别装载在ViewGroup中,随着手势的滑动来显示相应的View。那么ViewGroup是什么呢?
从上图我们大概可以猜到ViewGroup是什么了,此刻我在想你们是否跟我想的一样,好我们来去看看官方的解释,看是否跟大家猜的一样:
“A ViewGroup is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers. ”
翻译过来大概意思是:ViewGroup是一种特殊的视图可以包含其他视图(称为孩子)的视图组基类的布局和视图的容器。
那ViewGroup怎么实现呢?这个简单,按官方文档上面的例子,实践并总结出除了构造方法之外onMeasure()、onLayout(),这两方法是必须实现的,不然你的View就无法显示在屏幕上面。看以下示例:
import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.LinearLayout;/** * Created by win7 on 2017/7/4. */public class BaseViewGroup extends ViewGroup {private final String TAG = "BaseViewGroup";private AView centerView;private BView rigthView;private CView leftView;private VelocityTracker mVelocityTracker; //速率捕捉器private final int SCREEN_LEFT = 1; // 左屏private final int SCREEN_CENTER = 2; //中屏private final int SCREEN_RIGHT = 3;//右屏private final int mUnits = 1000;//手势滑动速度private final int mDuration = -1;private int mTouchSlop; //滑动阀值private int mMaximumVelocity; //最大移动速度private int mActivePointerId;//活动触摸点private int mCurrentScreen = 2; //当前所处屏private int mScreenWidth; //屏幕宽度private float mLastMotionX; //上一次移动点的位置private boolean mPointInViews; //触摸点是否在指定范围内private boolean mScrolled; //是否滑动 public BaseViewGroup(Context context) { this(context, null); } public BaseViewGroup(Context context, AttributeSet attrs) { this(context, attrs, -1); } public BaseViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { //获取屏幕宽度 DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager windowManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); windowManager.getDefaultDisplay().getMetrics(displayMetrics); mScreenWidth = displayMetrics.widthPixels; LinearLayout.LayoutParams aLayoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); //中屏 centerView = new AView(context); this.addView(centerView, aLayoutParams); //右屏 rigthView = new BView(context); this.addView(rigthView, layoutParams); //左屏 leftView = new CView(context); this.addView(leftView, layoutParams); //获取标准的常量用来设置UI的超时、大小和距离 ViewConfiguration configuration = ViewConfiguration.get(context); //获取用户认为滚动之前,触摸可以移动像素的距离。 mTouchSlop = configuration.getScaledTouchSlop(); //获取滑动的最大速度, 以像素/每秒来进行计算 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();}/** *要求所有子View测量自己并计算这一点的测量值 *基于View的布局. */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //测量原始的大小 int defaultWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); int defaultHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); //测量子View的大小 final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); } //保存测量结果 setMeasuredDimension(defaultWidth, defaultHeight);}/** *在布局中放置所有的子节点 */ @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { int width = this.getWidth(); int height = this.getHeight(); //对子View进行排版 centerView.layout(0, 0, width, height); rigthView.layout(0, 0, width, height); leftView.layout(0, 0, width, height); //改变left和right的坐标点位置,以达到左、中、右三屏效果 rigthView.scrollTo(-width, 0); leftView.scrollTo(width, 0); }@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev);}/** *判断触摸点是否在子View上 *@param ev *@return */public boolean pointInViews(MotionEvent ev) { boolean rigthView = false; float x = ev.getX(); float y = ev.getY(); if (mCurrentScreen == SCREEN_CENTER) { return centerView.pointInViews(x, y); } else if (mCurrentScreen == SCREEN_LEFT) { return leftView.pointInViews(x, y); } else if (mCurrentScreen == SCREEN_RIGHT) { return this.rigthView.pointInViews(x, y); } return rigthView;}/**事件拦截*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { mPointInViews = pointInViews(ev); if (!mPointInViews) { return false; } final float x = ev.getX(); final float y = ev.getY(); // 防止事件冲突 getParent().requestDisallowInterceptTouchEvent(true); boolean handle = super.onInterceptTouchEvent(ev); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastMotionX = x; //获取手势触控点 if (ev.getPointerCount() > 0) { mActivePointerId = ev.getPointerId(0); } break; case MotionEvent.ACTION_MOVE: //根据手势滑动距离来判断是否拦截此次手势事件 if (Math.abs(x - mLastMotionX) >= mTouchSlop) { handle = true; } break; case MotionEvent.ACTION_UP: break; } return handle;}@Overridepublic boolean onTouchEvent(MotionEvent event) { if (!mPointInViews) { return false; } if (null == mVelocityTracker) { //初始化速率捕捉器 mVelocityTracker = VelocityTracker.obtain(); } if (null != mVelocityTracker) { //删除手势 mVelocityTracker.addMovement(event); } switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: //记录手势按下的X坐标点 mLastMotionX = event.getX(); mScrolled = false; break; case MotionEvent.ACTION_MOVE: //是否有手势触控点 final int activePointerIndex = event.getPointerCount() > mActivePointerId ? event.findPointerIndex(mActivePointerId) : -1; if (activePointerIndex == -1) { break; } //获取手势触控点的X坐标 int x = (int) event.getX(activePointerIndex); //计算滑动过程 int move = (int) mLastMotionX - x; if (mScrolled) { mLastMotionX = x; onTouchMove(move); } else if (Math.abs(x - mLastMotionX) >= mTouchSlop) { mScrolled = true; onTouchMove(move); } break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: if (mActivePointerId < 0) { break; } final int activeUpPointerIndex = event.getPointerCount() > mActivePointerId ? event.findPointerIndex(mActivePointerId) : -1; if (activeUpPointerIndex == -1) { break; } if (event.getPointerId(event.getActionIndex()) != mActivePointerId) { break; } //计算当前手势的速度 mVelocityTracker.computeCurrentVelocity(mUnits, mMaximumVelocity); //获取当前手势X移动的速度 float mVelocityX = mVelocityTracker.getXVelocity(); mActivePointerId = -1; scrollToNextScreen(mVelocityX); break; case MotionEvent.ACTION_POINTER_DOWN: break; case MotionEvent.ACTION_CANCEL: releaseTracker(); } return true;}/** *释放速率捕捉器 */private void releaseTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; }}/** *根据X坐标位移 */private void onTouchMove(int move) { if (mCurrentScreen == SCREEN_LEFT) { leftView.scrollBy(move, 0); centerView.scrollBy(move, 0); } else if (mCurrentScreen == SCREEN_CENTER) { centerView.scrollBy(move, 0); leftView.scrollBy(move, 0); rigthView.scrollBy(move, 0); } else if (mCurrentScreen == SCREEN_RIGHT) { rigthView.scrollBy(move, 0); centerView.scrollBy(move, 0); }}/** *根据手势的速度来判断滑动的方向 *@param mVelocityX 当前手势X移动的速度 */private void scrollToNextScreen(float mVelocityX) { boolean isScrollToNextScreen = false; if (mVelocityX > mUnits) { isScrollToNextScreen = false; } else if (mVelocityX < -mUnits) { isScrollToNextScreen = true; } else { if (leftView.isHalfDisplaying()) { // left snapTo(SCREEN_LEFT, mScreenWidth); } else if (rigthView.isHalfDisplaying()) { // right snapTo(SCREEN_RIGHT, mScreenWidth); } else { snapTo(SCREEN_CENTER, mScreenWidth); } return; } int nextScreen; if (leftView.isScrolling() || leftView.isAllDisplaying()) { nextScreen = SCREEN_LEFT; } else if (rigthView.isAllDisplaying()) { nextScreen = SCREEN_RIGHT; } else { nextScreen = SCREEN_CENTER; } if (isScrollToNextScreen) { nextScreen++; } else { if (nextScreen != SCREEN_LEFT && mCurrentScreen != SCREEN_RIGHT) { nextScreen--; } } if (nextScreen > SCREEN_RIGHT) { snapTo(nextScreen, mScreenWidth); } else if (nextScreen == SCREEN_RIGHT) { snapTo(nextScreen, mScreenWidth); } else if (nextScreen <= SCREEN_RIGHT && nextScreen >= SCREEN_LEFT) { snapTo(nextScreen, mScreenWidth); }} /** *滑动到某一屏 *@param toCurrent *@param x */private void snapTo(int toCurrent, int x) { if (SCREEN_LEFT == toCurrent) { leftView.snapTo(0, mDuration); centerView.snapTo(-x, mDuration); rigthView.snapTo(-x, 0); mCurrentScreen = 1; } else if (SCREEN_CENTER == toCurrent) { centerView.snapTo(0, mDuration); rigthView.snapTo(-x, mDuration); leftView.snapTo(x, mDuration); mCurrentScreen = SCREEN_CENTER; } else if (SCREEN_RIGHT == toCurrent) { rigthView.snapTo(0, mDuration); centerView.snapTo(x, mDuration); leftView.snapTo(x, mDuration); mCurrentScreen = SCREEN_RIGHT; }}
}
onMeasure()、onLayout()这个两个重要的函数,我会在后面的ViewGroup、View之onMeasure、onLayout解刨中详细讲解。
onInterceptTouchEvent 我会在后面的View和ViewGroup事件分发机制中讲解
到此,对Viewgroup的讲解就暂告一段落,建议大家动手实践,附上Dmoe源码
阅读全文
0 0
- 【Android】Android自定义ViewGroup
- android之自定义ViewGroup
- Android中自定义ViewGroup
- android自定义View(viewGroup)
- Android中自定义ViewGroup
- Android 自定义ViewGroup
- Android 自定义ViewGroup
- Android 自定义viewgroup
- Android 自定义ViewGroup
- android--自定义ViewGroup
- Android中自定义ViewGroup
- Android 自定义ViewGroup (一)
- Android 自定义ViewGroup (二)
- Android中自定义ViewGroup
- Android自定义ViewGroup详解
- Android-自定义ViewGroup
- Android 自定义ViewGroup
- Android自定义ViewGroup
- springMVC from:checkboxes
- 一个成功的网站是这样炼成的
- django中的路由规则
- 指针和const
- 位运算相关题目
- Android自定义ViewGroup
- 32. Longest Valid Parentheses
- Java反射
- react-navigation设置navigationOptions中Static中使用this的方法
- 发现一个Fragment没有走FragmentManager的registerFragmentLifecycleCallbacks的情况
- 闵维方、魏新反对王选,罪责难逃!
- Linux文件系统管理、挂载光盘、U盘、移动硬盘,以及手工分区硬盘
- mac 安装opencv/python-opencv
- 动态再次获得权限