好的,我从您链接到的问题 (Creating animation for images from small to large when scrolling vertical) 中玩了自定义视图,添加动画非常容易。结果如下所示:
首先,我们必须让自定义视图捕捉到某个项目。为此,我们必须像这样修改ScrollTouchListener:
public abstract class ScrollTouchListener implements View.OnTouchListener {
private boolean touching = false;
private boolean scrolling = false;
private double x = 0;
private double y = 0;
private double scrollPositionX = 0;
private double scrollPositionY = 0;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
touching = true;
return true;
case MotionEvent.ACTION_UP:
touching = false;
if(scrolling) {
scrolling = false;
onScrollEnded(scrollPositionX, scrollPositionY);
} else {
onClick(x, y);
}
return true;
case MotionEvent.ACTION_MOVE:
double newX = event.getX();
double newY = event.getY();
double difX = x - newX;
double difY = y - newY;
if (scrolling) {
performScroll(difX, difY);
} else if(difX > 0 || difY > 0) {
scrolling = true;
onScrollStarted(scrollPositionX, scrollPositionY);
performScroll(difX, difY);
}
x = newX;
y = newY;
return true;
default:
return false;
}
}
protected abstract void onScrollStarted(double scrollPositionX, double scrollPositionY);
protected abstract void onScroll(double scrollPositionX, double scrollPositionY, double deltaX, double deltaY);
protected abstract void onScrollEnded(double scrollPositionX, double scrollPositionY);
protected abstract void onClick(double x, double y);
private void performScroll(double difX, double difY) {
scrollPositionX += difX;
scrollPositionY += difY;
onScroll(scrollPositionX, scrollPositionY, difX, difY);
}
public double getScrollPositionX() {
return scrollPositionX;
}
public void setScrollPositionX(double scrollPositionX) {
this.scrollPositionX = scrollPositionX;
}
public double getScrollPositionY() {
return scrollPositionY;
}
public void setScrollPositionY(double scrollPositionY) {
this.scrollPositionY = scrollPositionY;
}
}
基本上我只是为onScrollStarted() 添加了回调,当用户开始滚动时调用它——在这种情况下我们不需要它——以及当用户停止滚动时调用的onScrollEnded()。为了方便起见,我将跟踪滚动进度的责任转移到了ScrollTouchListener,我还添加了getter 和setter,以便我们可以在需要时修改滚动进度——在我们的例子中,视图可以捕捉到某个位置。我添加的另一件事是onClick 回调形式的点击检测。我们需要它,以便我们可以单击视图以使其展开。我们不能为此使用普通的OnClickListener,因为在我们的例子中我们添加了OnTouchListenerScrollTouchListener,它消耗所有的触摸事件,所以点击监听器停止工作。但是点击检测并不完美,因为它是在那里实现的。主要问题是,只有在您不移动手指时,点击才算数。如果你只移动手指一个像素,它就已经算作滚动了。这里应该有一些额外的逻辑,你可以将手指移动几个像素,它仍然算作点击,如有必要,可以稍后实现。
然后在自定义视图中,我们向ScrollTouchListener 中的onScroll() 方法的实现添加一个计算,该计算计算当前展开视图和顶部第一个可见视图的索引:
double relativeScroll = scrollPositionY / scrollProgressPerView;
currentItemIndex = (int)Math.round(relativeScroll);
currentItemIndex是自定义视图的成员变量。之后添加onScrollEnded() 和onClick() 方法的实现。我们所做的只是计算自定义视图应该滚动多远。捕捉到某个项目或滚动到单击的项目:
@Override
protected void onScrollEnded(double scrollPositionX, double scrollPositionY) {
scrollProgress = currentItemIndex * scrollProgressPerView + 1;
setScrollPositionY(scrollProgress);
updateChildViews();
}
@Override
protected void onClick(double x, double y) {
int clickedIndex = (int) Math.round((getScrollPositionY() + y) / scrollProgressPerView) + 1;
scrollProgress = clickedIndex * scrollProgressPerView + 1;
setScrollPositionY(scrollProgress);
updateChildViews();
}
就是这样。这就是启用捕捉和选择Views 所需的全部内容。因此,自定义视图中 ScrollTouchListener 的整个实现应如下所示:
private final ScrollTouchListener touchListener = new ScrollTouchListener() {
@Override
protected void onScrollStarted(double scrollPositionX, double scrollPositionY) {
// Nothing to do here
}
@Override
protected void onScroll(double scrollPositionX, double scrollPositionY, double deltaX, double deltaY) {
scrollProgress += deltaY;
if(scrollProgress < 0.0) {
scrollProgress = 0.0;
}
if(scrollProgress > maxScrollProgress) {
scrollProgress = maxScrollProgress;
}
double relativeScroll = scrollPositionY / scrollProgressPerView;
currentItemIndex = (int)Math.round(relativeScroll);
updateChildViews();
}
@Override
protected void onScrollEnded(double scrollPositionX, double scrollPositionY) {
scrollProgress = currentItemIndex * scrollProgressPerView + 1;
setScrollPositionY(scrollProgress);
updateChildViews();
}
@Override
protected void onClick(double x, double y) {
int clickedIndex = (int) Math.round((getScrollPositionY() + y) / scrollProgressPerView) + 1;
scrollProgress = clickedIndex * scrollProgressPerView + 1;
setScrollPositionY(scrollProgress);
updateChildViews();
}
};
您可能想知道实际的动画内容是从哪里来的,但实际上我们不必关心这些。有一个非常方便的功能可以为我们处理所有动画。您只需要将其添加到 xml 布局中的自定义视图的属性:
android:animateLayoutChanges="true"
例如,我用于测试目的的布局如下所示:
<at.test.app.customviews.accordion.view.Accordion xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/alpen"/>
<ImageView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/alpen"/>
<ImageView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/alpen"/>
<ImageView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/alpen"/>
</at.test.app.customviews.accordion.view.Accordion>
注意我添加到自定义视图中的android:animateLayoutChanges="true"。这就是让这个视图为任何布局更改设置动画所需的全部内容。