
public class SlideUpLayout extends ViewGroup {
private View mUpView;
private View mDownView;
private View mSlideView;
//private RecyclerView mListView;
private ViewDragHelper mHelper;
//上下滑的程度,0表示在upView,1表示在downView
private float mSlidePercent = 0;
private boolean mInLayout = false;
private boolean mFirstLayout = true;
public SlideUpLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if(child == mUpView){
return Math.max(- mUpView.getMeasuredHeight() + mSlideView.getMeasuredHeight(), Math.min(top, 0));
}else if(child == mDownView){
return Math.max(mSlideView.getMeasuredHeight(), Math.min(top, mUpView.getMeasuredHeight()));
}else if(child == mSlideView){
return Math.max(- mSlideView.getMeasuredHeight(), Math.min(top, 0));
}
return 0;
}
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mUpView || child == mDownView;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(releasedChild == mUpView){
int upViewHeight = mUpView.getMeasuredHeight();
int slideViewHeight = mSlideView.getMeasuredHeight();
float offset = (upViewHeight + releasedChild.getTop() - slideViewHeight) * 1.0f / (upViewHeight - slideViewHeight);
mHelper.settleCapturedViewAt(releasedChild.getLeft(), yvel > 0 || yvel == 0 && offset > 0.5f ? 0 : -upViewHeight + slideViewHeight);
invalidate();
}else if(releasedChild == mDownView){
int downViewHeight = mDownView.getMeasuredHeight();
int slideViewHeight = mSlideView.getMeasuredHeight();
float offset = (releasedChild.getTop() - slideViewHeight) * 1.0f / downViewHeight;
mHelper.settleCapturedViewAt(releasedChild.getLeft(), yvel > 0 || yvel == 0 && offset > 0.5f ? mUpView.getMeasuredHeight() : slideViewHeight);
invalidate();
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if(changedView == mUpView){
mDownView.setTop(top + mUpView.getMeasuredHeight());
mSlidePercent = (float) (-top) / mDownView.getMeasuredHeight();
if(mSlidePercent > 0.9f){
mSlideView.setTop(-mSlideView.getMeasuredHeight() + (int)((mSlidePercent - 0.9f)/(1-0.9) * mSlideView.getMeasuredHeight()));
}else{
mSlideView.setTop(-mSlideView.getMeasuredHeight());
}
requestLayout();
if(mOnSlideListener != null){
mOnSlideListener.onSlide(mSlidePercent);
}
}else if(changedView == mDownView){
mUpView.setTop(top - mUpView.getMeasuredHeight());
mSlidePercent = (float) (mUpView.getMeasuredHeight() - top) / mDownView.getMeasuredHeight();
if(mSlidePercent > 0.9f){
mSlideView.setTop(-mSlideView.getMeasuredHeight() + (int)((mSlidePercent - 0.9f)/(1-0.9) * mSlideView.getMeasuredHeight()));
}else{
mSlideView.setTop(-mSlideView.getMeasuredHeight());
}
requestLayout();
if(mOnSlideListener != null){
mOnSlideListener.onSlide(mSlidePercent);
}
}
}
@Override
public int getViewVerticalDragRange(View child) {
return child == mUpView ? mUpView.getMeasuredHeight() - mSlideView.getMeasuredHeight() :
child == mDownView ? mDownView.getMeasuredHeight() :
child == mSlideView ? mSlideView.getMeasuredHeight() : 0;
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
mUpView = getChildAt(0);
mDownView = getChildAt(1);
mSlideView = getChildAt(2);
//mListView = mDownView.findViewById(R.id.rv_select_course);
//up
MarginLayoutParams lp = (MarginLayoutParams) mUpView.getLayoutParams();
int widthSpec = MeasureSpec.makeMeasureSpec(
widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(
heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
mUpView.measure(widthSpec, heightSpec);
//slide
lp = (MarginLayoutParams) mSlideView.getLayoutParams();
widthSpec = getChildMeasureSpec(widthMeasureSpec,
lp.leftMargin + lp.rightMargin, lp.width);
heightSpec = getChildMeasureSpec(heightMeasureSpec,
lp.topMargin + lp.bottomMargin, lp.height);
mSlideView.measure(widthSpec, heightSpec);
//down
lp = (MarginLayoutParams) mDownView.getLayoutParams();
widthSpec = MeasureSpec.makeMeasureSpec(
widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
heightSpec = MeasureSpec.makeMeasureSpec(
heightSize - lp.topMargin - lp.bottomMargin - mSlideView.getMeasuredHeight(), MeasureSpec.EXACTLY);
mDownView.measure(widthSpec, heightSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mInLayout = true;
MarginLayoutParams lp = (MarginLayoutParams) mUpView.getLayoutParams();
int upTop;
if(mFirstLayout){
upTop = lp.topMargin;
}else{
upTop = mUpView.getTop();
}
mUpView.layout(lp.leftMargin, upTop,
lp.leftMargin + mUpView.getMeasuredWidth(),
upTop + mUpView.getMeasuredHeight());
lp = (MarginLayoutParams) mDownView.getLayoutParams();
int downTop;
if(mFirstLayout){
downTop = mUpView.getMeasuredHeight() + lp.topMargin;
}else{
downTop = mDownView.getTop();
}
mDownView.layout(lp.leftMargin, downTop,
lp.leftMargin + mDownView.getMeasuredWidth(),
downTop + mDownView.getMeasuredHeight());
lp = (MarginLayoutParams) mSlideView.getLayoutParams();
int slideTop;
if(mFirstLayout){
slideTop = - mSlideView.getMeasuredHeight() + lp.topMargin;
}else{
slideTop = mSlideView.getTop();
}
mSlideView.layout(lp.leftMargin, slideTop,
lp.leftMargin + mSlideView.getMeasuredWidth(),
slideTop + mSlideView.getMeasuredHeight());
mInLayout = false;
mFirstLayout = false;
}
@Override
public void requestLayout() {
if(!mInLayout) {
super.requestLayout();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mFirstLayout = true;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mFirstLayout = true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//return mHelper.shouldInterceptTouchEvent(ev);
//方法一:还需要根据坐标是否是mListView的范围,进行控制。该方式更直接,理论上效率会更高
/*if(isInViewArea(mListView, ev.getRawX(), ev.getRawY()) && mListView.canScrollVertically(-1)){
//listview可以滚动则交给子view决定
return false;
}else{
//已经滚动到顶部,则交给ViewDragHelper处理
return mHelper.shouldInterceptTouchEvent(ev);
}*/
//方法二:把以上的处理改为更一般的情况,递归获取每个子view进行判断处理
if(hasViewCanScrollUp(mDownView, ev.getRawX(), ev.getRawY())){
return false;
}else{
return mHelper.shouldInterceptTouchEvent(ev);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (mHelper.continueSettling(true)) {
invalidate();
}
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
/**
* 判断指定点所在的view是否未到顶可以继续上划,如果view为ViewGroup的话还得递归往子view判断进去
*/
private boolean hasViewCanScrollUp(View view, float x, float y){
if(view instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup) view;
for(int i=0; i<viewGroup.getChildCount(); i++){
View child = viewGroup.getChildAt(i);
if(hasViewCanScrollUp(child, x, y)){
return true;
}
}
return isInViewArea(view, x, y) && view.canScrollVertically(-1);
}else{
return isInViewArea(view, x, y) && view.canScrollVertically(-1);
}
}
/**
* 判断坐标是否在view区域内
*/
private boolean isInViewArea(View view, float x, float y){
int[] local = new int[2];
view.getLocationOnScreen(local);
return x > local[0] && x < local[0]+view.getMeasuredWidth() && y > local[1] && y < local[1]+view.getMeasuredHeight();
}
public float getSlidePercent(){
return mSlidePercent;
}
public boolean isSlideUp(){
return floatCompare(mSlidePercent, 0);
}
public boolean isSlideDown(){
return floatCompare(mSlidePercent, 1);
}
public void slideToDown(){
mHelper.smoothSlideViewTo(mDownView, mDownView.getLeft(), mSlideView.getMeasuredHeight());
invalidate();
}
public void slideToUp(){
mHelper.smoothSlideViewTo(mUpView, mUpView.getLeft(), 0);
invalidate();
}
private OnSlideListener mOnSlideListener = null;
/**
* 外部可设置监听slide的位置回调
*/
public void setOnSlideListener(OnSlideListener listener) {
mOnSlideListener = listener;
}
public interface OnSlideListener{
/**
* slide回调
* @param percent 取值区间[0, 1],0代表滑到最顶部,1代表滑到最底部
*/
void onSlide(float percent);
}
private boolean floatCompare(float f1, float f2){
return Math.abs(f1 - f2) < Float.MIN_VALUE;
}
}
xml
<com.example.glidedemo.dragHelper.SlideUpLayout
android:id="@+id/slide_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<FrameLayout
android:id="@+id/slide_layout_up"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1f00ffff">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="up"/>
</FrameLayout>
<FrameLayout
android:id="@+id/slide_layout_down"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1fffff00">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="down"/>
<ListView
android:id="@+id/slide_layout_down_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
<FrameLayout
android:id="@+id/slide_layout_slide"
android:layout_width="match_parent"
android:layout_height="100dp"
android:clickable="true"
android:background="#1fff00ff">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="slide"/>
</FrameLayout>
</com.example.glidedemo.dragHelper.SlideUpLayout>
网友评论