自定义ViewGroup可折叠控件,类似CoordinatorLayout的效果

很多APP首页都是采用头部+列表的形式显示,但是首页的空间有限,这时就需要把头部布局折叠起来了,参考效果如下:

UI图

Google在Design库22+上面增加了CoordinatorLayout+Behavior可以实现这种效y果,但是实现起来相对比较复杂,下面我将用另一种方式实现。
工程已经上传aizuzi/FoldLayout,欢迎大家给start

效果图

效果图

实现步骤

第一步

我们自定义一个控件作为头部显示,实现复杂的折叠效果也是在该类完成,在这里我先放一个ImageView作为示例,具体代码如下

public class HeadView extends LinearLayout {
  private ImageView imageview;

  private float imagevViewY;
  public HeadView(Context context) {
    super(context);
    init();
  }

  public HeadView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  public HeadView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }

  /**
   * 初始化头部布局
   */
  private void init() {
    imageview = new ImageView(getContext());
    imageview.setImageResource(R.mipmap.ic_launcher);
    addView(imageview,new LayoutParams(UnitUtil.dp2px(getContext(),80),UnitUtil.dp2px(getContext(),80)));

    setGravity(Gravity.CENTER);
    setBackgroundColor(getResources().getColor(R.color.colorAccent));
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    if(imagevViewY ==0)
    imagevViewY = imageview.getY();
  }

  /**
   * 拖动时改变控件的位置
   * @param percentage
   */
  public void offsetPixel(float percentage) {
    int viewHeight = getHeight();
    imageview.setY(imagevViewY+(int) ((viewHeight-imageview.getHeight())/2*percentage));
  }
}
第二步

下面的列表用一个自定义的RecyclerView显示,如果是使用ListView或者GridView可以重写dispatchTouchEvent方法来分发触摸事件

public class CustomRecyclerView extends RecyclerView {
  private static final int TOUCH_IDLE = 0;
  private static final int TOUCH_DRAG_LAYOUT = 1;

  private int scrollMode;
  private float downY;
  private int minHeight;
  boolean isAtTop;

  public CustomRecyclerView(Context arg0) {
    super(arg0);
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    if (getTop() > minHeight) {
      getParent().requestDisallowInterceptTouchEvent(false);
      return false;
    }
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
      downY = ev.getRawY();
      isAtTop = isReadyForPullStart();
      scrollMode = TOUCH_IDLE;
      getParent().requestDisallowInterceptTouchEvent(true);
    } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
      if (scrollMode == TOUCH_IDLE) {
        float yDistance = Math.abs(downY - ev.getRawY());
        if (yDistance > 0) {
          scrollMode = TOUCH_DRAG_LAYOUT;
          if (downY < ev.getRawY() && isAtTop) {
            getParent().requestDisallowInterceptTouchEvent(false);
            return false;
          }
        }
      }
    }

    return super.dispatchTouchEvent(ev);
  }

  //传入头部布局的最小高度,用于判断分发触摸事件
  public void setMinHeight(int minHeight) {
    this.minHeight = minHeight;
  }

  //判断RecyclerView是否滚动到顶部
  protected boolean isReadyForPullStart() {
    if (getChildCount() <= 0)
      return false;
    View view = getChildAt(0);
    int firstVisiblePosition = getChildAdapterPosition(view);
    if (firstVisiblePosition == 0) {
      return view.getTop() >= getPaddingTop();
    } else {
      return false;
    }
  }
}
第三步

实现折叠效果的关键在这里,采用自定义ViewGroup来管理上面两个空间的位置,在这里使用了support-v4包下的ViewDragHelper工具类来协调两个空间的位置。

核心代码在ViewDragHelper.Callback回调里,ViewDragHelper已经帮我们实现了拖拽控件,我们在回调里判断到了最小的高度则停止拖拽,并且通知子View实现自身的逻辑即可

  private class MDragCallback extends ViewDragHelper.Callback {

    @Override
    public void onViewPositionChanged(View changedView, int left, int top,
                                      int dx, int dy) {
      //控件正在拖拽中回调
    }

    @Override
    public boolean tryCaptureView(View child, int pointerId) {
      //是否需要捕获child的拖动,为false则不拖动
      return true;
    }

    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
      //手指移开屏幕,这里需要做回弹的动画
    }

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
     //返回拖动后控件的坐标,child为拖动中的子控件
      return top;
    }
  }

最后

第一次写教程还有很多地方写得不是很明白,还望大家体谅,如果大家有问题可以在后面留言或者发简信给我,我会尽快回复。

最后再重复一下,工程已经全部上传aizuzi/FoldLayout,欢迎大家去给start

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值