京东列表页控件:主要分为两部分,头部和内容部分
1.当向上滑动时,头部跟着滑动,头部消失的时候,内容部分开始滑动,
2.当向下滑动时,如果内容页正处于最顶部,则头部也跟着滑动
3.当内容部分处于中间部分时,向下滑显示头部,向上滑关闭头部
下面部分虽然完成了该部分功能,但是顶部消失过于卡顿,导致情况使用不能像想像的那样,主要是viewGroup如果一旦拦截事件,那么viewGroup里面的view将无法在获取事件,所以只能在dispatchTouchEvent里面进行分发,由于事件触发慢,导致卡顿
public class PullToView extends LinearLayout {
//顶部视图控件拉动状态
//隐藏状态
private static final int HIDE = 1;
//拖动
private static final int PULL = 2;
//展示状态
private static final int SHOW = 3;
private Context mContext;
//头部所处状态
private int mHeaderState;
//内容部分状态
private AdapterView<?> mAdapterView;
private View mHeaderView;
private int mHeaderViewHeight;
private View mContentView;
//ListView
private ListView mListView;
//GridView
private GridView mGridView;
private int mLastMotionY;
private int mIndexMotionY;
public PullToView(Context context) {
super(context);
mContext = context;
}
public PullToView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public PullToView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
init();
}
private void init() {
int count = getChildCount();
if (count != 2) {
throw new IllegalArgumentException("The Layout must contains two views");
}
//获取第一个控件
mHeaderView = getChildAt(0);
measureView(mHeaderView);
//获取第一个控件高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mContentView = getChildAt(1);
if (mContentView instanceof ViewGroup) {
initAdapterView(((ViewGroup) mContentView));
}
setmAdapterView(true);
mHeaderState = SHOW;
}
/**
* 计算控件的高宽
*
* @param view
*/
private void measureView(View view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) {
params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.WRAP_CONTENT);
}
// 获取宽度(外间距,内间距,view的宽度)
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, params.width);
int pHeight = params.height;
int childHeightSpec;
// MeasureSpec 它有三种模式:
// UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小
// EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小
// AT_MOST(至多),子元素至多达到指定大小的值
if (pHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(childWidthSpec, childHeightSpec);
}
private void initAdapterView(ViewGroup viewGroup) {
if (viewGroup == null) {
return;
}
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view instanceof ListView) {
mListView = ((ListView) view);
} else if (view instanceof GridView) {
mGridView = ((GridView) view);
} else if (view instanceof ViewGroup) {
initAdapterView(((ViewGroup) view));
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = y;
mIndexMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
int distance = y - mIndexMotionY;
changingHeaderViewTopMargin(deltaY, distance);
mLastMotionY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int indexY = y - mIndexMotionY;
if (indexY <= 0) {
changingHeaderViewTopMargin(0, indexY);
}
break;
}
return super.dispatchTouchEvent(e);
}
/**
* 改变头部距离
*
* @param delta
* @return
*/
private void changingHeaderViewTopMargin(int delta, int distance) {
//获取第一个控件高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
//获取头部的布局属性
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
int topMargin = params.topMargin;
if (delta <= 0) {//上划
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
// 如果AdapterView当前第一个可见的位置是数据的第一个,且处于AdapterView顶部
if (mAdapterView.getFirstVisiblePosition() == 0&& mHeaderState != HIDE) {
mAdapterView.getChildAt(0).setTop(0);
mHeaderState = PULL;
params.topMargin = params.topMargin + delta;
} else if (distance < -200 && mHeaderState == SHOW) {//当
mHeaderState = HIDE;
params.topMargin = -mHeaderViewHeight;
} else if (mHeaderState == PULL) {
mHeaderState = HIDE;
params.topMargin = -mHeaderViewHeight;
}
if (mHeaderViewHeight < Math.abs(params.topMargin)) {
mHeaderState = HIDE;
params.topMargin = -mHeaderViewHeight;
}
} else if (delta > 0) {
//如果第一个控件展示出了
int top = mAdapterView.getChildAt(0).getTop();
if (mAdapterView.getFirstVisiblePosition()==0&&top!=0){
}else if (mAdapterView.getFirstVisiblePosition() == 0 && mHeaderState != SHOW) {
params.topMargin = params.topMargin + delta;
if (mHeaderState == PULL) {
mAdapterView.getChildAt(0).setTop(0);
}
if (params.topMargin >= 0) {
params.topMargin = 0;
mHeaderState = SHOW;
} else if (params.topMargin < 0 && Math.abs(params.topMargin) < mHeaderViewHeight) {
mHeaderState = PULL;
} else {
params.topMargin = -mHeaderViewHeight;
mHeaderState = HIDE;
}
} else if (distance > 200 && mHeaderState == HIDE) {
if (getAdapterScrollY() > 1000) {
params.topMargin = 0;
mHeaderState = SHOW;
}
}
}
if (topMargin != params.topMargin) {
mHeaderView.setLayoutParams(params);
invalidate();
}
}
private void setmAdapterView(boolean isListView) {
if (isListView) {
mAdapterView = mListView;
} else {
mAdapterView = mGridView;
}
}
public int getAdapterScrollY() {
View c = mAdapterView.getChildAt(0);
if (c == null) {
return 0;
}
int firstVisiblePosition = mListView.getFirstVisiblePosition();
int top = c.getTop();
return -top + firstVisiblePosition * c.getHeight();
}
}