package czd.lib.view.scrollview.pull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.*; import czd.lib.R; import czd.lib.view.ViewUtil; import java.lang.ref.WeakReference; public class PullScrollView extends ScrollView { private static final int DEFAULT_ADVANCE_GAP = 150; private int height = 0; private int lastY = 0; private int t = 0; private boolean onTop = true, onBottom = true, onStop = true; private int advance_gap = DEFAULT_ADVANCE_GAP; private int tall = 0; private static final int RATIO = 3; private PullScrollListener listener = null; private ScrollHandler handler = new ScrollHandler(this); private static final int STATE_RELEASETOREFRESH = 0; private static final int STATE_PULLTOREFRESH = 1; private static final int STATE_REFRESHING = 2; private static final int STATE_DONE = 3; private Context _context; private LinearLayout header; private String _headerStatePull; private String _headerStateRelease; private String _headerStateLoading; private String _headerStateDone; private Drawable _headerBackground; private int _headerTextColor; private int _headerInfoVisible; private ImageView header_iv; private ProgressBar header_pb; private TextView header_stv; private TextView header_itv; private Animation anim_up; private Animation anim_down; private int state = STATE_DONE; private boolean refreshable = false; private boolean pulling = true; private int startY = 0; private boolean isback = false; private int m = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); private int header_height = 0; private LinearLayout top_container; private LinearLayout container; public PullScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } public PullScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public PullScrollView(Context context) { super(context); init(context, null); } private void init(Context context, AttributeSet attrs) { this._context = context; if (attrs != null) { TypedArray tattrs = this._context.obtainStyledAttributes(attrs, R.styleable.PullView); this._headerStatePull = tattrs.getString(0); this._headerStateRelease = tattrs.getString(1); this._headerStateLoading = tattrs.getString(2); this._headerStateDone = tattrs.getString(3); this._headerBackground = tattrs.getDrawable(4); this._headerTextColor = tattrs.getInt(5, android.R.color.black); this._headerInfoVisible = tattrs.getInt(6, View.VISIBLE); tattrs.recycle(); } this.header = (LinearLayout)ViewUtil.viewById(this._context, R.layout.common_pull_header); this.header_iv = (ImageView)this.header.findViewById(R.id.common_pull_header_image); this.header_pb = (ProgressBar)this.header.findViewById(R.id.common_pull_header_progress); this.header_stv = (TextView)this.header.findViewById(R.id.common_pull_header_state); this.header_itv = (TextView)this.header.findViewById(R.id.common_pull_header_info); this.header.setBackgroundDrawable(this._headerBackground); this.header_iv.setVisibility(View.VISIBLE); this.header_pb.setVisibility(View.VISIBLE); this.header_stv.setTextColor(_headerTextColor); this.header_itv.setTextColor(_headerTextColor); this.header_itv.setVisibility(_headerInfoVisible); header.measure(m, m); header_height = header.getMeasuredHeight(); this.header.setPadding(0, -1 * header_height, 0, 0); header.invalidate(); top_container = new LinearLayout(_context); top_container.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); top_container.setOrientation(LinearLayout.VERTICAL); top_container.setGravity(Gravity.CENTER_HORIZONTAL); this.addView(top_container); top_container.addView(this.header); container = new LinearLayout(context); container.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); container.setOrientation(LinearLayout.VERTICAL); container.setGravity(Gravity.CENTER_HORIZONTAL); top_container.addView(container); this.anim_up = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); this.anim_up.setInterpolator(new LinearInterpolator()); this.anim_up.setDuration(250); this.anim_up.setFillAfter(true); this.anim_down = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); this.anim_down.setInterpolator(new LinearInterpolator()); this.anim_down.setDuration(200); this.anim_down.setFillAfter(true); } @Override public final boolean onInterceptTouchEvent(MotionEvent event) { if (refreshable) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (state != STATE_REFRESHING && t == 0) { startY = (int)event.getY(); } } } return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { if (refreshable) { switch (event.getAction()) { case MotionEvent.ACTION_UP: if (startY != 0) { if (state != STATE_REFRESHING) { switch (state) { case STATE_PULLTOREFRESH: state = STATE_DONE; resetHeader(); break; case STATE_RELEASETOREFRESH: state = STATE_REFRESHING; resetHeader(); onRefresh(); break; default: break; } } isback = false; startY = 0; } pulling = false; break; case MotionEvent.ACTION_MOVE: onStop = false; if (state != STATE_REFRESHING && t == 0 && startY > 0) { int tmpY = (int)event.getY(); switch (state) { case STATE_RELEASETOREFRESH: pulling = true; scrollTo(0, 0); if (tmpY < startY) { state = STATE_DONE; resetHeader(); } else if ((tmpY - startY) / RATIO < header_height) { state = STATE_PULLTOREFRESH; resetHeader(); } break; case STATE_PULLTOREFRESH: pulling = true; scrollTo(0, 0); if (tmpY <= startY) { state = STATE_DONE; resetHeader(); } else if ((tmpY - startY) / RATIO >= header_height) { state = STATE_RELEASETOREFRESH; isback = true; resetHeader(); } break; case STATE_DONE: if (tmpY >= startY) { state = STATE_PULLTOREFRESH; resetHeader(); pulling = true; } else { startY = 0; } default: break; } if (pulling && (state == STATE_PULLTOREFRESH || state == STATE_RELEASETOREFRESH)) { header.setPadding(0, (tmpY - startY) / RATIO - header_height, 0, 0); } } break; default: break; } } return super.onTouchEvent(event); } public void onRefreshComplete() { state = STATE_DONE; resetHeader(); invalidate(); scrollTo(0, 0); } private void onRefresh() { if (listener != null) { listener.onRefresh(); } } public void setInfo(String text) { this.header_itv.setText(text); if (this.header_itv.getVisibility() != View.VISIBLE) { this.header_itv.setVisibility(View.VISIBLE); } } public void setInfoVisible(int visible) { this.header_itv.setVisibility(visible); } private void resetHeader() { switch (state) { case STATE_DONE: this.header_stv.setText(_headerStateDone); this.header.setPadding(0, -1 * header_height, 0, 0); this.header_pb.setVisibility(View.GONE); this.header_iv.clearAnimation(); this.header_iv.setVisibility(View.VISIBLE); break; case STATE_PULLTOREFRESH: this.header_stv.setText(_headerStatePull); this.header_pb.setVisibility(View.GONE); this.header_iv.setVisibility(View.VISIBLE); if (isback) { isback = false; this.header_iv.clearAnimation(); this.header_iv.startAnimation(anim_down); } break; case STATE_RELEASETOREFRESH: this.header_stv.setText(_headerStateRelease); this.header_pb.setVisibility(View.GONE); this.header_iv.setVisibility(View.VISIBLE); this.header_iv.clearAnimation(); this.header_iv.startAnimation(anim_up); break; case STATE_REFRESHING: this.header.setPadding(0, 0, 0, 0); this.header_stv.setText(_headerStateLoading); this.header_iv.clearAnimation(); this.header_iv.setVisibility(View.GONE); this.header_pb.setVisibility(View.VISIBLE); break; default: break; } } @Override public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) { if (this.container == null) { super.addView(child, index, params); } else { this.container.addView(child, index, params); } } @Override public void addView(View child, int index) { if (this.container == null) { super.addView(child, index); } else { this.container.addView(child, index); } } @Override public void addView(View child, android.view.ViewGroup.LayoutParams params) { if (this.container == null) { super.addView(child, params); } else { this.container.addView(child, params); } } @Override public void addView(View child) { if (this.container == null) { super.addView(child); } else { this.container.addView(child); } } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); this.t = t; if (pulling) { scrollTo(0, 0); } if (listener != null) { listener.onScroll(this, l, t, oldl, oldt); } handler.sendMessageDelayed(handler.obtainMessage(0, t, oldt), 100); whereami(t); } private void whereami(int t) { if (t <= advance_gap) { if (!onTop && listener != null) { onTop = true; listener.onTop(t); } } else { if (onTop && listener != null) { onTop = false; listener.outTop(t); } } if (tall - t - height <= advance_gap) { if (!onBottom && listener != null) { onBottom = true; listener.onBottom(t); } } else { if (onBottom && listener != null) { onBottom = false; listener.outBottom(t); } } } public void setAdvanceGap(int gap) { this.advance_gap = gap; } public int getAdvanceGap() { return this.advance_gap; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (height == 0) { height = getHeight(); advance_gap = Math.min(advance_gap, height / 5); } else { tall = getChildAt(0).getHeight(); whereami(lastY); } } public void setScrollPullListener(PullScrollListener listener) { this.listener = listener; this.refreshable = true; } private static class ScrollHandler extends Handler { WeakReference<PullScrollView> scroll_weak; public ScrollHandler(PullScrollView scroll) { this.scroll_weak = new WeakReference<PullScrollView>(scroll); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); PullScrollView scroll = scroll_weak.get(); if (scroll != null) { if (!scroll.onStop && msg.arg1 == scroll.lastY) { scroll.onStop = true; if (scroll.listener != null) { scroll.listener.onStop(msg.arg1); } } scroll.lastY = scroll.getScrollY(); } } } }