为了更好的了解一下滑动控件,我们需要先了解一下Scroller的作用.
Scroller可以说是View的辅助类,在使用它之前,用户需要通过startScroll的参数,即起始坐标和(x,y)轴上需要滚动的距离,可以理解为一条用点表达的有向线段,调用方法为:mScroller.startScroll(x0,y0,x1,y1)。由于Scroller封装了时间,要滚动的目标x轴和y轴,以及在每个时间内View应该滚动到的(x,y)轴坐标,用户就可以通过Scroller对象的getCurX()和getCurY()方法来获取当前时刻View应该滚动到的位置。然后通过View的scrollTo和scrollBy方法来进行滚动,那么如何判断滚动是否结束呢,我们需要重写View类的computeScroll()方法,该方法会在View绘制的时候被调用,在里面调用Scroller对象的computeScrollOffset方法判断滚动是否完成,如果返回true表示未完成,如果返回false表示完成。而上述的scrollTo和scrollBy方法就是在返回值为true时调用。并且最后还需要调用postInvalidate()或者Invalidate()方法实现View的重绘。View的重绘会导致computeScroll方法被调用,从而继续整个过程。
以上内容都是书面话语,不是我个人理解,但是本人也是靠着这些解释和定义逐渐明白怎么自己写滑动控件的,下面来说一下本人对于滑动的理解。当然这些理解是本人在写滑动控件的摸索过程中自己的经验之谈,对与不对欢迎大家指点。
首先声明,我下面的自定义的控件并未实际用到Scroller。
Scroller实现的滑动是非接触性滑动,可以理解为惯性滑动(例如:你滑动ListView控件时,当你的手离开后,控件的持续性滑动,这种滑动不是你的手带动,而是类似于惯性(背后算个暂时未研究),这是通过Scoller和View本身的scrollTo和scrollBy实现的)或者弹性滑动(下拉刷新)
而不使用Scroller实现的滑动,则是接触性滑动,也就是View的滑动只会根据你滑动的变化,在根据View本身的scrollTo和scrollBy实现滑动,如果你没有接触View,不管你向下或者向上再怎么使劲,当你手离开是,他也不会再滑动了。而我下面实现的滑动就是接触性滑动。
Demo 实例
package com.test.hudezhi.testroject.customview.scrolllayout;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import com.test.hudezhi.testroject.R;
public class SquareScroll extends ViewGroup {
private Scroller mScroller;
private View footerView;
private View bodyView;
private View headerView;
private LayoutInflater inflater;
private int initHeight;
private int initBottom;
private int downScroll;
private int mLastY;
public SquareScroll(Context context) {
this(context, null);
}
public SquareScroll(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareScroll(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScroller = new Scroller(context);
inflater = LayoutInflater.from(context);
initView();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
this.postInvalidate();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
break;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
int currentY = (int) event.getRawY();
Log.i("", "" + currentY);
int deltaY = currentY - mLastY;
if (deltaY > 0) {
if (downScroll >= initHeight) {
downScroll = initHeight;
} else {
if (downScroll + deltaY >= initHeight) {
scrollTo(0, 0);
downScroll = initHeight;
} else {
downScroll += deltaY;
whetherScroll(deltaY);
}
}
} else if (deltaY < 0) {
if (downScroll <= -initBottom) {
downScroll = (-initBottom);
} else {
if (downScroll + deltaY <= (-initBottom)) {
int finalY = initBottom + initHeight;
scrollTo(0, finalY);
downScroll = (-initBottom);
} else {
downScroll += deltaY;
whetherScroll(deltaY);
}
}
}
mLastY = currentY;
break;
}
return true;
}
private void whetherScroll(int offsetY) {
scrollBy(0, -offsetY);
this.invalidate();
}
private void initView() {
initHeaderView();
initBodyView();
initFooterView();
}
private void initHeaderView() {
headerView = inflater.inflate(R.layout.header, this, false);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
headerView.setLayoutParams(lp);
addView(headerView);
}
private void initBodyView() {
bodyView = inflater.inflate(R.layout.body, this, false);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
bodyView.setLayoutParams(lp);
addView(bodyView);
}
private void initFooterView() {
footerView = inflater.inflate(R.layout.footer, this, false);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
footerView.setLayoutParams(lp);
addView(footerView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int tempHeight = 0;
int tempWidth = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childWidth = child.getMeasuredWidth();
tempWidth = Math.max(childWidth, tempWidth);
tempHeight += child.getMeasuredHeight();
}
heightSize = Math.max(tempHeight, heightSize);
widthSize = Math.max(tempWidth, widthSize);
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean is, int l, int t, int r, int b) {
int childCount = getChildCount();
int left = 0;
int top = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
top += child.getMeasuredHeight();
}
initHeight = headerView.getMeasuredHeight();
initBottom = footerView.getMeasuredHeight();
scrollTo(0, initHeight);
}
}