package com.example.test;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.GridView;
import android.widget.LinearLayout;
public class HeadGridView extends LinearLayout{
private static final String TAG = HeadGridView.class.getSimpleName();
private LinearLayout mHeadLayout;
private GridView mGridView;
private int mHeadHeight;
private boolean mIsBeingDragged;
private float mLastMotionY;
private float mTouchSlop;
private boolean mGridViewFocused = true;
private State mState = State.getDefaultMode();
public HeadGridView(Context context) {
this(context, null);
}
public HeadGridView(Context context, AttributeSet attrs) {
super(context, attrs);
initContentView(context);
}
private void initContentView(Context context) {
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
setOrientation(VERTICAL);
GridView gridView = new GridView(context);
LayoutParams gridParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
gridView.setNumColumns(2);
gridView.setLayoutParams(gridParams);
mGridView = gridView;
initGridView(gridView);
addView(gridView);
LinearLayout headLayout = new LinearLayout(context);
LayoutParams headParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
headLayout.setLayoutParams(headParams);
mHeadLayout = headLayout;
initHead(headLayout);
addView(headLayout,0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
int gridWidthSpec = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY);
int gridHeightSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY);
mGridView.measure(gridWidthSpec, gridHeightSpec);
setMeasuredDimension(parentWidth, parentHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mHeadHeight = getChildAt(0).getMeasuredHeight();
}
protected void initGridView(GridView gridView) {
}
protected void initHead(LinearLayout headLayout) {
}
@Override
public final boolean onInterceptTouchEvent(MotionEvent event) {
Log.d(TAG, "currentState = "+mState+" event = "+event.getAction()+" mIsBeingDragged = "+mIsBeingDragged);
final int action = event.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsBeingDragged = false;
return false;
}
if (mIsBeingDragged) {
return true;
}
switch (action) {
case MotionEvent.ACTION_MOVE:
final float y = event.getY();
final float absDiff = Math.abs(y - mLastMotionY);;
if (absDiff > mTouchSlop) {
mLastMotionY = y;
mIsBeingDragged = true;
if (mState != State.Head_Visible) {
mGridViewFocused = false;
}
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionY = event.getY();
mIsBeingDragged = false;
break;
default:
break;
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
pullEvent(event);
return true;
}
break;
}
case MotionEvent.ACTION_DOWN:
mLastMotionY = event.getY();
return true;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
mIsBeingDragged = false;
}
return true;
}
return false;
}
private void dispatchDownToGridView(MotionEvent e, float downY) {
int action = e.getAction();
MotionEvent de = MotionEvent.obtain(e);
de.setLocation(de.getX(), downY);
de.setAction(MotionEvent.ACTION_DOWN);
mGridView.dispatchTouchEvent(de);
e.setAction(action);
}
private void pullEvent(MotionEvent e) {
if (!mGridViewFocused) {
dispatchDownToGridView(e, mLastMotionY);
mGridViewFocused = true;
}
float y = e.getY();
int scrollValue = Math.round(mLastMotionY - y);
if (mState == State.Bottom_Top) {
if (scrollValue > 0){
if (mState == State.Bottom_Top) {
dispatchDownToGridView(e, mLastMotionY);
}
mGridView.dispatchTouchEvent(e);
mState = State.Bottom_Medium;
} else {
mState = State.Head_Visible;
}
}
if (mState.canHeadViewScroll()) {
int currentScrollY = getScrollY();
int maxScroolY = mHeadHeight - currentScrollY;
if (scrollValue > 0) { //向上滑动
if (maxScroolY < scrollValue) {
scrollValue = maxScroolY;
mState = State.Bottom_Top;
}
scrollBy(0, scrollValue);
} else { //向下滑动
if (currentScrollY > 0) {
if (currentScrollY < - scrollValue) {
scrollValue = - currentScrollY;
}
scrollBy(0, scrollValue);
}
}
} else {
mGridView.dispatchTouchEvent(e);
if (scrollValue < 0) {
int top = mGridView.getChildAt(0).getTop();
if (top == 0) {
mState = State.Bottom_Top;
}
}
}
mLastMotionY = y;
}
public static enum State{
Head_Visible(0x01),
Bottom_Top(0x02),
Bottom_Bottom(0x03),
Bottom_Medium(0x04);
private int id;
private State(int id) {
this.id = id;
}
public int getId(){
return id;
}
public static State getDefaultMode(){
return State.Head_Visible;
}
static State mapState(final int id) {
for (State value : State.values()) {
if (id == value.getId()) {
return value;
}
}
return getDefaultMode();
}
boolean canHeadViewScroll() {
return this == Head_Visible || this == Bottom_Top;
}
}
}
简单实现,目前还有些许问题,后续可参考:https://android.googlesource.com/platform/packages/apps/Gallery2/+/idea133/src/com/android/photos/views/HeaderGridView.java