自定义滚动View
我们的ScrollView是在内容超出窗口的时候才可以拖动的
listview也是一样
条目比窗口多的时候,才可以拖动
我们用一个自定义的滚动View
内容没有超出窗口的时候
也可以拖动
然后有一个弹回的效果
我们先看看源码怎么写的
public class CustomerScrollView extends ScrollView {
private static final int size = 4;
private View inner;
private float y;
private Rect normal = new Rect();
public CustomerScrollView(Context context) {
super(context);
}
public CustomerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 0) {
inner = getChildAt(0);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner == null) {
return super.onTouchEvent(ev);
} else {
commOnTouchEvent(ev);
}
return super.onTouchEvent(ev);
}
public void commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
y = ev.getY();
break;
case MotionEvent.ACTION_UP:
if (isNeedAnimation()) {
// Log.v("mlguitar", "will up and animation");
animation();
}
break;
case MotionEvent.ACTION_MOVE:
final float preY = y;
float nowY = ev.getY();
/**
* size=4 表示 拖动的距离为屏幕的高度的1/4
*/
int deltaY = (int) (preY - nowY) / size;
// 滚动
// scrollBy(0, deltaY);
y = nowY;
if (isNeedMove()) {
if (normal.isEmpty()) {
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
return;
}
int yy = inner.getTop() - deltaY;
// 移动布局
inner.layout(inner.getLeft(), yy, inner.getRight(),
inner.getBottom() - deltaY);
}
break;
default:
break;
}
}
public void animation() {
TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),
normal.top);
ta.setDuration(200);
inner.startAnimation(ta);
inner.layout(normal.left, normal.top, normal.right, normal.bottom);
normal.setEmpty();
}
public boolean isNeedAnimation() {
return !normal.isEmpty();
}
public boolean isNeedMove() {
int offset = inner.getMeasuredHeight() - getHeight();
int scrollY = getScrollY();
if (scrollY == 0 || scrollY == offset) {
return true;
}
return false;
}
}
我们来分析一下
好像其他方法我们都不是很熟悉
我们就先从比较熟悉的onTouchEvent开始
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner == null) {
return super.onTouchEvent(ev);
} else {
commOnTouchEvent(ev);
}
return super.onTouchEvent(ev);
}
OK,如果inner为空,调super
inner是上面onFinishInflate方法里初始化的,所以一般情况下不为空
那么就会执行commOnTouchEvent方法
那么我们来看看这个方法
commOnTouchEvent
这个就是和我们平时处理touchEvent差不多的
所以我们按动作来分析,就是DOWN,MOVE和UP
DOWN
case MotionEvent.ACTION_DOWN:
y = ev.getY();
break;
获取y值
UP
case MotionEvent.ACTION_UP:
if (isNeedAnimation()) {
// Log.v("mlguitar", "will up and animation");
animation();
}
break;
isNeedAnimation就是是否需要执行动画
这里的判断和执行我们都不用管
我们只要知道UP抬起的时候,
如果需要动画,那么就动画
如果不需要动画,那么就不会动画
MOVE
case MotionEvent.ACTION_MOVE:
final float preY = y;
float nowY = ev.getY();
/**
* size=4 表示 拖动的距离为屏幕的高度的1/4
*/
int deltaY = (int) (preY - nowY) / size;
// 滚动
// scrollBy(0, deltaY);
y = nowY;
if (isNeedMove()) {
if (normal.isEmpty()) {
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
return;
}
int yy = inner.getTop() - deltaY;
// 移动布局
inner.layout(inner.getLeft(), yy, inner.getRight(),
inner.getBottom() - deltaY);
}
break;
首先是
final float preY = y;
float nowY = ev.getY();
int deltaY = (int) (preY - nowY) / size;
y = nowY;
获取各种y值,用来判断位置信息
然后if isNeedMove()
如果是需要移动就执行内部
然后如果normal是empty
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
return;
normal根据innner来设置位置
如果不是empty
int yy = inner.getTop() - deltaY;
inner.layout(inner.getLeft(), yy, inner.getRight(),
inner.getBottom() - deltaY);
inner重新设置位置
来看看ifNeedMove
public boolean isNeedMove() {
int offset = inner.getMeasuredHeight() - getHeight();
int scrollY = getScrollY();
if (scrollY == 0 || scrollY == offset) {
return true;
}
return false;
}
其实就是获取inner的偏移量
如果已经偏移了
那么肯定要弹回去