假设写一个android桌面滑动切换屏幕的控件(一)
首先这个控件应该是继承ViewGroup:
初始化:
public class MyGroup extends ViewGroup{ private Scroller mScroller;
private float mOriMotionX;
private float mLastMotionX;
private VelocityTracker mVelocityTracker;
private int mTouchState = TOUCH_STATE_REST;
private static final int TOUCH_STATE_REST = 0;
private int mTouchSlop;
private int mMaximumVelocity;
private static final int TOUCH_STATE_SCROLLING = 1;
private float mLastDownX;
private static final int DEFAULT_VALUE = 1000;
private int mNextScreen = -1;
private boolean mFlagLimitUp = false;
private static final int SNAP_VELOCITY = 700; private int mCurrentScreen;
public MyGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initWorkspace();
} private void initWorkspace() {
mScroller = new Scroller(getContext());
setCurrentScreen(0); final ViewConfiguration configuration = ViewConfiguration
.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();//这个是定义控件在scroll的最小像素距离
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); //速率。fling的一个以每秒滑动多少像素的值
}
先重写onmeasure:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
} }
onLayout:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int paddingleft = 0;
int paddingTop = 0;
int childLeft = paddingleft;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight() ; child.layout(childLeft, paddingTop, childLeft + childWidth,
childHeight + paddingTop);
<strong>childLeft += child.getMeasuredWidth(); //下个child的左边距和第一个child的左边距之间的距离正好是第一个child的width</strong>
}
} }
然后写View的touch事件:
onInterceptTouchEvent仅仅有返回false事件才会传递给控件里的view。就是view的ontouch事件才干够捕捉
View里的onTouchEvent返回为true,才干运行多次touch事件。事件才干得了传递
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
//假设为move事件,mTouchState为TOUCH_STATE_REST为精巧状态,这个是防止子控件在滑动时又用手指去滑,这样的情况下不响应这个事件
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
} final float x = ev.getX(); switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
//假设xMoved为true表示手指在滑动
if (xMoved) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
//mScroller.isFinished() 为true表示滑动结束了,这时候我们把状态置为TOUCH_STATE_REST
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break; case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
default:
break;
} //假设不是在精巧状态。都返回true,这样事件就不会传递给onTouchEvent了
return mTouchState != TOUCH_STATE_REST;
}
在滑动的时候返回true的原因是这时候不须要响应里面控件的ontouch事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev); int mScrollX = this.getScrollX(); //mScrollX表示X轴上的距离,往左滑动为正。这个时候屏幕向右移动 final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mOriMotionX = x;
mLastMotionX = x;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mOriMotionX = x;
mLastMotionX = x;
mLastDownX = x;
return true;
case MotionEvent.ACTION_MOVE:
System.out.println("====action move mScrollX="+mScrollX);
final int buffer = getWidth() / 2; //这个表示在第一页或是最后一页还能够滑动半个屏幕
//假设是往后滑动。屏幕向前,那么mLastMotionX是比x大的。deltaX是正的
int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
System.out.println("=====deltaX="+deltaX);
//deltaX<0表示往前滑动
if (deltaX < 0) {
//这个是往右滑动,屏幕向左移动
scrollBy(Math.max(-mScrollX - buffer, deltaX), 0);
}else{
int availableToScroll = 0;
if (getChildCount() > 0) { //此时Workspace上可能未加不论什么item,count == 0
System.out.println("====rihgt="+(getChildAt(
getChildCount() - 1).getRight())+"avail="+(getChildAt(
getChildCount() - 1).getRight()- mScrollX - getWidth()
));
//getChildAt(getChildCount() - 1).getRight()为全部的view加一起的宽度,这里加了3个view,一个view为1080,则这个值为3240
availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- mScrollX - getWidth();
//availableToScroll + buffer能够滑动的最大距离,deltax为滑动的距离
scrollBy(Math.min(availableToScroll + buffer, deltaX), 0);
}
} return true;
case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(DEFAULT_VALUE,
mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity();
//velocityX为手指滑动的速率。我们会跟给定值SNAP_VELOCITY做比較
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
// 这个时候是手指往前滑动,屏幕是向后移动
snapToScreen(mCurrentScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurrentScreen < getChildCount() - 1) {
// move right
snapToScreen(mCurrentScreen + 1);
} else {
snapToDestination(mLastMotionX < mOriMotionX);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mTouchState = TOUCH_STATE_REST;
if (Math.abs(mLastDownX - x) > 10) {
return true;
}
return false;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
return false;
default:
break;
} return true;
}
/**滑动的距离。离屏宽几分之中的一个时,就開始运行换屏动作。*/
/**
* snapToDestination.
* mLastMotionX < mOriMotionX (mLastMotion < mOriMotionX)表示这个是手向后滑动,但屏幕是往前的,反之是向前
* forward为true为往前划动,这时将scrollX加上三分之二的屏幕的宽度
* scrollX / screenWidth 来决定当前在哪个屏幕
* @param forward 是前进还是后退.
*/
public void snapToDestination(boolean forward) {
final int screenWidth = getWidth();
int scrollX = getScrollX(); if (forward) {
scrollX += screenWidth - screenWidth / 3;
} else {
scrollX += screenWidth / 3;
}
System.out.println("======screenWidth="+screenWidth+"scrollX / screenWidth="+(scrollX / screenWidth));
snapToScreen(scrollX / screenWidth);
} /**
* 假设计算要滑动的距离:(whichScreen * getWidth())为滑动后的X坐标,this.getScrollX()为当前的坐标,两者相减为滑动的距离
* Math.abs(delta) * 2为滑动的持续时间
*/
public void snapToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
boolean changingScreens = whichScreen != mCurrentScreen; mNextScreen = whichScreen;
int mScrollX = this.getScrollX();
final int newX = whichScreen * getWidth();
final int delta = newX - mScrollX;
System.out.println("====snapToScreen delta="+delta);
mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2);
//invalidate很重要,不然你移动一点页面不能回复原状
invalidate();
}
假设写一个android桌面滑动切换屏幕的控件(一)的更多相关文章
- [Android] 使用Include布局+Fragment滑动切换屏幕
前面的文章已经讲述了"随手拍"项目图像处理的技术部分,该篇文章主要是主界面的布局及屏幕滑动切换,并结合鸿洋大神的视频和郭神的第一行代码(强推两人Android博客),完毕了 ...
- 写一个Android输入法01——最简步骤
本文演示用Android Studio写一个最简单的输入法.界面和交互都很简陋,只为剔肉留骨,彰显写一个Android输入法的要点. 1.打开Android Studio创建项目,该项目和普通APP的 ...
- 为PhoneGap写一个android插件
为PhoneGap写一个android插件,要怎么做? 其实这句话应该反过来说,为android写一个PhoneGap插件,要怎么做? 这里以最简单的Hello World!为例,做个说明: 1.第一 ...
- Android判断Touch为滑动事件还是操作控件
Android判断Touch为滑动事件还是操作控件 因为在项目中要判断WebView是否处于滚动状态,但它不像ListView有onScrollStateChanged方法来监听,要实现就得手动监听它 ...
- 在开发第一个Android应用之前需要知道的5件事:
你能否详细讲述一下,在开发Android应用过程中每一阶段要用到的技能和编程语言? 建立一个Android应用程序可以归结为两个主要技能/语言:Java和Android系统.Java是Android的 ...
- (转载)Android UI设计之AlertDialog弹窗控件
Android UI设计之AlertDialog弹窗控件 作者:qq_27630169 字体:[增加 减小] 类型:转载 时间:2016-08-18我要评论 这篇文章主要为大家详细介绍了Android ...
- Android中EditText,Button等控件的设置
EditText可以使用:TextView.setEnabled(true)来设置为可编辑,其实很简单,写在这里以便以后自己查看. Button设置可用性:setVisibility(View.VIS ...
- Appium Android Bootstrap源码分析之控件AndroidElement
通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...
- Android support library支持包常用控件介绍(二)
谷歌官方推出Material Design 设计理念已经有段时间了,为支持更方便的实现 Material Design设计效果,官方给出了Android support design library ...
随机推荐
- JD-GUI on Ubuntu 13.04 64-bit
Java Decompiler (jd-gui) is a cute little tool I like using when working in Java. Unfortunately it o ...
- python循环,判断及函数
python中的for循环 #for循环格式(类似Java中的foreach):for 标识符 in 列表名称 : >>> movies = ["movie1", ...
- 关于nodejs,request模块的一个bug
今天在使用request时发生了一个错误, 对方网站的证书设置的不正确导致本地请求不能返回数据: 解决方案是在配置request时加入一个忽略证书验证得字段: 具体代码如下 request.post( ...
- Subsequence(暴力+二分)
Subsequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 10875 Accepted: 4493 Desc ...
- 郁闷的C小加(一)(后缀表达式)
郁闷的C小加(一) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 我们熟悉的表达式如a+b.a+b*(c+d)等都属于中缀表达式.中缀表达式就是(对于双目运算符来说 ...
- html系列教程--标题,水平线,注释以及段落
HTML标题 标题,用来显示文章重要性的文字,包含了文章的主旨,类似于作文题目. 标题(Heading)是通过 <h1> - <h6> 等标签进行定义的,由大到小一次排列,h1 ...
- 改良版的SQL Service 通用存储过程分页
上次写了通用存储过程.感觉还是有很大的BUG.就是条件不能参数画化.这个BUG可以说是致命的.但是我一直想在用什么方法能解决这个东西.其实我只是想写少量的代码来做更多的事情.我想能不能传集合给存储过程 ...
- C++学习笔录3
1.隐藏:存在于派生类和基类中.只要成员名字相同就叫做隐藏.参数和函数名都相同时,是一种特殊的隐藏,叫做重写,重写只出现在函数中.若需要访问基类中被隐藏的成员,必须访问这种方法:“对象名:类名::隐藏 ...
- QF——OC的多态,动态绑定及实现原理
多态: 封装,继承,多态是面向对象的三大特征. 那多态到底是什么呢? 多态:允许不同的类定义相同的方法,OC能自己判断当前类所对应的方法,不会混乱. 动态类型:程序直到运行时才确定对象的类型. 动态绑 ...
- sersync+inotify实时备份数据
Sersync项目简介与框架 简介 Sersync项目利用inotify与rsync技术实现对服务器数据实时同步的解决方案,其中inotify用于监控sersync所在服务器上文件系统的事件变化,rs ...