您的位置:首页 > 移动开发 > Android开发

DrawerLayout 源码分析

2016-07-17 23:31 429 查看

简介

DrawerLayout
充当窗口内容的顶层容器,允许”抽屉”式的控件可以从窗口的一边或者两边垂直边缘拉出

使用

抽屉的位置或者布局可以通过
android:layout_gravity
子view的属性控制从那边拉出,left/start代表从左边拉出,right/end代表从右侧拉出,需要注意的是只能有一个抽屉控件从窗口的垂直边缘,如果布局中每个垂直窗口有多于一个抽屉控件,将会抛出异常

根布局使用
DrawerLayout
作为第一个主内容布局,主内容布局宽高设置为
match_parent
不用设置
layout_gravity
,然后在主内容布局上添加子控件,并且设置
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

源码分析

构造函数

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>();
}


构造函数中,设置
view group
的初始焦点,根据手机密度计算出
Drawer
margin
值,初始化从左侧边缘拉出来的布局的回掉监听和从右侧边缘拉出来的布局的回掉监听,其中,在
DrawerLayout
的源码是的滑动部分使用的是
ViewDragHelper
,所以要初始化左侧的滑动和右侧的滑动,设置触摸时的焦点,初始化view的List

ViewDragHelper的回调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的状态发生变化时,更新拖拽状态
@Override
public void onViewDragStateChanged(int state) {
updateDrawerState(mAbsGravity, state, mDragger.getCapturedView());
}
// 当view的位置发生变化时,重新布局
@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);
}
}
// 被拖拽的被回掉时调用,先获得子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();
}
//触摸到边缘时回掉函数
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
postDelayed(mPeekRunnable, PEEK_DELAY);
}
// 根据拖拽的方向计算出view的左侧位置,判断是哪个方向滑动,如果是单侧划定关闭另一侧的view,取消另一侧的滑动
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;
}
// 触摸边缘开始时调用此方法,先根据滑动方向获得当前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
,还有几种情况也要拦截,如果左侧拖转的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方法

@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
,根据子view的
gravity
属性计算
childLeft
newOffset
,如果子view是垂直方向的,根据
gravity
属性计算
top
and
bottom


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
,画右侧的view阴影部分,如果view的
gravity
值为
Gravity.RIGHT
画左侧的view阴影部分

DrawerLayout
的主要功能就是滑动,源码中使用了
ViewDragHelper
实现了滑动,具体不了解的地方可以去看
ViewDragHelper
源码,
DrawerLayout
继承自
ViewGroup
,所以要去计算自身以及子view的宽高,以及实现子view在
DrawerLayout
的布局,本文主要描述主要的几个方法,想了解其他内容,自行查看源码

以上源码来自api 23,如果有不正确的地方,欢迎指正
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码 布局