簡介
DrawerLayout
充當窗口內(nèi)容的頂層容器,允許"抽屜"式的控件可以從窗口的一邊或者兩邊垂直邊緣拉出
使用
抽屜的位置或者布局可以通過android:layout_gravity
子view的屬性控制從那邊拉出根穷,left/start代表從左邊拉出,right/end代表從右側(cè)拉出浪漠,需要注意的是只能有一個抽屜控件從窗口的垂直邊緣奏属,如果布局中每個垂直窗口有多于一個抽屜控件木柬,將會拋出異常
根布局使用DrawerLayout
作為第一個主內(nèi)容布局间学,主內(nèi)容布局寬高設置為match_parent
不用設置layout_gravity
,然后在主內(nèi)容布局上添加子控件拒担,并且設置layout_gravity
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerlayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@+id/fragment_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<RelativeLayout
android:id="@+id/left"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:background="@android:color/white">
<ListView
android:id="@+id/left_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</RelativeLayout>
<RelativeLayout
android:id="@+id/right"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="right"
android:background="@android:color/holo_green_light">
<TextView
android:id="@+id/right_textview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="個人登陸頁面" />
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
詳細代碼請參考 http://blog.csdn.net/elinavampire/article/details/41477525
源碼分析
構造函數(shù)
public DrawerLayout(Context context) {
this(context, null);
}
public DrawerLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
final float density = getResources().getDisplayMetrics().density;
mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);
final float minVel = MIN_FLING_VELOCITY * density;
mLeftCallback = new ViewDragCallback(Gravity.LEFT);
mRightCallback = new ViewDragCallback(Gravity.RIGHT);
mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback);
mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
mLeftDragger.setMinVelocity(minVel);
mLeftCallback.setDragger(mLeftDragger);
mRightDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mRightCallback);
mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);
mRightDragger.setMinVelocity(minVel);
mRightCallback.setDragger(mRightDragger);
// So that we can catch the back button
setFocusableInTouchMode(true);
ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
ViewGroupCompat.setMotionEventSplittingEnabled(this, false);
if (ViewCompat.getFitsSystemWindows(this)) {
IMPL.configureApplyInsets(this);
mStatusBarBackground = IMPL.getDefaultStatusBarBackground(context);
}
mDrawerElevation = DRAWER_ELEVATION * density;
mNonDrawerViews = new ArrayList<View>();
}
構造函數(shù)中嘹屯,設置view group
的初始焦點,根據(jù)手機密度計算出Drawer
的margin
值从撼,初始化從左側(cè)邊緣拉出來的布局的回掉監(jiān)聽和從右側(cè)邊緣拉出來的布局的回掉監(jiān)聽州弟,其中,在DrawerLayout
的源碼是的滑動部分使用的是ViewDragHelper
,所以要初始化左側(cè)的滑動和右側(cè)的滑動,設置觸摸時的焦點婆翔,初始化view的List
ViewDragHelper的回調(diào)ViewDragCallback
其中初始化的過程中有個很重要的方法拯杠,就是ViewDragHelper
的回掉,下面我們就來看一下ViewDragCallback
private class ViewDragCallback extends ViewDragHelper.Callback {
private final int mAbsGravity;
private ViewDragHelper mDragger;
private final Runnable mPeekRunable = new Runnable() {
@Override public void run() {
peekDrawer();
}
};
// 注明拖拽的方向
public ViewDragCallback(int gravity) {
mAbsGravity = gravity;
}
public void setDragger(ViewDragHelper dragger) {
mDragger = dragger;
}
// 移除方法回掉
public void removeCallbacks() {
DrawerLayout.this.removeCallbacks(mPeekRunnable);
}
// 當前child是拖拽的view啃奴,并且當前拖拽是當前設置的方向潭陪,并且當前的child可以拖拽
@Override
public boolean tryCaptureView(View child, int pointerId) {
// Only capture views where the gravity matches what we're looking for.
// This lets us use two ViewDragHelpers, one for each side drawer.
return isDrawerView(child) && checkDrawerViewAbsoluteGravity(child, mAbsGravity) && getDrawerLockMode(child) == LOCK_MODE_UNLOCKED;
}
// 當前拖拽的view的狀態(tài)發(fā)生變化時,更新拖拽狀態(tài)
@Override
public void onViewDragStateChanged(int state) {
updateDrawerState(mAbsGravity, state, mDragger.getCapturedView());
}
// 當view的位置發(fā)生變化時最蕾,重新布局
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
float offset;
final int childWidth = changedView.getWidth();
// This reverses the positioning shown in onLayout.
if (checkDrawerViewAbsoluteGravity(changedView, Gravity.LEFT)) {
offset = (float) (childWidth + left) / childWidth;
} else {
final int width = getWidth();
offset = (float) (width - left) / childWidth;
}
setDrawerViewOffset(changedView, offset);
changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
invalidate();
}
// view開始被拖拽
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
final LayoutParams lp = (LayoutParams) capturedChild.getLayoutParams();
lp.isPeeking = false;
closeOtherDrawer();
}
// 確認當前拖拽的方向依溯,關閉掉其他方向的拖拽
private void closeOtherDrawer() {
final int otherGrav = mAbsGravity == Gravity.LEFT ? Gravity.RIGHT : Gravity.LEFT;
final View toClose = findDrawerWithGravity(otherGrav);
if (toClose != null) {
closeDrawer(toClose);
}
}
// 被拖拽的被回掉時調(diào)用,先獲得子view的寬瘟则,然后計算出左邊距黎炉,滑動到指定位置
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// Offset is how open the drawer is, therefore left/right values
// are reversed from one another.
final float offset = getDrawerViewOffset(releasedChild);
final int childWidth = releasedChild.getWidth();
int left;
if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT)) {
left = xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth;
} else {
final int width = getWidth();
left = xvel < 0 || xvel == 0 && offset > 0.5f ? width - childWidth : width;
}
mDragger.settleCapturedViewAt(left, releasedChild.getTop());
invalidate();
}
//觸摸到邊緣時回掉函數(shù)
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
postDelayed(mPeekRunnable, PEEK_DELAY);
}
// 根據(jù)拖拽的方向計算出view的左側(cè)位置,判斷是哪個方向滑動壹粟,如果是單側(cè)劃定關閉另一側(cè)的view拜隧,取消另一側(cè)的滑動
private void peekDrawer() {
final View toCapture;
final int childLeft;
final int peekDistance = mDragger.getEdgeSize();
final boolean leftEdge = mAbsGravity == Gravity.LEFT;
if (leftEdge) {
toCapture = findDrawerWithGravity(Gravity.LEFT);
childLeft = (toCapture != null ? -toCapture.getWidth() : 0) + peekDistance;
} else {
toCapture = findDrawerWithGravity(Gravity.RIGHT);
childLeft = getWidth() - peekDistance;
}
// Only peek if it would mean making the drawer more visible and the drawer isn't locked
if (toCapture != null && ((leftEdge && toCapture.getLeft() < childLeft) || (!leftEdge && toCapture.getLeft() > childLeft)) && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
final LayoutParams lp = (LayoutParams) toCapture.getLayoutParams();
mDragger.smoothSlideViewTo(toCapture, childLeft, toCapture.getTop());
lp.isPeeking = true;
invalidate();
closeOtherDrawer();
cancelChildViewTouch();
}
}
// 是否鎖定邊緣,如果鎖定邊緣趁仙,view不為空并且view不能拖拽洪添,關閉view的抽屜
@Override
public boolean onEdgeLock(int edgeFlags) {
if (ALLOW_EDGE_LOCK) {
final View drawer = findDrawerWithGravity(mAbsGravity);
if (drawer != null && !isDrawerOpen(drawer)) {
closeDrawer(drawer);
}
return true;
}
return false;
}
// 觸摸邊緣開始時調(diào)用此方法,先根據(jù)滑動方向獲得當前view雀费,如果當前view可以拖拽干奢,捕獲view的操作
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
final View toCapture;
if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
toCapture = findDrawerWithGravity(Gravity.LEFT);
} else {
toCapture = findDrawerWithGravity(Gravity.RIGHT);
}
if (toCapture != null && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
mDragger.captureChildView(toCapture, pointerId);
}
}
// 獲取拖拽view的水平方向的范圍
@Override
public int getViewHorizontalDragRange(View child) {
return isDrawerView(child) ? child.getWidth() : 0;
}
// 捕獲水平方向的view被拖拽到的位置
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
return Math.max(-child.getWidth(), Math.min(left, 0));
} else {
final int width = getWidth();
return Math.max(width - child.getWidth(), Math.min(left, width));
}
}
// 垂直方向view移動的位置
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return child.getTop();
}
}
ViewDragHelper
使用了Scroller,最后滑動的computeScroll()
@Override
public void computeScroll() {
final int childCount = getChildCount();
float scrimOpacity = 0;
for (int i = 0; i < childCount; i++) {
final float onscreen = ((LayoutParams) getChildAt(i).getLayoutParams()).onScreen;
scrimOpacity = Math.max(scrimOpacity, onscreen);
}
mScrimOpacity = scrimOpacity;
// "|" used on purpose; both need to run.
if (mLeftDragger.continueSettling(true) | mRightDragger.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
看過ViewDragHelper
的人應該都知道上面這個方法中的含義盏袄,這里簡單在代碼中注釋,詳見ViewDragHelper 源碼分析
onInterceptTouchEvent方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
// "|" used deliberately here; both methods should be invoked.
final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev) | mRightDragger.shouldInterceptTouchEvent(ev);
boolean interceptForTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mInitialMotionX = x;
mInitialMotionY = y;
if (mScrimOpacity > 0) {
final View child = mLeftDragger.findTopChildUnder((int) x, (int) y);
if (child != null && isContentView(child)) {
interceptForTap = true;
}
}
mDisallowInterceptRequested = false;
mChildrenCanceledTouch = false;
break;
}
case MotionEvent.ACTION_MOVE: {
// If we cross the touch slop, don't perform the delayed peek for an edge touch.
if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {
mLeftCallback.removeCallbacks();
mRightCallback.removeCallbacks();
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
closeDrawers(true);
mDisallowInterceptRequested = false;
mChildrenCanceledTouch = false;
}
}
return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;
}
在使用ViewDragHelper
時都知道要攔截事件交給ViewDragHelper
忿峻,還有幾種情況也要攔截,如果左側(cè)拖轉(zhuǎn)的view不為空辕羽,并且gravity == Gravity.NO_GRAVITY
也攔截該事件逛尚,在Down
和Up
也攔截該事件
private boolean hasPeekingDrawer() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
if (lp.isPeeking) {
return true;
}
}
return false;
}
如果當前的子view是拖拽的view,也攔截該事件
onMeasure方法
由于DrawerLayout
是繼承自ViewGroup
刁愿,所以onMeasure方法主要是計算本身的寬高和子view的寬高绰寞,處理設置wrap_content
的情況
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
if (isInEditMode()) {
// Don't crash the layout editor. Consume all of the space if specified
// or pick a magic number from thin air otherwise.
// TODO Better communication with tools of this bogus state.
// It will crash on a real device.
if (widthMode == MeasureSpec.AT_MOST) {
widthMode = MeasureSpec.EXACTLY;
} else if (widthMode == MeasureSpec.UNSPECIFIED) {
widthMode = MeasureSpec.EXACTLY;
widthSize = 300;
}
if (heightMode == MeasureSpec.AT_MOST) {
heightMode = MeasureSpec.EXACTLY;
}
else if (heightMode == MeasureSpec.UNSPECIFIED) {
heightMode = MeasureSpec.EXACTLY;
heightSize = 300;
}
} else {
throw new IllegalArgumentException( "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
}
}
setMeasuredDimension(widthSize, heightSize);
final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);
final int layoutDirection = ViewCompat.getLayoutDirection(this);
// Only one drawer is permitted along each vertical edge (left / right). These two booleans
// are tracking the presence of the edge drawers.
boolean hasDrawerOnLeftEdge = false;
boolean hasDrawerOnRightEdge = false;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (applyInsets) {
final int cgrav = GravityCompat.getAbsoluteGravity(lp.gravity, layoutDirection);
if (ViewCompat.getFitsSystemWindows(child)) {
IMPL.dispatchChildInsets(child, mLastInsets, cgrav);
} else {
IMPL.applyMarginInsets(lp, mLastInsets, cgrav);
}
}
if (isContentView(child)) {
// Content views get measured at exactly the layout's size.
final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
child.measure(contentWidthSpec, contentHeightSpec);
} else if (isDrawerView(child)) {
if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
if (ViewCompat.getElevation(child) != mDrawerElevation) {
ViewCompat.setElevation(child, mDrawerElevation);
}
}
final @EdgeGravity int childGravity = getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
// Note that the isDrawerView check guarantees that childGravity here is either
// LEFT or RIGHT
boolean isLeftEdgeDrawer = (childGravity == Gravity.LEFT);
if ((isLeftEdgeDrawer && hasDrawerOnLeftEdge) || (!isLeftEdgeDrawer && hasDrawerOnRightEdge)) {
throw new IllegalStateException("Child drawer has absolute gravity " + gravityToString(childGravity) + " but this " + TAG + " already has a " + "drawer view along that edge");
}
if (isLeftEdgeDrawer) {
hasDrawerOnLeftEdge = true;
} else {
hasDrawerOnRightEdge = true;
}
final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, mMinDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width);
final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
child.measure(drawerWidthSpec, drawerHeightSpec);
} else {
throw new IllegalStateException("Child " + child + " at index " + i + " does not have a valid layout_gravity - must be Gravity.LEFT, " + "Gravity.RIGHT or Gravity.NO_GRAVITY");
}
}
}
如果寬或者高不是MeasureSpec.EXACTLY
時,如果widthMode
等于MeasureSpec.AT_MOST
,則widthMode
等于MeasureSpec.EXACTLY
,如果widthMode
等于MeasureSpec.UNSPECIFIED
則寬默認等于300,高同理铣口,然后遍歷子view
boolean isContentView(View child) {
return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
}
如果子view沒有設置gravity
屬性的話滤钱,給子view設置寬高以及mode
boolean isDrawerView(View child) {
final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(child));
if ((absGravity & Gravity.LEFT) != 0) {
// This child is a left-edge drawer
return true;
}
if ((absGravity & Gravity.RIGHT) != 0) {
// This child is a right-edge drawer
return true;
}
return false;
}
判斷gravity
屬性是left
or right
,然后通過child.measure(drawerWidthSpec, drawerHeightSpec);
給子view設置寬高Spec
onLayout方法
onLayout
方法主要是給子view布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mInLayout = true;
final int width = r - l;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (isContentView(child)) {
child.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(), lp.topMargin + child.getMeasuredHeight());
} else { // Drawer, if it wasn't onMeasure would have thrown an exception.
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int childLeft;
final float newOffset;
if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
childLeft = -childWidth + (int) (childWidth * lp.onScreen);
newOffset = (float) (childWidth + childLeft) / childWidth;
} else { // Right; onMeasure checked for us.
childLeft = width - (int) (childWidth * lp.onScreen);
newOffset = (float) (width - childLeft) / childWidth;
}
final boolean changeOffset = newOffset != lp.onScreen;
final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (vgrav) {
default:
case Gravity.TOP: {
child.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);
break;
}
case Gravity.BOTTOM: {
final int height = b - t;
child.layout(childLeft, height - lp.bottomMargin - child.getMeasuredHeight(), childLeft + childWidth, height - lp.bottomMargin);
break;
}
case Gravity.CENTER_VERTICAL: {
final int height = b - t;
int childTop = (height - childHeight) / 2;
// Offset for margins. If things don't fit right because of
// bad measurement before, oh well.
if (childTop < lp.topMargin) {
childTop = lp.topMargin;
} else if (childTop + childHeight > height - lp.bottomMargin) {
childTop = height - lp.bottomMargin - childHeight;
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
break;
}
}
if (changeOffset) {
setDrawerViewOffset(child, newOffset);
}
final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE;
if (child.getVisibility() != newVisibility) {
child.setVisibility(newVisibility);
}
}
}
mInLayout = false;
mFirstLayout = false;
}
遍歷子view脑题,如果子view設置了gravity
,根據(jù)子view的gravity
屬性計算childLeft
和newOffset
件缸,如果子view是垂直方向的,根據(jù)gravity
屬性計算top
andbottom
drawChild方法
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final int height = getHeight();
final boolean drawingContent = isContentView(child);
int clipLeft = 0, clipRight = getWidth();
final int restoreCount = canvas.save();
if (drawingContent) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
if (v == child || v.getVisibility() != VISIBLE || !hasOpaqueBackground(v) || !isDrawerView(v) || v.getHeight() < height) {
continue;
}
if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {
final int vright = v.getRight();
if (vright > clipLeft) clipLeft = vright;
} else {
final int vleft = v.getLeft();
if (vleft < clipRight) clipRight = vleft;
}
}
canvas.clipRect(clipLeft, 0, clipRight, getHeight());
}
final boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(restoreCount);
if (mScrimOpacity > 0 && drawingContent) {
final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
final int imag = (int) (baseAlpha * mScrimOpacity);
final int color = imag << 24 | (mScrimColor & 0xffffff);
mScrimPaint.setColor(color);
canvas.drawRect(clipLeft, 0, clipRight, getHeight(), mScrimPaint);
} else if (mShadowLeftResolved != null && checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
final int shadowWidth = mShadowLeftResolved.getIntrinsicWidth();
final int childRight = child.getRight();
final int drawerPeekDistance = mLeftDragger.getEdgeSize();
final float alpha = Math.max(0, Math.min((float) childRight / drawerPeekDistance, 1.f));
mShadowLeftResolved.setBounds(childRight, child.getTop(), childRight + shadowWidth, child.getBottom());
mShadowLeftResolved.setAlpha((int) (0xff * alpha));
mShadowLeftResolved.draw(canvas);
} else if (mShadowRightResolved != null && checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
final int shadowWidth = mShadowRightResolved.getIntrinsicWidth();
final int childLeft = child.getLeft();
final int showing = getWidth() - childLeft;
final int drawerPeekDistance = mRightDragger.getEdgeSize();
final float alpha = Math.max(0, Math.min((float) showing / drawerPeekDistance, 1.f));
mShadowRightResolved.setBounds(childLeft - shadowWidth, child.getTop(), childLeft, child.getBottom());
mShadowRightResolved.setAlpha((int) (0xff * alpha));
mShadowRightResolved.draw(canvas);
}
return result;
}
判斷當前的view是否設置gravity
屬性值叔遂,如果沒有設置gravity
,計算clipLeft
和clipRight
值,如果mScrimOpacity > 0
畫一個矩形他炊,如果view的gravity
值為Gravity.LEFT
,畫右側(cè)的view陰影部分争剿,如果view的gravity
值為Gravity.RIGHT
畫左側(cè)的view陰影部分
DrawerLayout
的主要功能就是滑動,源碼中使用了ViewDragHelper
實現(xiàn)了滑動佑稠,具體不了解的地方可以去看ViewDragHelper
源碼秒梅,DrawerLayout
繼承自ViewGroup
,所以要去計算自身以及子view的寬高舌胶,以及實現(xiàn)子view在DrawerLayout
的布局捆蜀,本文主要描述主要的幾個方法,想了解其他內(nèi)容幔嫂,自行查看源碼
以上源碼來自api 23辆它,如果有不正確的地方,歡迎指正