http://blog.csdn.net/guolin_blog/article/details/48719871
一、在styles中添加
其中orientation为滑动的反向,当xml中orientation的设为vertical为纵向滑动,设为horizontal时为横向滑动。
<declare-styleable name="ScrollerLayout">
<attr name="orientation" format="enum">
<enum name="vertical" value="0"/>
<enum name="horizontal" value="1"/>
</attr>
</declare-styleable>
二、实现类ScrollerLayout
一个控件在其父窗口中的坐标位置
View.getLocationInWindow(int[] location)
一个控件在其整个屏幕上的坐标位置
View.getLocationOnScreen(int[] location)
其中主要运用scrollTo()、scrollBy()、getRawX()、getRawY()和Scroller来实现滑动原理的。
scrollBy()方法是让View相对于当前的位置滚动某段距离;
scrollTo()方法则是让View相对于初始的位置滚动某段距离;
getRawX()和getRawY()获得的是相对屏幕的位置,
getX()和getY()获得的永远是view的触摸位置坐标(这两个值不会超过view的长度和宽度)。
(其中向上、向左时为正值, 向下、向右为负值)
Scroller的基本用法其实还是比较简单的,主要可以分为以下几个步骤:
- 创建Scroller的实例
- 调用startScroll()方法来初始化滚动数据并刷新界面
- 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
public class ScrollerLayout extends ViewGroup {
/**
* 用于完成滚动操作的实例
*/
private Scroller mScroller;
/**
* 判定为拖动的最小移动像素数
*/
private int mTouchSlop;
/**
* 手机按下时的屏幕坐标
*/
private float mXDown;
private float mYDown;
/**
* 手机当时所处的屏幕坐标
*/
private float mXMove;
private float mYMove;
/**
* 上次触发ACTION_MOVE事件时的屏幕坐标
*/
private float mXLastMove;
private float mYlastMove;
// 初始化左右边界值
private int leftBorder;
private int rightBorder;
private int topBorder;
private int bottomBortor;
private int origintion = 0;
public ScrollerLayout(Context context) {
this(context, null);
}
public ScrollerLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrollerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ScrollerLayout, defStyleAttr, 0);
origintion = array.getInt(R.styleable.ScrollerLayout_orientation, 0);
array.recycle();
initView(context);
}
private void initView(Context context){
// 第一步,创建Scroller的实例
mScroller = new Scroller(context);
ViewConfiguration configuration = ViewConfiguration.get(context);
// 获取TouchSlop值
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
//mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i ++ ){
View childView = getChildAt(i);
// 为ScrollerLayout中的每一个子控件测量大小
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i ++){
View childView = getChildAt(i);
if (origintion == 1) {
// 为ScrollerLayout中的每一个子控件在水平方向上进行布局
childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(),childView.getMeasuredHeight());
} else if (origintion == 0){
childView.layout(0, i * childView.getMeasuredHeight(), childView.getMeasuredWidth(),
(i + 1) * childView.getMeasuredHeight());
}
}
// 初始化左右上下边界值
leftBorder = getChildAt(0).getLeft();
rightBorder = getChildAt(childCount - 1).getRight();
topBorder = getChildAt(0).getTop();
bottomBortor = getChildAt(childCount - 1).getBottom();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mXDown = ev.getRawX();
mYDown = ev.getRawY();
mXLastMove = mXMove;
mYlastMove = mYMove;
break;
case MotionEvent.ACTION_MOVE:
mXMove = ev.getRawX();
mYMove = ev.getRawY();
if (origintion == 1) {
float diff = Math.abs(mXMove - mXDown);
mXLastMove = mXMove;
// 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
if (diff > mTouchSlop) {
return true;
}
} else if (origintion == 0){
float diff = Math.abs(mYMove - mYDown);
mYlastMove = mYMove;
if (diff > mTouchSlop){
return true;
}
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
mXMove = event.getRawX();
mYMove = event.getRawY();
if (origintion == 1) {
int scrolledX = (int) (mXLastMove - mXMove);
if (getScrollX() + scrolledX < leftBorder) {
scrollTo(leftBorder, 0);
return true;
} else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
scrollTo(rightBorder - getWidth(), 0);
return true;
}
scrollBy(scrolledX, 0);
mXLastMove = mXMove;
} else if (origintion == 0){
int scrolledY = (int) (mYlastMove - mYMove);
if (getScrollY() + scrolledY < topBorder){
scrollTo(0, topBorder);
return true;
} else if (getScrollY() + getHeight() + scrolledY > bottomBortor){
scrollTo(0, bottomBortor - getHeight());
return true;
}
scrollBy(0, scrolledY);
mYlastMove = mYMove;
}
break;
case MotionEvent.ACTION_UP:
if (origintion == 1) {
// 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
int dx = targetIndex * getWidth() - getScrollX();
// 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
mScroller.startScroll(getScrollX(), 0, dx, 0);
} else if (origintion == 0){
int targetIndey = (getScrollY() + getHeight() / 2) / getHeight();
int dy = targetIndey * getHeight() - getScrollY();
mScroller.startScroll(0, getScrollY(), 0, dy);
}
invalidate();
break;
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
// 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
if (mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}
三、在Activity的xml文件中添加布局
<com.example.myapplication.ScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:orientation="vertical"
tools:context="com.example.myapplication.MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="This is first child view"/>
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="This is second child view"/>
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="This is third child view"/>
</com.example.myapplication.ScrollerLayout>
网友评论