package cn.com;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
public class MyScrollView extends ViewGroup{
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyScrollView(Context context) {
super(context);
initView();
}
private MyScroller myScroller;
// private Scroller myScroller;
/**
* 初始化
*/
private void initView() {
myScroller = new MyScroller(getContext());
// myScroller = new Scroller(getContext());
detector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
@Override
// 当一个手指抬起时,回调该方法
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
// 当有手指按下时,回调该方法
public void onShowPress(MotionEvent e) {
}
@Override
/**
* 当手指在屏幕滑动时,回调该方法
* @param e1 是down事件
* @param e2 是最近的一个move事件
* @param distanceX 二个相临事件之间X方向的距离
* @param distanceY 二个相临事件之间Y方向的距离
*/
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
// System.out.println("distanceX::"+distanceX);
/**
* 让当前的view发生一些偏移
* 参数一 是x方向偏移的距离
* 参数二 是y方向偏移的距离
*/
scrollBy((int)distanceX,0);
/**
* 将当前的view的 scroll Position 定位在某个点上
* 参数一 是X坐标点
* 参数二 是Y坐标点
*/
// scrollTo(mScrollX + x, mScrollY + y);
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
});
}
/**
* 手势解析的工具类
*/
private GestureDetector detector;
@Override
/**
* 分发事件
* 系统默认的事件分发的规则,一般情况无需修改此方法
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
/**
* onInterceptTouchEvent 方法中,down事件的X坐标
*/
private int onInterDownX;
/**
* onInterceptTouchEvent 方法中,down事件的Y坐标
*/
private int onInterDownY;
@Override
/**
* 中断事件的传递
* 默认返回 false 意思是,不中断,
* 返回 true 意思是,中断事件传递,由自己消费
*/
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean result = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onInterDownX = (int) event.getX();
onInterDownY = (int) event.getY();
// 解决testView 跳跃的BUG
detector.onTouchEvent(event);
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
// 水平方向滑动的距离 = (move事件的X坐标 - down 事件的X坐标)的绝对值
int disX = (int) Math.abs(event.getX()-onInterDownX);
int disY = (int) Math.abs(event.getY()-onInterDownY);
// 如果水平方向,大于竖直方向,则中断事件
if(disX> disY && disX>15){ // disX>15 是为过虑手指的抖动
result = true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return result;
}
/**
* DOWN 事件时的X坐标
*/
private int downX;
/**
* 显示在屏幕的子view的下标
*/
private int currIndex;
@Override
public boolean onTouchEvent(MotionEvent event) {
// super.onTouchEvent(event)
/**
* 当点中listView水平滑动时,down事件,MyScrollView没有中断,按默认传递给了listView,前几个MOVE事件,由于不超
* 过15个像素同样也传递给了listView,当移动的距离超过15个像素时,MyScrollView中断了事件,事件交由自己来处理,
* 即,执行当前的onTouchEvent 方法,而此时,onTouchEvent方法收到的第一个事件,是MOVE事件
*/
System.out.println("MyScrollView.onTouchEvent()"+event.getAction());
detector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 0
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE: // 2
break;
case MotionEvent.ACTION_UP: // 1
// upX - downX > getWidth()/2 切换至上一页
// 否则保持不变
int upX = (int) event.getX();
int tempIndex = currIndex;
// downX - upX > getWidth()/2 切换至下一页
if((downX - upX )>getWidth()/2){
tempIndex++;
}else if((upX - downX)>getWidth()/2){
tempIndex--;
}
moveToDest(tempIndex);
break;
}
return true;
}
/**
* 显示指定下标 的页面
* @param tempIndex
*/
public void moveToDest(int tempIndex) {
if(tempIndex<0){
tempIndex = 0;
}
if(tempIndex>getChildCount()-1){
tempIndex = getChildCount()-1;
}
currIndex = tempIndex;
// 触发页面改变的监听
if(onPageChangedListener!=null){
onPageChangedListener.onPagedSelect(currIndex);
}
// 将下标为currIndex 的子View显示在屏幕中
// scrollTo(getWidth()*currIndex, 0);
int distanceX = getWidth()*currIndex - getScrollX();// 距离 = 终点坐标 - 当前坐标
myScroller.startScroll(getScrollX(), 0, distanceX, 0);
// handler.sendEmptyMessage(FLUSH);
// 刷新页面,会导致一系列的方法被调用,其中就会调用到computeScroll 方法
invalidate();
}
@Override
/**
* 当刷新页面的时候,如果需要改变 mSCrollX和mScrollY的值,可以在此方法中,做出修改。
*/
public void computeScroll() {
super.computeScroll();
// 计算当前的偏移量
if(myScroller.computeScrollOffset()){
int currX = myScroller.getCurrX();
// System.out.println("currX::"+currX);
// 让页面移动到指定的位置
scrollTo(currX, 0);
// 再次刷新,刷新页面,会导致一系列的方法被调用,其中就会调用到computeScroll 方法
invalidate();
}
}
// private final int FLUSH = 999;
//
// private Handler handler = new Handler(){
// public void handleMessage(android.os.Message msg) {
// switch (msg.what) {
// case FLUSH:
// // 计算当前的偏移量
// if(myScroller.computeScrollOffset()){
// int currX = myScroller.getCurrX();
// System.out.println("currX::"+currX);
//
// // 让页面移动到指定的位置
// scrollTo(currX, 0);
// // 再次刷新
// handler.sendEmptyMessage(FLUSH);
// }
//
//
// break;
// }
// };
// };
@Override
/**
* 当系统测量view的大小的时候,调用,我们在此方法中的任务就是,计算自身的大小,调用setMeasuredDimension 方法设置大小
* 如果当前view是一个布局,那么,还应在此方法中,测试,所有的子view的大小。
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
System.out.println("widthSize::"+widthSize);
System.out.println("widthMeasureSpec::"+widthMeasureSpec);
// 测量,testView 的大小
//getChildAt(2).measure(widthMeasureSpec, heightMeasureSpec);
// 为所有的子view测量大小
for(int i=0;i<getChildCount();i++){
View view = getChildAt(i);
view.measure(widthMeasureSpec, heightMeasureSpec);
}
// getMeasuredWidth(); // 获得view的测量值的宽度
// getMinimumHeight(); // 获得view的测量值的高度
// 测量的值,在 onMeasure方法执行完以后,就有了测量大小
// getWidth(); // view的真实的宽度
// getHeight(); // view的真实的高度
// 真实的大小,在 onLayout 方法执行完了以后,才有
// 系统的布局,在指定view的大小时,会参考 view的测量值的大小,根据测量的值来设置真实的大小
}
@Override
/**
* 当父view为我们指定好位置以后,调用此方法 ,
* 如果当前view也是一个viewGroup,那么,应在此方法 中,为当前view的子view指定位置
* changed 是说你的位置是否有发生改变
* l t r b 是当前view 在父view 坐标系中的位置
*/
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 为子view指定位置
// View child0 = getChildAt(0);
// // 为下标为0的子view,指定位置
// child0.layout(50, 30, 150, 200);
//
// View child1 = getChildAt(1);
// // 为下标为1的子view,指定位置
// child1.layout(100, 230, 500, 600);
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
// 让第一个子view填充满整个屏幕,其他的子view,依次向右平移一个宽度
child.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());
}
}
private IOnPageChangedListener onPageChangedListener;
public void setOnPageChangedListener(IOnPageChangedListener onPageChangedListener){
this.onPageChangedListener = onPageChangedListener;
}
/**
* 当页面选择发生改变时的回调接口
* @author leo
*
*/
public interface IOnPageChangedListener{
/**
* 选择的页面发生改变时,回调此方法
* @param currIndex 新的页面的下标
*/
void onPagedSelect(int currIndex);
}
}
package cn.com;
import android.content.Context;
import android.os.SystemClock;
/**
* 计算位移偏移量
*/
public class MyScroller {
private int startX;
private int startY;
private int distanceX;
private int distanceY;
public MyScroller(Context ctx){
}
/**
* 开始移动
* @param startX 开始点的X坐标
* @param startY 开始点的Y坐标
* @param distanceX X方向移动的距离
* @param distanceY Y方向移动的距离
*/
public void startScroll(int startX,int startY,int distanceX,int distanceY){
this.startX = startX;
this.startY = startY;
this.distanceX = distanceX;
this.distanceY = distanceY;
startTime = SystemClock.uptimeMillis();// 手机从开机到现在的时间,不包含深度休眠的时间
isFinish = false;
}
private long startTime;
private int totalTime = 200;
private boolean isFinish;
private int currX;
private int currY;
/**
* 计算当前的偏移量,
* @return
* 返回true 是指还在运行
*/
public boolean computeScrollOffset(){
if(isFinish){
return false;
}
// 获得动画运行的时间
long passTime = SystemClock.uptimeMillis() - startTime;
if(passTime<totalTime){ // 还在动动
currX = (int) (startX + distanceX * passTime /totalTime);
currY = (int) (startY + distanceY * passTime /totalTime);
}else{
// 运动已经结束了
setCurrX(startX + distanceX);
setCurrY(startY + distanceY);
isFinish = true;
}
return true;
}
public int getCurrX() {
return currX;
}
public void setCurrX(int currX) {
this.currX = currX;
}
public int getCurrY() {
return currY;
}
public void setCurrY(int currY) {
this.currY = currY;
}
}