很多APP首頁都是采用頭部+列表的形式顯示钉寝,但是首頁的空間有限乍迄,這時就需要把頭部布局折疊起來了管引,參考效果如下:
Google在Design庫22+上面增加了CoordinatorLayout+Behavior可以實(shí)現(xiàn)這種效y果,但是實(shí)現(xiàn)起來相對比較復(fù)雜闯两,下面我將用另一種方式實(shí)現(xiàn)褥伴。
工程已經(jīng)上傳aizuzi/FoldLayout,歡迎大家給start
效果圖
實(shí)現(xiàn)步驟
第一步
我們自定義一個控件作為頭部顯示漾狼,實(shí)現(xiàn)復(fù)雜的折疊效果也是在該類完成重慢,在這里我先放一個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方法來分發(fā)觸摸事件
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);
}
//傳入頭部布局的最小高度似踱,用于判斷分發(fā)觸摸事件
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;
}
}
}
第三步
實(shí)現(xiàn)折疊效果的關(guān)鍵在這里,采用自定義ViewGroup來管理上面兩個空間的位置,在這里使用了support-v4包下的ViewDragHelper工具類來協(xié)調(diào)兩個空間的位置核芽。
核心代碼在ViewDragHelper.Callback回調(diào)里囚戚,ViewDragHelper已經(jīng)幫我們實(shí)現(xiàn)了拖拽控件,我們在回調(diào)里判斷到了最小的高度則停止拖拽轧简,并且通知子View實(shí)現(xiàn)自身的邏輯即可
private class MDragCallback extends ViewDragHelper.Callback {
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
//控件正在拖拽中回調(diào)
}
@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) {
//返回拖動后控件的坐標(biāo)哮独,child為拖動中的子控件
return top;
}
}
最后
第一次寫教程還有很多地方寫得不是很明白拳芙,還望大家體諒,如果大家有問題可以在后面留言或者發(fā)簡信給我皮璧,我會盡快回復(fù)舟扎。
最后再重復(fù)一下,工程已經(jīng)全部上傳aizuzi/FoldLayout恶导,歡迎大家去給start