【Android】Android自定义ViewGroup
2016-02-27 16:43
393 查看
ViewGroup存在的目的就是对其子
View进行管理,为其子
View添加显示、响应的规则。因此,自定义
ViewGroup通常需要重写
onMeasure()方法对其子
View进行测量,重写
onLayout()方法来确定子
View的位置,重写
onTouchEvent()方法增加响应事件。所以我们需要做这几件事:
重写
onMeasure()方法对其子
View进行测量
重写
onLayout()方法来确定子
View的位置
重写
onTouchEvent()方法增加响应事件
以一个粘性
View效果作为示例,来看看每部分都需要怎么做。先看完整版代码,后面会进一步分析:
package com.wondertwo.app.customview; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Scroller; /** * 实现粘性效果,即当子View向上滑动大于某值后,松开手指它将向上滑动并显示下一个子View * 当滑动距离小于某值,松开手指后它将回到开始的位置 * * Created by wondertwo on 2016/2/27. */ public class SticklyView extends ViewGroup { private int mScreenHeight; private Scroller mScroller; private int mLastY; private int mStart; private int mEnd; public SticklyView(Context context) { super(context); } public SticklyView(Context context, AttributeSet attrs) { super(context, attrs); } public SticklyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 重写onMeasure()方法,通过遍历子View来测量子View的大小 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; ++i) { View childView = getChildAt(i); measureChild(childView, widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); // 设置ViewGroup的高度,高度等于每个子View的高度乘以子View的个数 MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams(); mlp.height = mScreenHeight * childCount; setLayoutParams(mlp); for (int i = 0; i< childCount; i++) { View child = getChildAt(i); if (child.getVisibility() != View.GONE) { // public void layout(int left, int top, int right, int bottom)设置child的位置 child.layout(l, i * mScreenHeight, r, (i + 1) *mScreenHeight); } } } /** * 手指触摸动作的监听 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY(); switch (event.getAction()) { // 记录触摸起点 case MotionEvent.ACTION_DOWN: mLastY = y; mStart = getScrollY(); break; case MotionEvent.ACTION_MOVE: // 拦截动画 if (!mScroller.isFinished()) { mScroller.abortAnimation(); } int dy = mLastY - y; if (getScrollY() < 0) { dy = 0; } if (getScrollY() > getHeight() - mScreenHeight) { dy = 0; } scrollBy(0, dy); mLastY = y; break; case MotionEvent.ACTION_UP: // 记录触摸终点 int dScrollY = checkAlignment(); if (dScrollY > 0) { if (dScrollY < mScreenHeight / 3) { mScroller.startScroll( 0, getScrollY(), 0, -dScrollY); } else { mScroller.startScroll( 0, getScrollY(), 0, mScreenHeight - dScrollY); } } else { if (-dScrollY < mScreenHeight / 3) { mScroller.startScroll( 0, getScrollY(), 0, -dScrollY); } else { mScroller.startScroll( 0, getScrollY(), 0, -mScreenHeight - dScrollY); } } break; } // 重绘 postInvalidate(); return true; } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { scrollTo(0, mScroller.getCurrY()); postInvalidate(); } } private int checkAlignment() { int mEnd = getScrollY(); boolean isUp = ((mEnd - mStart) > 0) ? true : false; int lastPrev = mEnd % mScreenHeight; int lastNext = mScreenHeight - lastPrev; if (isUp) { //向上的 return lastPrev; } else { return -lastNext; } } }
重写
onMeasure()方法对其子
View进行测量先要获得子
View的数量:
int count = getChildCount()。然后通过
for循环遍历每个子
View获得他们的大小。代码如下:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); // 设置ViewGroup的高度,高度等于每个子View的高度乘以子View的个数 MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams(); mlp.height = mScreenHeight * childCount; setLayoutParams(mlp); for (int i = 0; i< childCount; i++) { View child = getChildAt(i); if (child.getVisibility() != View.GONE) { // public void layout(int left, int top, int right, int bottom)设置child的位置 child.layout(l, i * mScreenHeight, r, (i + 1) *mScreenHeight); } } }
重写
onLayout()方法来确定子
View的位置和重写
onTouchEvent()方法增加响应事件了。其实这一步才是实现粘效果的关键。代码如下:
/** * 手指触摸动作的监听 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY(); switch (event.getAction()) { // 记录触摸起点 case MotionEvent.ACTION_DOWN: mLastY = y; mStart = getScrollY(); break; case MotionEvent.ACTION_MOVE: // 拦截动画 if (!mScroller.isFinished()) { mScroller.abortAnimation(); } int dy = mLastY - y; if (getScrollY() < 0) { dy = 0; } if (getScrollY() > getHeight() - mScreenHeight) { dy = 0; } scrollBy(0, dy); mLastY = y; break; case MotionEvent.ACTION_UP: // 记录触摸终点 int dScrollY = checkAlignment(); if (dScrollY > 0) { if (dScrollY < mScreenHeight / 3) { mScroller.startScroll( 0, getScrollY(), 0, -dScrollY); } else { mScroller.startScroll( 0, getScrollY(), 0, mScreenHeight - dScrollY); } } else { if (-dScrollY < mScreenHeight / 3) { mScroller.startScroll( 0, getScrollY(), 0, -dScrollY); } else { mScroller.startScroll( 0, getScrollY(), 0, -mScreenHeight - dScrollY); } } break; } // 重绘 postInvalidate(); return true; }
相关文章推荐
- Android AutoLayout全新的适配方式 堪称适配终结者
- android studio编译项目,9 patch图片报错Crunching Cruncher
- Android开发之EditText属性详解
- Android实战技巧:ViewStub的应用
- android开发习惯优化小记
- 如何判断android activity是否运行
- 最新力作:《Android自定义组件开发详解》
- 用Android Studio 运行ndk 程序
- Android属性动画
- android官网图像与动画章节demo的分析
- Android studio导入jar包及Library包
- Android中的android:layout_width和android:width
- Android之Animations的使用
- Android即时通讯--仿QQ即时聊天:(五)聊天模块
- Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();
- android meta-data的使用以及含义
- android 【九种对话框】的实现方式
- 【Android动画】之Tween动画 (渐变、缩放、位移、旋转)
- PDF阅读器系列之--MuPDF源码分析过程(二)
- Android高手进阶:Adapter深入理解与优化