作者:jing.chen
原生的systemui在QsDetail界面上的内容是无法滚动的,上下滑动的手势动作是实现QS界面的展开与收起功能。
原生的效果如左图,需求的效果如右图:
要实现QsDetail内容滚动效果,需要把touch事件传到QsDetail这层里,先分析view的层级结构,view的层级结构如下:
QsDetail里的列表是自定义的NonInterceptingScrollView,原生定义的,继承ScrollView,其效果同listView,根据Android touch事件的分发机制,如果所有的父view没有拦截touch事件,则touch事件会交给top的view,在这里应该交给NonInterceptingScrollView处理,而NonInterceptingScrollView是继承ScrollView,其内容超出界面大小就会滚动,而实际没有滚动,应该是某个父View有拦截touch事件造成,查看代码只有StatusBarWindowView及NotificationPanelView有拦截touch事件,而经debug发现StatusBarWindowView在QsDetail界面上进行上下滚动操作时并没有拦截事件,而是NotificationPanelView拦截了这个touch事件,NotificationPanelView的onInterceptTouchEvent代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mBlockTouches || mQs.isCustomizing()) {
return false;
}
initDownStates(event);
if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
return true;
}
if (mQsOverscrollExpansionEnabled && !isFullyCollapsed() && onQsIntercept(event)) {
//在此处拦截QsDetail界面上进行上下滚动touch事件
return true;
}
return super.onInterceptTouchEvent(event);
}查看onQsIntercept相关代码如下:
private boolean onQsIntercept(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
switch (event.getActionMasked()) {
......
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
trackMovement(event);
if (mQsTracking) {
// Already tracking because onOverscrolled was called. We need to update here
// so we don't stop for a frame until the next touch event gets handled in
// onTouchEvent.
setQsExpansion(h + mInitialHeightOnTouch);
trackMovement(event);
mIntercepting = false;
return true;
}
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
//在此处拦截QsDetail界面上进行上下滚动touch事件
mQsTracking = true;
onQsExpansionStarted();
notifyExpandingFinished();
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = y;
mInitialTouchX = x;
mIntercepting = false;
mNotificationStackScroller.removeLongPressCallback();
return true;
}
break;
......
}
return false;
}关键是shouldQuickSettingsIntercept条件满足导致,其代码如下:
/**
* @return Whether we should intercept a gesture to open Quick Settings.
*/
private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
if (!mQsExpansionEnabled || mCollapsedOnDown) {
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mQs.getHeader();
final boolean onHeader = x >= mQsFrame.getX()
&& x <= mQsFrame.getX() + mQsFrame.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
///Modify:modify message center func, add qs detail view to scroll
//return onHeader || (yDiff < 0 && isInQsArea(x, y));此为原生的条件
//此为修改后的条件
return (onHeader || (yDiff < 0 && isInQsArea(x, y))) && !(isQsDetailShowing() && isInQsAreaWithoutStack(x,y));
} else {
return onHeader;
}
}原生的条件是在整个message center展开的区域里有上下滚动并且滚动距离达到一定的阈值就会拦截这个事件,不管是否已经进入QsDetail界面,要想把touch传递给QsDetail,必须取消拦截,即添加在QsDetail区域判断条件,这样就可以实现在QsDetail界面滚动其内容,在QsDetail之外的区域还是收起快捷开关界面。