Ok I played around the custom view from the question you are linking to (Creating animation for images from small to large when scrolling vertical) and adding animations is pretty easy. The result looks like this:

First we have to make the custom view snap to a certain item. For that we have to modify the ScrollTouchListener
like this:
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;
}
}
Essentially I just added callback for onScrollStarted()
which is called when the user starts scrolling - in this case we don't need that - and onScrollEnded()
which is called when the user stops scrolling. I moved the responsibility of keeping track of the scroll progress to the ScrollTouchListener
for convenience and I also added getters and setters so we can modify the scroll progress if we need to - in our case so the views can snap into a certain position. Another thing I added is click detection in the form of the onClick
callback. We need that so we can click a view to make it expand. We can't use a normal OnClickListener
for this because we are adding a OnTouchListener
in our case the ScrollTouchListener
which consumes all touch events and so click listeners stop working. But the click detection is not perfect as it is implemented there. The main problem is that a click only counts if you do not move your finger. If you move your finger even by only one pixel it already counts as a scroll. There should be some extra logic here that you can move your finger by a few pixel and it still counts as a click, if necessary this can be implemented later.
Then in the custom view we add a calculation to the implementation of the onScroll()
method in the ScrollTouchListener
which calculates the index of the currently expanded view and of the first visible view at the top:
double relativeScroll = scrollPositionY / scrollProgressPerView;
currentItemIndex = (int)Math.round(relativeScroll);
currentItemIndex
is a member variables of the custom view. After that add the implementation of the onScrollEnded()
and onClick()
methods. All we do there is calculate the how far the custom view should be scrolled. Either to snap to a certain item or to scroll to the clicked item:
@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();
}
And that's it. This is all you need to enable the snapping and the selecting of Views
. As a result the whole implementation of the ScrollTouchListener
in the custom view should look something like this:
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();
}
};
You might wonder know where the actual animation stuff comes in, but we actually don't have to take care of that. There is one very convenient feature that handles all the animation for us. You just need to add this to attribute to the custom view in the xml layout:
android:animateLayoutChanges="true"
For example the layout I used for testing purposes looked like this:
<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>
Notice the android:animateLayoutChanges="true"
I added to the custom view. That is all you need to get this view to animate any layout change.