package com.example.jingbin.cloudreader.view.sliding;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import com.example.jingbin.cloudreader.R;
/**
* Created by Linhh on 16/4/12.
*/
public class SlidingLayout extends FrameLayout{
private int mTouchSlop;//系统允许最小的滑动判断值
private int mBackgroundViewLayoutId = 0;
private View mBackgroundView;//背景View
private View mTargetView;//正面View
private boolean mIsBeingDragged;
private float mInitialDownY;
private float mInitialMotionY;
private float mLastMotionY;
private int mActivePointerId = INVALID_POINTER;
private float mSlidingOffset = 0.5F;//滑动阻力系数
private static final int RESET_DURATION = 200;
private static final int SMOOTH_DURATION = 1000;
public static final int SLIDING_MODE_BOTH = 0;
public static final int SLIDING_MODE_TOP = 1;
public static final int SLIDING_MODE_BOTTOM = 2;
public static final int SLIDING_POINTER_MODE_ONE = 0;
public static final int SLIDING_POINTER_MODE_MORE = 1;
private int mSlidingMode = SLIDING_MODE_BOTH;
private int mSlidingPointerMode = SLIDING_POINTER_MODE_MORE;
private static final int INVALID_POINTER = -1;
private SlidingListener mSlidingListener;
public static final int STATE_SLIDING = 2;
public static final int STATE_IDLE = 1;
private int mSlidingTopMaxDistance = SLIDING_DISTANCE_UNDEFINED;
public static final int SLIDING_DISTANCE_UNDEFINED = -1;
private OnTouchListener mDelegateTouchListener;
public interface SlidingListener{
//不能操作繁重的任务在这里
public void onSlidingOffset(View view, float delta);
public void onSlidingStateChange(View view, int state);
public void onSlidingChangePointer(View view, int pointerId);
}
public SlidingLayout(Context context) {
this(context, null);
}
public SlidingLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingLayout);
mBackgroundViewLayoutId = a.getResourceId(R.styleable.SlidingLayout_background_view, mBackgroundViewLayoutId);
mSlidingMode = a.getInteger(R.styleable.SlidingLayout_sliding_mode,SLIDING_MODE_BOTH);
mSlidingPointerMode = a.getInteger(R.styleable.SlidingLayout_sliding_pointer_mode,SLIDING_POINTER_MODE_MORE);
mSlidingTopMaxDistance = a.getDimensionPixelSize(R.styleable.SlidingLayout_top_max,SLIDING_DISTANCE_UNDEFINED);
a.recycle();
if(mBackgroundViewLayoutId != 0){
View view = View.inflate(getContext(), mBackgroundViewLayoutId, null);
setBackgroundView(view);
}
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public void setBackgroundView(View view){
if(mBackgroundView != null){
this.removeView(mBackgroundView);
}
mBackgroundView = view;
this.addView(view, 0);
}
public View getBackgroundView(){
return this.mBackgroundView;
}
public void setSlidingDistance(int distance){
this.mSlidingTopMaxDistance = distance;
}
public int setSlidingDistance(){
return this.mSlidingTopMaxDistance;
}
/**
* 获得滑动幅度
* @return
*/
public float getSlidingOffset(){
return this.mSlidingOffset;
}
/**
* 设置滑动幅度
* @param slidingOffset
*/
public void setSlidingOffset(float slidingOffset){
this.mSlidingOffset = slidingOffset;
}
public void setSlidingListener(SlidingListener slidingListener){
this.mSlidingListener = slidingListener;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//实际上整个layout只能存在一个背景和一个前景才有用途
// if(getChildCount() > 2){
//
// }
if (getChildCount() == 0) {
return;
}
if (mTargetView == null) {
ensureTarget();
}
if (mTargetView == null) {
return;
}
}
private void ensureTarget() {
if (mTargetView == null) {
mTargetView = getChildAt(getChildCount() - 1);
}
}
public void setTargetView(View view){
if(mTargetView != null){
this.removeView(mTargetView);
}
mTargetView = view;
this.addView(view);
}
@Override
public void setOnTouchListener(OnTouchListener l) {
// super.setOnTouchListener(l);
mDelegateTouchListener = l;
}
public View getTargetView(){
return this.mTargetView;
}
public float getSlidingDistance(){
return getInstrument().getTranslationY(getTargetView());
}
public Instrument getInstrument(){
return Instrument.getInstance();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
final int action = MotionEventCompat.getActionMasked(ev);
//判断拦截
switch (action) {
case MotionEvent.ACTION_DOWN:
// Log.i("onInterceptTouchEvent", "down");
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialDownY = getMotionEventY(ev, mActivePointerId);
if (initialDownY == -1) {
return false;
}
mInitialDownY = initialDownY;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
// Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
return false;
}
final float y = getMotionEventY(ev, mActivePointerId);
if (y == -1) {
return false;
}
if(y > mInitialDownY) {
//判断是否是上拉操作
final float yDiff = y - mInitialDownY;
if (yDiff > mTouchSlop && !mIsBeingDragged && !canChildScrollUp()) {
mInitialMotionY = mInitialDownY + mTouchSlop;
mLastMotionY = mInitialMotionY;
mIsBeingDragged = true;
}
}else if(y < mInitialDownY){
//判断是否是下拉操作
final float yDiff = mInitialDownY - y;
if (yDiff > mTouchSlop && !mIsBeingDragged && !canChildScrollDown()) {
mInitialMotionY = mInitialDownY + mTouchSlop;
mLastMotionY = mInitialMotionY;
mIsBeingDragged = true;
}
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Log.i("onInterceptTouchEvent", "up");
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
break;
}
return mIsBeingDragged;
}
private float getMotionEventY(MotionEvent ev, int activePointerId) {
final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
if (index < 0) {
return -1;
}
return MotionEventCompat.getY(ev, index);
}
/**
* 判断View是否可以上拉
* @return canChildScrollUp
*/
public boolean canChildScrollUp() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (mTargetView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTargetView;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return ViewCompat.canScrollVertically(mTargetView, -1) || mTargetView.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTargetView, -1);
}
}
/**
* 判断View是否可以下拉
* @return canChildScrollDown
*/
public boolean canChildScrollDown() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (mTargetView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTargetView;
return absListView.getChildCount() > 0 && absListView.getAdapter() != null
&& (absListView.getLastVisiblePosition() < absListView.getAdapter().getCount() - 1 || absListView.getChildAt(absListView.getChildCount() - 1)
.getBottom() < absListView.getPaddingBottom());
} else {
return ViewCompat.canScrollVertically(mTargetView, 1) || mTargetView.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTargetView, 1);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(mDelegateTouchListener != null && mDelegateTouchListener.onTouch(this,event)){
return true;
}
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// Log.i("onTouchEvent", "down");
break;
case MotionEvent.ACTION_MOVE:
float delta = 0.0f;
float movemment = 0.0f;
if(mSlidingPointerMode == SLIDING_POINTER_MODE_MORE) {
//homhom:it's different betweenn more than one pointer
int activePointerId = MotionEventCompat.getPointerId(event, event.getPointerCount() - 1);
if (mActivePointerId != activePointerId) {
//change pointer
// Log.i("onTouchEvent","change point");
mActivePointerId = activePointerId;
mInitialDownY = getMotionEventY(event, mActivePointerId);
mInitialMotionY = mInitialDownY + mTouchSlop;
mLastMotionY = mInitialMotionY;
if (mSlidingListener != null) {
mSlidingListener.onSlidingChangePointer(mTargetView, activePointerId);
}
}
//pointer delta
// delta = getInstrument().getTranslationY(mTargetView)
// + ((getMotionEventY(event, mActivePointerId) - mLastMotionY))
// / mSlidingOffset;
delta = getMotionEventY(event, mActivePointerId) - mLastMotionY;
//滑动阻力计算
// float tempOffset = getInstrument().getTranslationY(mTargetView)
// + delta;
float tempOffset = 1 - (Math.abs(getInstrument().getTranslationY(mTargetView)
+ delta) / mTargetView.getMeasuredHeight());
delta = getInstrument().getTranslationY(mTargetView)
+ delta * mSlidingOffset * tempOffset;
mLastMotionY = getMotionEventY(event, mActivePointerId);
//used for judge which side move to
movemment = getMotionEventY(event, mActivePointerId) - mInitialMotionY;
}else {
float tempOffset = 1 - Math.abs(getInstrument().getTranslationY(mTargetView) / mTargetView.getMeasuredHeight());
delta = (event.getY() - mInitialMotionY) * mSlidingOffset * tempOffset;
//used for judge which side move to
movemment = event.getY() - mInitialMotionY;
}
float distance = getSlidingDistance();
switch (mSlidingMode){
case SLIDING_MODE_BOTH:
getInstrument().slidingByDelta(mTargetView, delta);
break;
case SLIDING_MODE_TOP:
if(movemment >= 0 || distance > 0){
//向下滑动
if(delta < 0 ){
//如果还往上滑,就让它归零
delta = 0;
}
if(mSlidingTopMaxDistance == SLIDING_DISTANCE_UNDEFINED || delta < mSlidingTopMaxDistance){
//滑动范围内 for todo
}else{
//超过滑动范围
delta = mSlidingTopMaxDistance;
}
getInstrument().slidingByDelta(mTargetView, delta);
}
break;
case SLIDING_MODE_BOTTOM:
if(movemment <= 0 || distance < 0){
//向上滑动
if(delta > 0 ){
//如果还往下滑,就让它归零
delta = 0;
}
getInstrument().slidingByDelta(mTargetView, delta);
}
break;
}
if(mSlidingListener != null){
mSlidingListener.onSlidingStateChange(this, STATE_SLIDING);
mSlidingListener.onSlidingOffset(this,delta);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Log.i("onTouchEvent", "up");
if(mSlidingListener != null){
mSlidingListener.onSlidingStateChange(this, STATE_IDLE);
}
getInstrument().reset(mTargetView,RESET_DURATION);
break;
}
//消费触摸
return true;
}
public void setSlidingMode(int mode){
mSlidingMode = mode;
}
public int getSlidingMode(){
return mSlidingMode;
}
public void smoothScrollTo(float y){
getInstrument().smoothTo(mTargetView, y, SMOOTH_DURATION);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(mTargetView != null){
mTargetView.clearAnimation();
}
mSlidingMode = 0;
mTargetView = null;
mBackgroundView = null;
mSlidingListener = null;
}
}