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

来源:互联网 发布:seo sem 区别 编辑:程序博客网 时间:2024/04/27 20:51

很多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

0 0
原创粉丝点击