首页 > 代码库 > Android GridView 添加 HeadView

Android GridView 添加 HeadView

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