Android自定义ViewGroup实现弹性滑动效果
作者:yangtianrui95 发布时间:2022-07-25 19:44:08
标签:Android,ViewGroup,弹性滑动
自定义View实现一个弹性滑动的效果,供大家参考,具体内容如下
实现原理
onMeasure()中测量所有子View
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 测量所有子View
int count = getChildCount();
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
onLayout()中,将所有的子View按照位置依次往下排列
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 设置ViewGroup的高度,对所有子View进行排列
int childCount = getChildCount();
MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
params.height = mScreenHeight * childCount;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
// 给每个ChildView放置在指定位置
childView.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight);
}
}
}
onTouchEvent()中处理滑动
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = (int) event.getY();
mStart = getScrollY();
return true;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
// 终止滑动
mScroller.abortAnimation();
}
int offsetY = (int) (mLastY - event.getY());
Log.d(TAG, "onTouchEvent: getScrollY: " + getScrollY());
Log.d(TAG, "onTouchEvent: offsetY " + offsetY);
// 到达顶部,使用offset判断方向
if (getScrollY() + offsetY < 0) { // 当前已经滑动的 Y 位置
offsetY = 0;
}
// 到达底部
if (getScrollY() > getHeight() - mScreenHeight && offsetY > 0) {
offsetY = 0;
}
scrollBy(0, offsetY);
// 滑动完成后,重新设置LastY位置
mLastY = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int distance = mEnd - mStart;
if (distance > 0) { // 向上滑动
if (distance < mScreenHeight / 3) {
Log.d(TAG, "onTouchEvent: distance < screen/3");
// 回到原来位置
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
// 滚到屏幕的剩余位置
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - distance);
}
} else { // 向下滑动
if (-distance < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - distance);
}
}
postInvalidate();
}
return super.onTouchEvent(event);
}
其中ACTION_UP这段代码是处理弹性滑动的
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int distance = mEnd - mStart;
if (distance > 0) { // 向上滑动
if (distance < mScreenHeight / 3) {
Log.d(TAG, "onTouchEvent: distance < screen/3");
// 回到原来位置
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
// 滚到屏幕的剩余位置
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - distance);
}
} else { // 向下滑动
if (-distance < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - distance);
}
}
postInvalidate();
完整代码
public class ScrollViewGroup extends ViewGroup {
private static final String TAG = "ScrollView";
private Scroller mScroller;
private int mScreenHeight; // 窗口高度
private int mLastY;
private int mStart;
private int mEnd;
public ScrollViewGroup(Context context) {
this(context, null);
}
public ScrollViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScroller = new Scroller(context);
// 获取屏幕高度
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
mScreenHeight = metrics.heightPixels;
Log.d(TAG, "ScrollViewGroup: ScreenHeight " + mScreenHeight);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 测量所有子View
int count = getChildCount();
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 设置ViewGroup的高度,对所有子View进行排列
int childCount = getChildCount();
MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
params.height = mScreenHeight * childCount;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
// 给每个ChildView放置在指定位置
childView.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = (int) event.getY();
mStart = getScrollY();
return true;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
// 终止滑动
mScroller.abortAnimation();
}
int offsetY = (int) (mLastY - event.getY());
Log.d(TAG, "onTouchEvent: getScrollY: " + getScrollY());
Log.d(TAG, "onTouchEvent: offsetY " + offsetY);
// 到达顶部,使用offset判断方向
if (getScrollY() + offsetY < 0) { // 当前已经滑动的 Y 位置
offsetY = 0;
}
// 到达底部
if (getScrollY() > getHeight() - mScreenHeight && offsetY > 0) {
offsetY = 0;
}
scrollBy(0, offsetY);
// 滑动完成后,重新设置LastY位置
mLastY = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int distance = mEnd - mStart;
if (distance > 0) { // 向上滑动
if (distance < mScreenHeight / 3) {
Log.d(TAG, "onTouchEvent: distance < screen/3");
// 回到原来位置
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
// 滚到屏幕的剩余位置
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - distance);
}
} else { // 向下滑动
if (-distance < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -distance);
} else {
mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - distance);
}
}
postInvalidate();
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
}
来源:http://blog.csdn.net/y874961524/article/details/52752169


猜你喜欢
- 关于Context我们首先应该知道:(1)它描述的是一个应用程序环境的信息,即上下文。(2)该类是一个抽象(abstract class)类
- 前言有位朋友,某天突然问东哥:在 Java 中,防止重复提交最简单的方案是什么?这句话中包含了两个关键信息,第一:防止重复提交;第二:最简单
- Service翻译成中文是服务,熟悉Windows 系统的同学一定很熟悉了。Android里的Service跟Windows里的Servic
- 喜欢另辟蹊径的我,在这里废话不多说了,直接上代码和图片了。效果图如下:第一步:MainActivity的代码如下:package net.l
- IO流代码:void LoadByIO() { float time = Time.t
- 字典表示一种复杂的数据结构,这种数据结构允许按照某个键来访问元素。字典也称为映射或散列表。字典的主要特性是能根据键快速查找值。也可以自由添加
- 约瑟夫环约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数
- springmvc提供了 * ,类似于过滤器,他将在我们的请求具体出来之前先做检查,有权决定接下来是否继续,对我们的请求进行加工。 * ,可
- 代理模式代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基
- 配置基础的定时任务最基本的配置方法,而且这样配置定时任务是单线程串行执行的,也就是说每次只能有一个定时任务可以执行,可以试着声明两个方法,在
- 前提:你的电脑是AMD处理器,想使用Android studio,自己的电脑系统是win10家庭版,在百度找到勾选hyper-v就能用,然后
- 一个简单的实现版本,没有去Hook键鼠等操作,事先录制好操作步骤(将鼠标移动到需要操作的位置,按下热键执行相应动作),点击运行即可。主要还是
- 实验目的在C#和Rest/Restful以及其它的Web服务交互过程中,大量使用到JSON传递数据,如何快捷的转化C#对象到JSON和转化J
- 前几天在“Android绘图之渐隐动画”一文中通过画线实现了渐隐动画,但里面有个问题,画笔较粗(大于1)时线段之间会有裂隙,我又改进了一下。
- 前言本文主要介绍了关于java结合keytool实现非对称签名和验证的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍
- 问题描述:输入:两个日期输出:两个日期相差的天数具体代码实现方法1:通过Calendar类的日期比较。注意:这里需要考虑一下:日期是跨年份的
- 问题描述:使用Design包的TabLayout实现类似网易选项卡动态滑动效果的时候,使用addTab()方法给TabLayout动态添加标
- 最新Android版本、API级别与代号对应关系代号版本号API/NDK级别发布时间-O8.0API level 262017-3-21牛轧
- 上下文:程序运行需要的环境(外部变量)上下文切换:将之前的程序需要的外部变量复制保存,然后切换到新的程序运行环境系统调用:(用户态陷入操作系
- 重新认识 Java 的 System.in以前也写过不少命令行的程序,处理文件时总需要通过参数指定路径,直到今天看资料时发现了一种我自己从来