package com.marshalchen.common.uimodule.discrollview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;
/**
* Layout container for a View hierarchy that can discrollved (ie Discrollvable).
* For each discrollvables, if it's possible (ie enough space to scroll the whole view),
* the discrollvable starts to be discrollved when its center reaches the center of the DiscrollView;
* else, the discrollvables starts to be discrollved when its top reaches the bottom of the DiscrollView
*
* @see com.marshalchen.common.uimodule.discrollview.Discrollvable
* @see com.marshalchen.common.uimodule.discrollview.DiscrollViewContent
*
*/
public class DiscrollView extends ScrollView {
private DiscrollViewContent mContent;
public DiscrollView(Context context) {
super(context);
}
public DiscrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DiscrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setupFirstView();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if(getChildCount() != 1) {
throw new IllegalStateException("Discrollview must host one child.");
}
View content = getChildAt(0);
if(!(content instanceof DiscrollViewContent)) {
throw new IllegalStateException("Discrollview must host a DiscrollViewContent.");
}
mContent = (DiscrollViewContent) content;
if(mContent.getChildCount() < 2) {
throw new IllegalStateException("Discrollview must have at least 2 children.");
}
}
private void setupFirstView() {
//first first take all the parent height
View first = mContent.getChildAt(0);
if(first != null) {
first.getLayoutParams().height = getHeight();
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
onScrollChanged(t);
}
public static float clamp(float value, float max, float min) {
return Math.max(Math.min(value, min), max);
}
private int getAbsoluteBottom() {
View last = getChildAt(getChildCount() - 1);
if(last == null) {
return 0;
}
return last.getBottom();
}
private void onScrollChanged(int top) {
int scrollViewHeight = getHeight();
int scrollViewBottom = getAbsoluteBottom();
int scrollViewHalfHeight = scrollViewHeight / 2;
// starts at 1 because the first View is a static non Discrollvable
// view.
for(int index = 1; index < mContent.getChildCount() ; index++) {
View child = mContent.getChildAt(index);
if(! (child instanceof Discrollvable)) {
//it's a static view, doesn't care about
continue;
}
Discrollvable discrollvable = (Discrollvable) child;
int discrollvableTop = child.getTop();
int discrollvableHeight = child.getHeight();
int discrollvableAbsoluteTop = discrollvableTop - top;
//the Discrollvable is too big to be discrollved when its center is reached
//the Discrollvable center. Discrollve it by its top.
if(scrollViewBottom - child.getBottom() < discrollvableHeight + scrollViewHalfHeight) {
//the Discrollvable top reaches the DiscrollView bottom
if(discrollvableAbsoluteTop <= scrollViewHeight) {
int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
discrollvable.onDiscrollve(clamp(visibleGap / (float) discrollvableHeight, 0.0f, 1.0f));
} else {
discrollvable.onResetDiscrollve();
}
} else {
if(discrollvableAbsoluteTop <= scrollViewHalfHeight) {
//the Discrollvable center reaches the DiscrollView center
int visibleGap = scrollViewHalfHeight - discrollvableAbsoluteTop;
discrollvable.onDiscrollve(clamp(visibleGap / (float) discrollvableHeight, 0.0f, 1.0f));
} else {
discrollvable.onResetDiscrollve();
}
}
}
}
}