【转载/修改】ScrollLayout代码修正,追加模仿viewpager滚动速度
组件作用为类似ViewPager但直接插视图的横向滚动容器。
修改自:http://blog.csdn.net/yaoyeyzq/article/details/7571940
在该组件基础上修正了滚动速度,使其匹配ViewPager的滚动速度,以下为代码:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller; /**
* @author
*/
public class ScrollLayout extends ViewGroup { private static final int MAX_SETTLE_DURATION = 600; // ms private Scroller mScroller;
private VelocityTracker mVelocityTracker; /**
* 当前的屏幕位置
*/
private int mCurScreen; /**
* 设置默认屏幕的属性,0表示第一个屏幕
*/
private int mDefaultScreen = 0; /**
* 标识滚动操作已结束
*/
private static final int TOUCH_STATE_REST = 0;
/**
* 标识正在执行滑动操作
*/
private static final int TOUCH_STATE_SCROLLING = 1; /**
* 标识滑动速率
*/
private static final int SNAP_VELOCITY = 600; /**
* 当前滑动状态
*/
private int mTouchState = TOUCH_STATE_REST; /**
* 在用户触发ontouch事件之前,我们认为用户能够使view滑动的距离(像素)
*/
private int mTouchSlop; /**
* 手指触碰屏幕的最后一次x坐标
*/
private float mLastMotionX; /**
* 手指触碰屏幕的最后一次y坐标
*/
@SuppressWarnings("unused")
private float mLastMotionY; public ScrollLayout(Context context) {
super(context);
mScroller = new Scroller(context);
mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
} public ScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
} public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScroller = new Scroller(context);
mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childLeft = 0;
final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only canmCurScreen run at EXACTLY mode!");
} final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only can run at EXACTLY mode!");
} final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// 初始化视图的位置
scrollTo(mCurScreen * width, 0);
} /**
* 根据滑动的距离判断移动到第几个视图
*/
public void snapToDestination(int velocity) {
final int screenWidth = getWidth();
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen, velocity);
} /**
* 滚动到制定的视图
*
* @param whichScreen 视图下标
*/
public void snapToScreen(int whichScreen, int velocity) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) { final int delta = whichScreen * getWidth() - getScrollX(); // 计算duration
int width = getWidth();
int halfWidth = width / 2;
float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / width);
float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio);
int duration = 4 * Math.round(1000 * Math.abs(distance / Math.abs(velocity)));
duration = Math.min(duration, MAX_SETTLE_DURATION);
// 开始滚动
mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
mCurScreen = whichScreen;
invalidate();
}
} public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mCurScreen = whichScreen;
scrollTo(whichScreen * getWidth(), 0);
} public int getCurScreen() {
return mCurScreen;
} @Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
} @Override
public boolean onTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event); final int action = event.getAction();
final float x = event.getX(); switch (action) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastMotionX = x;
break; case MotionEvent.ACTION_MOVE:
int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x; scrollBy(deltaX, 0);
break; case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity(); if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
// 向左移动
snapToScreen(mCurScreen - 1, velocityX);
} else if (velocityX < -SNAP_VELOCITY
&& mCurScreen < getChildCount() - 1) {
// 向右移动
snapToScreen(mCurScreen + 1, velocityX);
} else {
snapToDestination(velocityX);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
} return true;
} float distanceInfluenceForSnapDuration(float f) {
f -= 0.5f; // center the values about 0.
f *= 0.3f * Math.PI / 2.0f;
return (float) Math.sin(f);
} @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
} final float x = ev.getX();
final float y = ev.getY(); switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break; case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break; case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
} return mTouchState != TOUCH_STATE_REST;
} }
【转载/修改】ScrollLayout代码修正,追加模仿viewpager滚动速度的更多相关文章
- 数据库分库分表(sharding)系列(五) 一种支持自由规划无须数据迁移和修改路由代码的Sharding扩容方案
作为一种数据存储层面上的水平伸缩解决方案,数据库Sharding技术由来已久,很多海量数据系统在其发展演进的历程中都曾经历过分库分表的Sharding改造阶段.简单地说,Sharding就是将原来单一 ...
- HOOK大法实现不修改程序代码给程序添加功能
[文章标题]: HOOK大法实现不修改程序代码给程序添加功能[文章作者]: 0x18c0[软件名称]: Scylla[使用工具]: OD.Stub_PE.ResHacker[版权声明]: 本文原创于0 ...
- DB 分库分表(5):一种支持自由规划无须数据迁移和修改路由代码的 Sharding 扩容方案
作为一种数据存储层面上的水平伸缩解决方案,数据库Sharding技术由来已久,很多海量数据系统在其发展演进的历程中都曾经历过分库分表的Sharding改造阶段.简单地说,Sharding就是将原来单一 ...
- 用ildasm/ilasm修改IL代码
原文地址:http://www.cnblogs.com/dudu/archive/2011/05/17/ildasm_ilasm_il.html 在开发中遇到这样一个场景,需要修改一个dll文件(.N ...
- 如何用chrome修改js代码,跳过网站等待时间
用chrome修改js代码 By Z.H. Fu 切问录 [maplewizard.github.io](http://maplewizard.github.io ) 网页中大部分的限制都是由js编写 ...
- 翻译文章“AST 模块:用 Python 修改 Python 代码”---!!注意ironpathyon未实现此功能
https://github.com/upsuper/blog/commit/0214fdd084c4adf2de2ed9912d644fb59ce13a1c +Title: [翻译] AST 模块: ...
- 【Eclipse】修改java代码不强制重启
找到tomcat的server.xml文件,修改以下代码,重新发布重启.然后修改java代码就可以不用重启了. 将reloadable=“true”改成reloadable="false&q ...
- 本地修改js代码并时时生效的解决办法
js作为客户端语言(当然它也可以作服务端语言),非常强悍,一般情况下,我们都是在开发阶段不停的改,然后上线之后就作为稳定运行的代码. 然而有时候可能因为js写得有问题,导致上线后,某些功能无法使用,这 ...
- git push完代码 想撤回 并保留之前修改的代码 / 修改完代码 发现分支不对 想切换分支 /恢复已修改的文件
git reset --soft xxxx // xxxx是版本号 回退 git stash //保留当前分支修改的代码 git checkout xxx //切换到xxx分支 git stash l ...
随机推荐
- 使用django表单,使网页添加上传文件,并分析文件。
开发环境是: apache + python + django+ eclipse(开发环境) 欲达到目的: 在网页上,添加上传文件控件.然后读取csv文件,并分析csv文件. 操作步骤: django ...
- 链表:删除链表中重复的结点(java实现)
题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后 ...
- Xml语言
一.XML是什么?作用是什么? l XML ( eXtensible Markup Language )语言是一种可扩展的标记语言.其中的可扩展是相对HTML来说的.因为XML标签没有被预定义,需要 ...
- Flask框架学习笔记(API接口管理平台 V1.0)
今天博主终于完成了API接口管理平台,最后差的就是数据库的维护, 博主这里介绍下平台的设计原理,首先基于python,利用flask的web框架+bootstrap前端框架完成,先阶段完成了前台展示页 ...
- git添加文件过滤
操作流程 touch .gitignore vi .gitignore,添加需要过滤的文件或目录 git commit 出现问题 假如.gitignore里面添加file,而git库中已经存在file ...
- 酷我音乐API
今天把酷我音乐API分享给大家: 歌曲搜索API:http://search.kuwo.cn/r.s?all={0}&ft=music& itemset=web_2013&cl ...
- C语言之实现控制台光标随意移动
原理引入windows.h,首先是要获得输入的东西,然后通过判断: 1.方向键:执行上下左右的移动功能 2:回车键:执行换行的功能. 3.普通键:输入功能. 终点就是要获取到屏幕上的坐标,当按下了方向 ...
- 【测试】使用hr用户下的employees表写一条SQL语句,执行计划走索引全扫描
SQL> select count(*) from employees; COUNT(*) ---------- Execution Plan ------------------------- ...
- SQL基本语句(3) LOAD DATA INFILE
使用LOAD语句批量录入数据 语法: LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name' [REPLACE | IGNOR ...
- OSGI.NET 插件无法启动之情景一
关于osgi.net 的使用网上也有不少的资料,最近在使用osgi.net 开发插件的时候,遇到了这样的bug,造成插件甚至整个项目都无法启动,异常的具体消息如下: Could not find a ...