自定義側(cè)滑控件 SlideSlipView
功能介紹
本控件是模仿QQ的消息側(cè)滑功能來開發(fā)的,實(shí)現(xiàn)的效果基本跟QQ的側(cè)滑效果一致。而且本控件基本不依賴其它控件,盡量降低耦合性。同時使用起來也非常簡單,支持自定義側(cè)滑內(nèi)容,通過布局文件的方式就可以實(shí)現(xiàn)??梢宰鳛镽ecyclerView的item使用,也可以單獨(dú)添加到布局中使用。

slide_slip.gif
實(shí)現(xiàn)方案
重寫ViewGroup的onLayout方法,對每一個子view合理布局、利用View的事件分發(fā)機(jī)制判斷每一個事件到來時需要做的功能(核心模塊)、通過Scroller和VelocityTracker配合使用達(dá)到自動滑動跟慣性滑動的效果。
使用指南
雖然本控件實(shí)現(xiàn)了想要的效果,但是多少還是有一些不太完美的地方需要大家在使用時注意一下。
- 該組件繼承自FrameLayout,所以在使用的時候?qū)⒆觱iew包裹到該組件內(nèi)就可以了,但是子view的布局方式是按照橫向順序排布的,跟橫向的LinearLayout效果是一樣的。
- 為了區(qū)分子view屬于折疊內(nèi)容還是屬于非折疊內(nèi)容,每一個子view都需要添加android:tag="1"屬性, 屬性值必須是整型變量,[0-100]代表是非折疊內(nèi)容,[101-200]代表折疊內(nèi)容。
- 本組件(不是指子view)的寬度只支持match_parent、固定值,不支持wrap_content;高度則沒有限制。
- 建議大家使用的時候?qū)⒄郫B內(nèi)容跟非折疊內(nèi)容分別用ViewGroup(比如LinearLayout、RelativeLayout、ConstraintLayout等等)包裹起來再放到該控件中,一方面可以減少tag的使用,另一方面可以實(shí)現(xiàn)更復(fù)雜的布局效果。
- 為了解決多指觸摸RecyclerView導(dǎo)致多個item同時發(fā)生側(cè)滑的問題,在使用該組件的時候最好跟TouchRecyclerview配合使用。
注:非折疊內(nèi)容是指組件沒有發(fā)生側(cè)滑時用戶看到的view構(gòu)成的部分,折疊內(nèi)容是指組件發(fā)生側(cè)滑時用戶看到的之前隱藏起來的部分。
實(shí)現(xiàn)代碼
雖然代碼很多,但是核心功能代碼都在onInterceptTouchEvent、onTouchEvent方法中,其它的都是一些工具方法,基本上不用太關(guān)心,代碼中都添加了詳細(xì)的注釋,就不再這里繼續(xù)分析代碼了。另外TouchRecyclerview代碼很簡單就不在這里展示了,最后會貼出源碼地址,歡迎大家去git上start!
/**
* CZL 自定義View模仿qq側(cè)滑刪除
*/
public class SlideSlipView extends FrameLayout {
/**
* 為了更好地分析代碼中的邏輯,下面一些概念在此聲明:
* 折疊狀態(tài):置頂、標(biāo)記為未讀、刪除這部分隱藏起來的時候
* 非折疊狀態(tài):置頂、標(biāo)記為未讀、刪除這部分顯示出來的時候
* 原始內(nèi)容:折疊狀態(tài)下item可見內(nèi)容部分
* 隱藏內(nèi)容:置頂、標(biāo)記為未讀、刪除這部分構(gòu)成的內(nèi)容部分
*/
private final String TAG = SlideSlipView.this.getClass().getSimpleName();
private Scroller scroller;//輔助滾動工具
private int mTouchSlop;
private final float VELOCITY_SLOP = 600;//慣性滑動最小速度值
private float mLastX;//記錄上次觸摸點(diǎn)x坐標(biāo)
private float mLastY;//記錄上次觸摸點(diǎn)y坐標(biāo)
//特殊觸摸標(biāo)記,含義:當(dāng)前被點(diǎn)擊的item之外是否還有其它item是展開的,true:是 false:否
// 當(dāng)為true的時候會進(jìn)行‘?dāng)r截一切’的舉動,也就是對事件只攔截但是什么滑動都不處理(這參考了qq的實(shí)現(xiàn)方式)
private boolean specialTouch = false;
//特殊觸摸標(biāo)記,含義:是否需要將當(dāng)前view變成‘折疊狀態(tài)’,true:是 false:否
//當(dāng)前view為'非折疊狀態(tài)'時,手指點(diǎn)擊‘原始內(nèi)容’區(qū)域并立即抬起后將自動折疊view
private boolean specialTouch2 = false;
private boolean fold = true;//折疊標(biāo)記,判斷當(dāng)前view是否是折疊狀態(tài),true:是 false:否
private VelocityTracker velocityTracker;//速度模擬類
private RecyclerView recyclerView;//如果在RV中使用當(dāng)前view,需要處理-滑動沖突、多個item同時展開的問題,這會用到RV
public SlideSlipView(Context context) {
this(context, (AttributeSet) null);
}
public SlideSlipView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideSlipView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
scroller = new Scroller(context);
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
setClipToPadding(false);//設(shè)置該屬性后,該view設(shè)置的padding部分可以隨內(nèi)容一起滑動
}
/**
* 重寫布局方法,支持子view設(shè)置margin、padding等屬性
* 子view必須設(shè)置tag屬性,否者直接拋出異常
* 可見子view設(shè)置的tag取值范圍0-100;隱藏子view設(shè)置tag取值范圍100-Integer最大值。
* 這里的子view是指直接子view,可見子view是指正常情況下可以看見的子view,隱藏子view是指正常情況下不可見的子view,當(dāng)滑動以后才能看到的view
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int childCount = getChildCount();
final int mPaddingLeft = Math.max(0, getPaddingLeft());
final int mPaddingRight = Math.max(0, getPaddingRight());
final int mPaddingTop = Math.max(0, getPaddingTop());
int widthSum = mPaddingLeft;//可見內(nèi)容部分初始左邊界位置
int widthSum2 = getMeasuredWidth();//隱藏內(nèi)容部分初始左邊界位置
int heightSum = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
int tag;
try {
tag = Integer.valueOf((String) childView.getTag());//這里設(shè)計通過tag區(qū)分可見子view跟隱藏子view,所以使用該控件必需設(shè)置子view的tag
} catch (Exception e) {
throw new IllegalArgumentException("SlideSlipView的子View需要設(shè)置tag,顯示View的tag 0-100,隱藏View的tag 101-Integer.MAX_VALUE");
}
heightSum = mPaddingTop + getMargin(2, childView);
if (tag <= 100 && tag >= 0) {//可見子view,tag 0-100
widthSum += getMargin(0, childView);//獲取childView左邊界的mleft位置
if (widthSum >= (getMeasuredWidth() - mPaddingRight)) {//當(dāng)可見子View累計寬度大于可用寬度時,不再顯示剩余‘可見子view’
continue;
}
int rightPosition = Math.min(getMeasuredWidth() - mPaddingRight, widthSum + childView.getMeasuredWidth());//當(dāng)子view的右邊界大于
childView.layout(widthSum, heightSum, rightPosition, heightSum + childView.getMeasuredHeight());
widthSum += childView.getMeasuredWidth();//計算子view的橫向位置
widthSum += getMargin(1, childView);//計算子view的橫向位置
} else if (tag > 100) {//隱藏子view, tag 100-Integer.MAX_VALUE
Log.e(TAG, "onLayout: currentViewWidth=" + getMeasuredWidth() + "\tchildWidth=" + childView.getMeasuredWidth() + "\ttag=" + tag);
widthSum2 += getMargin(0, childView);//隱藏子view有多少就布局多少個
childView.layout(widthSum2, heightSum, widthSum2 + childView.getMeasuredWidth(), heightSum + childView.getMeasuredHeight());
widthSum2 += childView.getMeasuredWidth();//計算子view的橫向位置
widthSum2 += getMargin(1, childView);//計算子view的橫向位置
}
}
}
/**
* 側(cè)滑關(guān)閉策略:
* 1、點(diǎn)擊的item是‘展開’狀態(tài),則將item折疊(包括當(dāng)前item及可能存在的其它item,因?yàn)镽V如果多個手指同時滑動多個item時,都會進(jìn)行側(cè)滑,這跟qq不一樣,是個小bug)
* 具體實(shí)現(xiàn)方案:在onInterceptTouchEvent方法的ACTION_UP事件,并更新狀態(tài),同時攔截該事件(不允許調(diào)用item的onClick方法)
* 2、被點(diǎn)擊的item是‘折疊’狀態(tài),如果其它item有‘展開’則‘折疊’其它item(在actiondown的時候就),如果其他item都是‘折疊’則正常處理點(diǎn)擊事件
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted;
float currentX = ev.getX();
float currentY = ev.getY();
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (existExpandChildren()) {//這行代碼作用:當(dāng)有其它item展開的時候,點(diǎn)擊當(dāng)前item關(guān)閉其它展開的item,并將該事件停止下傳
// Log.e(TAG, "---------------------------onInterceptTouchEvent: 特殊事件ACTION_DOWN");
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(true);//這個方法一旦調(diào)用,除非再調(diào)用一次,否則該view永遠(yuǎn)無法攔截事件
}
specialTouch = true;//特殊事件標(biāo)記,當(dāng)是true的時候攔截所有的事件,但是不對事件做處理(即rv不進(jìn)行滾動、slideview不進(jìn)行任何滑動響應(yīng))
}
if (!isFold()) {//如果當(dāng)前item是‘展開’狀態(tài)
specialTouch = false;//如果當(dāng)前item也是‘非折疊’狀態(tài)的話,停止‘?dāng)r截一切’的舉動
if (needFold(ev)) {
specialTouch2 = true;
intercepted = true;
break;
}
}
if (specialTouch) {//把特殊事件標(biāo)記放這里是為了讓第二個標(biāo)記的判斷也能走一遍
intercepted = true;
break;
}
//當(dāng)既沒有其它item展開,點(diǎn)擊的也不是‘原始內(nèi)容’時,那么將觸摸事件分發(fā)給slideslipview的子view
mLastX = currentX;
mLastY = currentY;
intercepted = false;
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(true);
}
}
break;
case MotionEvent.ACTION_MOVE:
if (specialTouch) {//特殊事件、特殊處理
// Log.e(TAG, "---------------------------onInterceptTouchEvent: 特殊事件ACTION_MOVE");
intercepted = true;
break;
}
//當(dāng)最開始點(diǎn)擊的是‘隱藏內(nèi)容’時會執(zhí)行到這里(點(diǎn)擊‘隱藏內(nèi)容’后slideview需要判斷后續(xù)的動作,如果是滑動的話那么對事件進(jìn)行攔截)
if (Math.abs(currentX - mLastX) > Math.abs(currentY - mLastY)) {//橫向滑動
mLastX = currentX;
mLastY = currentY;
intercepted = true;//攔截事件
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(true);//如果是橫向滑動的話,禁止RV攔截接下來的事件,否則RV會攔截接下來的事件,造成滑動沖突
}
} else if (Math.abs(currentY - mLastY) > Math.abs(currentX - mLastX)) {//豎直滑動
intercepted = false;//不攔截事件
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(false);//允許RV攔截滑動事件,RV一旦攔截事件的話接下來所有的事件都會交給RV處理
}
} else {
intercepted = false;
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(false);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
intercepted = false;//不對up事件進(jìn)行攔截,因?yàn)橐坏r截的話,那么子view的點(diǎn)擊(包括長按)功能就不管用了
break;
default:
intercepted = false;
}
// Log.e(TAG, "onInterceptTouchEvent: intercept=" + intercepted);
return intercepted;
}
/**
* 邏輯方法
* 這個方法用來判斷當(dāng)手指點(diǎn)擊‘非折疊狀態(tài)’下‘原始內(nèi)容’區(qū)域時是否需要自動折疊
* true:自動折疊 false:不折疊
*/
private boolean needFold(MotionEvent ev) {
boolean result = false;
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int tag = Integer.valueOf((String) childView.getTag());
if (!(tag >= 0 && tag <= 100)) {//這里的判斷是過濾掉‘隱藏內(nèi)容’view
continue;
}
Rect rect = new Rect();
childView.getGlobalVisibleRect(rect);
// Log.e(TAG, "onInterceptTouchEvent: x=" + ev.getX() + "\ty=" + ev.getY() + "\tleft=" + rect.left + "\ttop=" + rect.top + "\tright=" + rect.right + "\tbottom=" + rect.bottom);
Rect rect1 = new Rect(rect.left, 0, rect.right, rect.bottom - rect.top);
if (rect1.contains(Math.round(ev.getX()), Math.round(ev.getY()))) {//判斷點(diǎn)擊區(qū)域是否在‘原始內(nèi)容’部分內(nèi)
// Log.e(TAG, "onInterceptTouchEvent: 點(diǎn)擊第一個子view");
result = true;//特殊事件標(biāo)記,表示手指按下部分是否屬于‘原始內(nèi)容’,true:是 false:不是
if (getRecyclerView() != null) {//xz
getRecyclerView().requestDisallowInterceptTouchEvent(true);
}
break;
}
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (specialTouch) {//特殊事件-是為了處理點(diǎn)擊折疊狀態(tài)的item,關(guān)閉展開狀態(tài)的item后RV不處理觸摸事件(也就是不滑動)
boolean result;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "---------------------------onTouchEvent: 特殊事件ACTION_DOWN");
result = true;
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "---------------------------onTouchEvent: 特殊事件ACTION_MOVE");
result = true;
break;
case MotionEvent.ACTION_UP: {
Log.e(TAG, "---------------------------onTouchEvent: 特殊事件ACTION_UP");
specialTouch = false;
getRecyclerView().requestDisallowInterceptTouchEvent(false);
result = true;
}
break;
case MotionEvent.ACTION_CANCEL: {
Log.e(TAG, "---------------------------onTouchEvent: 特殊事件ACTION_CANCEL");
specialTouch = false;
getRecyclerView().requestDisallowInterceptTouchEvent(false);
result = true;
}
break;
default:
specialTouch = false;
getRecyclerView().requestDisallowInterceptTouchEvent(false);
result = false;
}
return result;
}
boolean result = false;//為了配合特殊事件2
float currentX = event.getX();
float currentY = event.getY();
addVelocityTracker(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastX = currentX;
mLastY = currentY;
result = true;//為了配合特殊事件2
}
break;
case MotionEvent.ACTION_MOVE:
if (specialTouch2 && Math.abs(currentX - mLastX) > mTouchSlop && Math.abs(currentX - mLastX) > Math.abs(currentY - mLastY)) {
//這里的條件語句目的只有一個,判斷 “從用戶手指點(diǎn)擊‘原始內(nèi)容’部分到手指離開” 這是一個點(diǎn)擊事件還是滑動事件
//當(dāng)手指移動范圍較大的時候看成滑動事件,否者看成點(diǎn)擊事件;(當(dāng)手指按下觸摸屏幕的時候,保持不動,對用戶來說他認(rèn)為沒有移動手指所以沒有移動,
// 但是對系統(tǒng)來說即使再微小的移動也能捕獲,而且確實(shí)當(dāng)你手指從按下那一刻就在不停的移動,只是人很難察覺到)
//至于為什么要區(qū)分事件,是因?yàn)閷Σ煌录枰霾煌幚恚c(diǎn)擊事件:手指抬起時需要對展開的item折疊 ,滑動事件:手指抬起時需要對item進(jìn)行自動滑動(折疊起來還是展開)
//而作為區(qū)分的標(biāo)記就是specialTouch2,這個值最終會在action_up時用到,所以這里的設(shè)計當(dāng)時也是耗費(fèi)了一些時間才想到的。
specialTouch2 = false;//特殊事件2,一旦開始移動的話就不再算作特殊事件,也就是將特殊事件看做失效
}
//當(dāng)用戶手指滑動slideview的時候,讓內(nèi)容滑動起來
if (Math.abs(currentX - mLastX) > Math.abs(currentY - mLastY)) {//注意點(diǎn):這里沒有使用mTouchSlop進(jìn)行判斷,是因?yàn)槭褂胢TouchSlop會讓滑動不流暢
if (currentX - mLastX > 0) {
//向右滑動
if (getScrollX() <= 0) {
//停止滑動,因?yàn)橐呀?jīng)滑動到邊界
} else {
scrollBy(-Math.min(getScrollX(), Math.round(currentX - mLastX)), 0);
}
} else {
//向左滑動
int childCount = getChildCount();
int rightBorder = getChildAt(childCount - 1).getRight();
Log.e(TAG, "onTouchEvent: rightBorder=" + rightBorder);
// if (getScrollX() >= rightBorder - getChildAt(0).getRight()) {
if (getScrollX() >= (rightBorder - getMeasuredWidth())) {//當(dāng)滾動距離大于‘隱藏子view’區(qū)域(間距+寬度)寬度的時候停止滑動
//停止滑動,因?yàn)橐呀?jīng)滑動到邊界
} else {//當(dāng)滾動距離小于‘隱藏子view’區(qū)域的寬度時,繼續(xù)進(jìn)行滑動(這里進(jìn)行了滑動判斷,防止手指移動距離大于內(nèi)容可滑動距離)
// scrollBy(Math.min(rightBorder - getChildAt(0).getRight() - getScrollX(), Math.round(mLastX - currentX)), 0);
scrollBy(Math.min(rightBorder - getMeasuredWidth() - getScrollX(), Math.round(mLastX - currentX)), 0);
}
}
mLastX = currentX;
mLastY = currentY;
}
break;
case MotionEvent.ACTION_UP: {
if (specialTouch2) {
//如果用戶點(diǎn)擊的是‘原始內(nèi)容’,折疊item
Log.e(TAG, "---------------------------------onTouchEvent: 特殊事件2");
setFold(true);
smoothScrollToStart();
if (getRecyclerView() != null) {
getRecyclerView().requestDisallowInterceptTouchEvent(false);
}
break;
}
//如果用戶滑動的item,手指離開的時候讓item自動滑動(這里面有用到慣性滑動)
int childCount = getChildCount();
int rightBorder = getChildAt(childCount - 1).getRight();
float x_velocity = getXVelocity();
if (x_velocity > VELOCITY_SLOP) {//向右滑動(慣性滑動)
Log.e(TAG, "---------------------------------onTouchEvent: 快速向右滑動");
setFold(true);
scroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 200);
invalidate();//遺忘點(diǎn),這句代碼如果不加的話導(dǎo)致view不會慣性滑動
} else if (x_velocity < -VELOCITY_SLOP) {//左滑動(慣性滑動)
Log.e(TAG, "---------------------------------onTouchEvent: 快速向左滑動");
setFold(false);
// scroller.startScroll(getScrollX(), 0, rightBorder - getChildAt(0).getRight() - getScrollX(), 0, 200);
scroller.startScroll(getScrollX(), 0, rightBorder - getMeasuredWidth() - getScrollX(), 0, 200);
invalidate();//遺忘點(diǎn),這句代碼如果不加的話導(dǎo)致view不會慣性滑動
} else {//根據(jù)滑動距離判斷是折疊還是展開
Log.e(TAG, "---------------------------------onTouchEvent: 自由滑動");
// if (getScrollX() > (rightBorder - getChildAt(0).getRight()) / 2) {
if (getScrollX() > (rightBorder - getMeasuredWidth()) / 2) {//當(dāng)滑動距離超過‘隱藏內(nèi)容’一半寬度時,手指離開屏幕后隱藏內(nèi)容剩余部分自動展開
setFold(false);
// scroller.startScroll(getScrollX(), 0, rightBorder - getChildAt(0).getRight() - getScrollX(), 0, 500);
scroller.startScroll(getScrollX(), 0, rightBorder - getMeasuredWidth() - getScrollX(), 0, 500);
invalidate();
} else {//當(dāng)滑動距離小于‘隱藏內(nèi)容’一半寬度時,手指離開屏幕后隱藏內(nèi)容已展開部分自動折疊
setFold(true);
scroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 500);
invalidate();
}
}
recycleVelocityTracker();
releaseRecyclerView();//XZ
}
break;
}
return super.onTouchEvent(event) || result;
}
/**
* 工具方法(View自帶方法,所有view都有該方法)
* 頁面每次重繪都會調(diào)用該方法,默認(rèn)是空實(shí)現(xiàn),一般都是跟scroller配合使用
*/
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
/**
* 工具方法
* 獲取慣性滑動的豎直速度
*/
private void addVelocityTracker(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
}
/**
* 工具方法
* 獲取慣性滑動的橫向速度
*/
private float getXVelocity() {
if (velocityTracker != null) {
velocityTracker.computeCurrentVelocity(1000);
return velocityTracker.getXVelocity();
}
return 0;
}
/**
* 工具方法
* VelocityTracker常規(guī)使用
*/
private void recycleVelocityTracker() {
if (velocityTracker != null) {
velocityTracker.recycle();//出錯點(diǎn),這里不需要再添加clear代碼,否者會報錯
}
velocityTracker = null;
}
/**
* 工具方法
* 獲取該view所在列表的recyclerview對象
*/
private RecyclerView getRecyclerView() {
if (recyclerView != null)
return recyclerView;
ViewParent viewParent = this;
while (!(viewParent.getParent() instanceof RecyclerView)) {
viewParent = viewParent.getParent();
if (viewParent == null) {
Log.e(TAG, "getRecyclerView: 沒有找到recyclerview");
break;
}
}
recyclerView = (viewParent == null ? null : (RecyclerView) viewParent.getParent());
if (recyclerView != null) {
Log.e(TAG, "getRecyclerView: 找到了recyclerview");
}
return recyclerView;
}
private void releaseRecyclerView() {
recyclerView = null;
}
/**
* view折疊狀態(tài)
* 默認(rèn)為true
* 當(dāng)刪除、置頂部分隱藏的時候是折疊狀態(tài),顯示的時候是非折疊狀態(tài)
*/
public boolean isFold() {
return fold;
}
public void setFold(boolean fold) {
this.fold = fold;
}
/**
* 工具方法
* 遍歷RV可見范圍內(nèi)是否有其它item是非折疊狀態(tài)
*/
private boolean existExpandChildren() {
boolean result = false;
if (getRecyclerView() != null) {
int childCount = getRecyclerView().getChildCount();
for (int i = 0; i < childCount; i++) {
View itemView = getRecyclerView().getChildAt(i);
SlideSlipView slideSlipView = getExpandSlideSlipView(itemView);
if (slideSlipView != null) {
slideSlipView.smoothScrollToStart();
slideSlipView.setFold(true);
result = true;
}
}
}
return result;
}
/**
* 工具方法
* scroller滑動常規(guī)使用
*/
private void smoothScrollToStart() {
scroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 500);
postInvalidate();
}
/**
* 工具方法
* 遞歸獲取slideslipview對象
*/
private SlideSlipView getExpandSlideSlipView(View parentView) {
if (parentView == null) {
return null;
}
if (parentView == this) {
return null;
}
if (parentView instanceof SlideSlipView) {
if (((SlideSlipView) parentView).isFold()) {
return null;
} else {
return (SlideSlipView) parentView;
}
}
if (!(parentView instanceof ViewGroup)) {
return null;
}
final int childCount = ((ViewGroup) parentView).getChildCount();
for (int i = 0; i < childCount; i++) {
SlideSlipView slipView = getExpandSlideSlipView(((ViewGroup) parentView).getChildAt(i));
if (slipView != null) {
if (slipView.isFold()) {
return null;
} else {
return slipView;
}
}
}
return null;
}
/**
* 工具方法
* 獲取子view的間距
*/
private int getMargin(int type, View childView) {
ViewGroup.LayoutParams layoutParams = childView.getLayoutParams();
if (!(layoutParams instanceof MarginLayoutParams)) {
return 0;
}
int result;
switch (type) {
case 0:
result = Math.max(0, ((MarginLayoutParams) layoutParams).leftMargin);
break;
case 1:
result = Math.max(0, ((MarginLayoutParams) layoutParams).rightMargin);
break;
case 2:
result = Math.max(0, ((MarginLayoutParams) layoutParams).topMargin);
break;
default:
result = 0;
}
return result;
}
}
源碼地址:https://github.com/AndroidFirstDeveloper/DevelopProject