package net.avenwu.support.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;
import android.widget.TextView;
/**
* Created by Chaobin Wu on 2014/10/10.
*/
public class RefreshLayout extends LinearLayout {
private FrameLayout mHeaderLayout;
private ListView mListView;
private float mX;
private float mY;
private Scroller mScroller;
private int mPaddingTop = 0;
private int MAX_OVER_SCROLL_HEIGHT;
boolean isSliding = false;
int touchSlop;
public RefreshLayout(Context context) {
this(context, null);
}
public RefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
touchSlop = configuration.getScaledTouchSlop();
mScroller = new Scroller(context, new BounceInterpolator());
mHeaderLayout = new FrameLayout(context);
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
mHeaderLayout.setLayoutParams(layoutParams);
mHeaderLayout.setBackgroundColor(getResources().getColor(android.R.color.holo_orange_dark));
addView(mHeaderLayout);
//TODO add text header
TextView textView = new TextView(context);
textView.setText("This is header layout content");
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics());
LayoutParams textParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, height);
textParams.gravity = Gravity.CENTER;
textView.setLayoutParams(textParams);
addHeaderChild(textView);
mPaddingTop -= height;
setPadding(0, mPaddingTop, 0, 0);
MAX_OVER_SCROLL_HEIGHT = 2 * height;
mListView = new ListView(context, attrs);
mListView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(mListView);
}
public void addHeaderChild(View view) {
if (mHeaderLayout != null) {
mHeaderLayout.addView(view);
requestLayout();
}
}
public void setAdapter(ListAdapter adapter) {
mListView.setAdapter(adapter);
if (adapter instanceof BaseAdapter) {
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private boolean isFirstItemVisible() {
if (null == mListView.getAdapter() || mListView.getAdapter().isEmpty()) {
return true;
} else {
return mListView.getFirstVisiblePosition() == 0;
// if (mListView.getFirstVisiblePosition() <= 1) {
// final View firstVisibleChild = mListView.getChildAt(0);
// if (firstVisibleChild != null) {
// return firstVisibleChild.getTop() >= mListView.getTop();
// }
// }
}
// return false;
}
private boolean isLastItemVisible() {
if (null == mListView.getAdapter() || mListView.getAdapter().isEmpty()) {
return true;
} else {
return mListView.getLastVisiblePosition() == mListView.getAdapter().getCount() - 1;
}
}
public ListView getListView() {
return mListView;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (action != MotionEvent.ACTION_DOWN && isSliding) return true;
switch (action) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
isSliding = false;
break;
case MotionEvent.ACTION_MOVE:
Log.d("onInterceptTouchEvent", "scrollY=" + getScrollY() + ", " + ev.toString());
if (ev.getY() > mY) {
if (isFirstItemVisible() && Math.abs(ev.getY() - mY) > touchSlop) {
mY = ev.getY();
isSliding = true;
}
} else {
if (getScrollY() < 0) {
mY = ev.getY();
isSliding = true;
}
}
break;
case MotionEvent.ACTION_DOWN:
isSliding = false;
mY = ev.getY();
break;
}
return isSliding || super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("RefreshLayout", event.toString());
boolean eatTouchEvent = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mY = event.getY();
eatTouchEvent = true;
break;
case MotionEvent.ACTION_MOVE:
if (isSliding) {// move down:getScrollY becomes smaller
float y = event.getY();
float diff = y - mY;
mY = event.getY();
if (diff != 0) {
float distance = getScrollY() - diff;
Log.d("RefreshLayout", "move, distance=" + distance + ", getScrollY=" + getScrollY() + ", diff=" + diff);
if (distance > 0) {
distance = Math.min(distance, MAX_OVER_SCROLL_HEIGHT);
} else {
distance = Math.max(distance, -MAX_OVER_SCROLL_HEIGHT);
}
scrollTo(getScrollX(), (int) distance);
}
eatTouchEvent = true;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (isSliding) {
isSliding = false;
mScroller.abortAnimation();
int currentY = getScrollY();
int desY = -currentY - MAX_OVER_SCROLL_HEIGHT / 2;
Log.d("RefreshLayout", "getScrollY=" + currentY + ", desY=" + desY);
//current y > 2/3 of max height, scroll to display the whole header
if (currentY + event.getY() - mY < -MAX_OVER_SCROLL_HEIGHT / 2) {
mScroller.startScroll(0, currentY, 0, desY);
} else {
mScroller.startScroll(0, currentY, 0, 0 - currentY);
}
invalidate();
eatTouchEvent = true;
}
break;
}
return eatTouchEvent || super.onTouchEvent(event);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int oldY = getScrollY();
int y = mScroller.getCurrY();
if (oldY != y) {
scrollTo(0, y);
}
invalidate();
} else {
mScroller.abortAnimation();
}
}
}