知識(shí)
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X(jué)表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!
SlidingDrawer源碼分析
發(fā)表時(shí)間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):20
一屬性變量分析
構(gòu)造函數(shù)完成獲取attr屬性內(nèi)容的攫取,攫取用戶設(shè)備的UI屬性,用于構(gòu)造新的UI構(gòu)造。
屬性內(nèi)容為,留意這里的SlidingShow作者本身定義的,拷自源碼包:
<resources> <declare-styleable name="SlidingShow"> <attr name="handle" format="reference" /> <attr name="content" format="reference" /> <attr name="orientation" format="integer" /> <attr name="bottomOffset" format="dimension" /> <attr name="topOffset" format="dimension" /> <attr name="allowSingleTap" format="boolean" /> <attr name="animateOnClick" format="boolean" /> </declare-styleable> </resources>android:allowSingleTap:指導(dǎo)是否可以經(jīng)由過(guò)程handle打開(kāi)或封閉
android:animateOnClick:指導(dǎo)是否當(dāng)應(yīng)用者按下手柄打開(kāi)/封閉時(shí)是否該有一個(gè)動(dòng)畫。
android:content:隱蔽的內(nèi)容
android:handle:handle(手柄)
二源碼情景分析
在分析源碼前,先要選擇一個(gè)分析次序,次序按照ViewGroup繪制周期來(lái)進(jìn)展。
[img]http://img.blog.csdn.net/20141228174928045
根據(jù)viewGroup的繪制次序開(kāi)端分析源碼。
2.1 測(cè)量函數(shù)
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {(1) int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); final View handle = mHandle; measureChild(handle, widthMeasureSpec, heightMeasureSpec);//(2緝獲取child View的大年夜小 //定義mContent大年夜小 if (mVertical) { int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;(3) mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } else { int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset; mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY)); } setMeasuredDimension(widthSpecSize, heightSpecSize); }(1) widthMeasureSpec和heightMeasureSpec為寬度規(guī)格和高度規(guī)格,可以獲取對(duì)應(yīng)的mode(AT_MOST盡大年夜/ EXACTLY精確/ UNSPECIFIED不限制),可以獲取對(duì)應(yīng)尺寸。
(2) 獲取viewGroup中子視圖的尺寸,這里是獲取handle的View大年夜小。
(3) 根據(jù)對(duì)應(yīng)的偏移量和handle view的大年夜小,設(shè)置Content大年夜小。
2.2 視圖函數(shù)
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) {(1) if (mTracking) { return; } final int width = r - l; //viewGroup的寬度 final int height = b - t; //viewGroup的高度 final View handle = mHandle; int childWidth = handle.getMeasuredWidth(); //handle尺寸 int childHeight = handle.getMeasuredHeight(); int childLeft; int childTop; final View content = mContent; if (mVertical) { childLeft = (width - childWidth) / 2; /** * 設(shè)定hanlde地位,這里handle與mBottomOffset和本身大年夜小有關(guān) * 設(shè)定content地位,這里content與mTopOffset和handle大年夜小有關(guān) */ childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset;(2) //設(shè)定content的地位 content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), mTopOffset + childHeight + content.getMeasuredHeight()); } else { childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset; childTop = (height - childHeight) / 2; content.layout(mTopOffset + childWidth, 0, mTopOffset + childWidth + content.getMeasuredWidth(), content.getMeasuredHeight()); } //肯定handle地位 handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);(3) mHandleHeight = handle.getHeight(); mHandleWidth = handle.getWidth(); }
(1) 參數(shù)changed表示view有新的尺寸或地位;參數(shù)l表示相對(duì)于父view的Left地位;參數(shù)t表示相對(duì)于父view的Top地位;參數(shù)r表示相對(duì)于父view的Right地位;參數(shù)b表示相對(duì)于父view的Bottom地位。
(2) 問(wèn)號(hào)表達(dá)式,根據(jù)前提展開(kāi)或是緊縮決定handle的Top地位。
(3) 肯定handle地位,包含了展開(kāi)和緊縮的剖斷,這里Content地位還不太對(duì),還顯示在expanded(展開(kāi)狀況)。
2.3 繪制圖像函數(shù)
@Override protected void dispatchDraw(Canvas canvas) { //繪制兩個(gè)子view final long drawingTime = getDrawingTime(); //獲取當(dāng)前GPU繪制view時(shí)光,不是日期時(shí)光(1) final View handle = mHandle; final boolean isVertical = mVertical; drawChild(canvas, handle, drawingTime);(1) if (mTracking || mAnimating) { //斷定當(dāng)緇ご態(tài)是否為追蹤或者產(chǎn)活潑畫狀況 final Bitmap cache = mContent.getDrawingCache(); if (cache != null) { if (isVertical) { canvas.drawBitmap(cache, 0, handle.getBottom(), null); } else { canvas.drawBitmap(cache, handle.getRight(), 0, null); } } else { canvas.save(); canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset, isVertical ? handle.getTop() - mTopOffset : 0);(2) drawChild(canvas, mContent, drawingTime); canvas.restore(); //更改save辦法前所有的繪制修改 } } else if (mExpanded) { drawChild(canvas, mContent, drawingTime);(3) } }
(1)根據(jù)當(dāng)前設(shè)備的canvas繪制handle
(2)更改響應(yīng)的canvas設(shè)備,用戶繪制content
(3)根據(jù)當(dāng)前設(shè)備的canvas繪制content
2.4 完成繪制函數(shù)
@Override protected void onFinishInflate() { mHandle = findViewById(mHandleId); if (mHandle == null) { throw new IllegalArgumentException("The handle attribute is must refer to an" + " existing child."); } mHandle.setOnClickListener(new DrawerToggler());//設(shè)置單擊監(jiān)聽(tīng)類(1) mContent = findViewById(mContentId); if (mContent == null) { throw new IllegalArgumentException("The content attribute is must refer to an" + " existing child."); } mContent.setVisibility(View.GONE); }
(1)設(shè)置監(jiān)聽(tīng)類,內(nèi)部設(shè)置響應(yīng)的點(diǎn)擊事宜動(dòng)畫。
2.4.1 監(jiān)聽(tīng)類的解析
private class DrawerToggler implements OnClickListener { public void onClick(View v) { if (mLocked) {(1) return; } // mAllowSingleTap isn't relevant here; you're *always* // allowed to open/close the drawer by clicking with the // trackball. //android:allowSingleTap:指導(dǎo)是否可以經(jīng)由過(guò)程handle打開(kāi)或封閉 //android:animateOnClick:指導(dǎo)是否當(dāng)應(yīng)用者按下手柄打開(kāi)/封閉時(shí)是否該有一個(gè)動(dòng)畫。 if (mAnimateOnClick) {(2) animateToggle();(3) } else { toggle();(4) } } }(1)斷定是否將view鎖住,不許可點(diǎn)擊。
(2)斷定是否應(yīng)用單擊動(dòng)畫,可以不應(yīng)用,可以應(yīng)用。
(3)animateToggle()辦法,單擊后的動(dòng)畫效不雅
public void animateToggle() { if (!mExpanded) { animateOpen(); } else { animateClose(); } }展開(kāi)和緊縮兩種動(dòng)畫效不雅:
public void animateOpen() { prepareContent(); //預(yù)備content(-1) final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; if (scrollListener != null) { scrollListener.onScrollStarted();//調(diào)用onScrollStarted函數(shù) } animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());//展開(kāi)動(dòng)畫(-2) sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);//設(shè)定當(dāng)前的accessibilityEvent if (scrollListener != null) { scrollListener.onScrollEnded(); //調(diào)用onScrollEnded函數(shù) } }(-1)預(yù)備content
private void prepareContent() { if (mAnimating) { return; } // Something changed in the content, we need to honor the layout request // before creating the cached bitmap final View content = mContent; if (content.isLayoutRequested()) { if (mVertical) { final int childHeight = mHandleHeight; int height = getBottom() - getTop() - childHeight - mTopOffset; content.measure(MeasureSpec.makeMeasureSpec(getRight() - getLeft(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), mTopOffset + childHeight + content.getMeasuredHeight()); } else { final int childWidth = mHandle.getWidth(); int width = getRight() - getLeft() - childWidth - mTopOffset; content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getBottom() - getTop(), MeasureSpec.EXACTLY)); content.layout(childWidth + mTopOffset, 0, mTopOffset + childWidth + content.getMeasuredWidth(), content.getMeasuredHeight()); } } // Try only once... we should really loop but it's not a big deal // if the draw was cancelled, it will only be temporary anyway content.getViewTreeObserver().dispatchOnPreDraw(); if (!content.isHardwareAccelerated()) content.buildDrawingCache(); content.setVisibility(View.GONE); // mContent.setVisibility(View.VISIBLE); }從新繪制content,設(shè)計(jì)響應(yīng)的measure() layout()等辦法。 跟onLayout(boolean changed, int l, int t, int r, int b)辦法中繪制content雷同。
(-2)動(dòng)畫展開(kāi)aimateOpen(boolean)辦法
private void animateOpen(int position) { prepareTracking(position);//預(yù)備路徑 performFling(position, -mMaximumAcceleration, true);//履行跳動(dòng) }
private void prepareTracking(int position) { mTracking = true;//設(shè)置標(biāo)記位 mVelocityTracker = VelocityTracker.obtain();(--1) boolean opening = !mExpanded; if (opening) { mAnimatedAcceleration = mMaximumAcceleration; //加快度設(shè)定 mAnimatedVelocity = mMaximumMajorVelocity; //最趕緊度設(shè)定 mAnimationPosition = mBottomOffset + (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth); moveHandle((int) mAnimationPosition); //移動(dòng)動(dòng)畫(--2) mAnimating = true; mHandler.removeMessages(MSG_ANIMATE);(--3) long now = SystemClock.uptimeMillis(); mAnimationLastTime = now; //記錄時(shí)光 mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION; mAnimating = true; } else { if (mAnimating) { mAnimating = false; mHandler.removeMessages(MSG_ANIMATE); } moveHandle(position);(--4) } }(--1) 速度追蹤器獲取
(--2) 處理移動(dòng)操作,moveHandle
private void moveHandle(int position) { final View handle = mHandle; if (mVertical) { if (position == EXPANDED_FULL_OPEN) {//完全展開(kāi) handle.offsetTopAndBottom(mTopOffset - handle.getTop());//設(shè)定程度偏移量 invalidate(); } else if (position == COLLAPSED_FULL_CLOSED) {//完全封閉 handle.offsetTopAndBottom(mBottomOffset + getBottom() - getTop() - mHandleHeight - handle.getTop()); invalidate(); } else { final int top = handle.getTop();//中心狀況 int deltaY = position - top; if (position < mTopOffset) { deltaY = mTopOffset - top; } else if (deltaY > mBottomOffset + getBottom() - getTop() - mHandleHeight - top) { deltaY = mBottomOffset + getBottom() - getTop() - mHandleHeight - top; } handle.offsetTopAndBottom(deltaY); final Rect frame = mFrame; final Rect region = mInvalidate; handle.getHitRect(frame); region.set(frame); region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY); region.union(0, frame.bottom - deltaY, getWidth(), frame.bottom - deltaY + mContent.getHeight()); invalidate(region); } } else { ...... } }(--3) 移除動(dòng)畫消息
(--4) 移動(dòng)到響應(yīng)地位
2.4.1總結(jié)animateClose();和animateOpen();根本上一樣這里就不在描述了。toggle();更是簡(jiǎn)單了很多,沒(méi)有對(duì)應(yīng)的動(dòng)畫,這里也不再分析。
2.5 滑動(dòng)事宜處理
前面的介紹中,起首描述若何繪制一個(gè)View,并給出了繪制次序;后來(lái)設(shè)計(jì)了響應(yīng)的點(diǎn)擊事宜處理,并供給了有動(dòng)畫和無(wú)動(dòng)畫兩種情況下的處理函數(shù);那么最后則是處理滑動(dòng)或者多點(diǎn)觸控的事宜。這里會(huì)用到兩個(gè)辦法,都是viewGroup的辦法,分別是onInterceptTouchEvent()和onTouchEvent()辦法,履行次序遵守下面五點(diǎn):
1. down事宜起首會(huì)傳遞到onInterceptTouchEvent()辦法
2. 如不雅該ViewGroup的onInterceptTouchEvent()在接收到down事宜處理完成之后return false,那么后續(xù)的move, up等事宜將持續(xù)會(huì)先傳遞給該ViewGroup,之后才和down事宜一樣傳遞給最終的目標(biāo)view的onTouchEvent()處理。
3. 如不雅該ViewGroup的onInterceptTouchEvent()在接收到down事宜處理完成之后return true,那么后續(xù)的move, up等事宜將不再傳遞給onInterceptTouchEvent(),而是和down事宜一樣傳遞給該ViewGroup的onTouchEvent()處理,留意,目標(biāo)view將接收不到任何事宜。
4. 如不雅最終須要處理事宜的view的onTouchEvent()返回了false,那么該事宜將被傳遞至其上一層次的view的onTouchEvent()處理。
5. 如不雅最終須要處理事宜的view 的onTouchEvent()返回了true,那么后續(xù)事宜將可以持續(xù)傳遞給該view的onTouchEvent()處理。
@Override public boolean onInterceptTouchEvent(MotionEvent event) { if (mLocked) { return false; } final int action = event.getAction(); float x = event.getX(); float y = event.getY(); final Rect frame = mFrame; final View handle = mHandle; handle.getHitRect(frame);//找到控件占據(jù)的矩形區(qū)域的矩形坐標(biāo) if (!mTracking && !frame.contains((int) x, (int) y)) { return false; } if (action == MotionEvent.ACTION_DOWN) { mTracking = true;//籌劃路徑中 handle.setPressed(true); // Must be called before prepareTracking() prepareContent(); // Must be called after prepareContent() if (mOnDrawerScrollListener != null) { mOnDrawerScrollListener.onScrollStarted(); } if (mVertical) { final int top = mHandle.getTop(); mTouchDelta = (int) y - top; prepareTracking(top);//設(shè)定當(dāng)前地位 } else { final int left = mHandle.getLeft(); mTouchDelta = (int) x - left; prepareTracking(left); } mVelocityTracker.addMovement(event); } return true;//返回true,Event交由onTouchEvent處理 }
@Override public boolean onTouchEvent(MotionEvent event) { if (mLocked) { return true; } if (mTracking) { mVelocityTracker.addMovement(event); final int action = event.getAction(); switch (action) { case MotionEvent.ACTION_MOVE://移動(dòng)操作 moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(mVelocityUnits); float yVelocity = velocityTracker.getYVelocity(); float xVelocity = velocityTracker.getXVelocity();//計(jì)算路徑的點(diǎn) boolean negative; final boolean vertical = mVertical; if (vertical) { negative = yVelocity < 0; if (xVelocity < 0) { xVelocity = -xVelocity; } if (xVelocity > mMaximumMinorVelocity) { xVelocity = mMaximumMinorVelocity; } } else { negative = xVelocity < 0; if (yVelocity < 0) { yVelocity = -yVelocity; } if (yVelocity > mMaximumMinorVelocity) { yVelocity = mMaximumMinorVelocity; } } float velocity = (float) Math.hypot(xVelocity, yVelocity);// sqrt(x2+ y2). if (negative) { velocity = -velocity; } final int top = mHandle.getTop(); final int left = mHandle.getLeft(); if (Math.abs(velocity) < mMaximumTapVelocity) { if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) || (!mExpanded && top > mBottomOffset + getBottom() - getTop() - mHandleHeight - mTapThreshold) : (mExpanded && left < mTapThreshold + mTopOffset) || (!mExpanded && left > mBottomOffset + getRight() - getLeft() - mHandleWidth - mTapThreshold)) { if (mAllowSingleTap) {//是否經(jīng)由過(guò)程點(diǎn)擊打開(kāi) playSoundEffect(SoundEffectConstants.CLICK); if (mExpanded) { animateClose(vertical ? top : left); } else { animateOpen(vertical ? top : left); } } else { performFling(vertical ? top : left, velocity, false);//履行松開(kāi)手的后面活動(dòng)(1) } } else { performFling(vertical ? top : left, velocity, false); } } else { performFling(vertical ? top : left, velocity, false); } } break; } } return mTracking || mAnimating || super.onTouchEvent(event); }(1)松開(kāi)手后的處理函數(shù)
private void performFling(int position, float velocity, boolean always) { mAnimationPosition = position; mAnimatedVelocity = velocity; //手勢(shì)控制速度 if (mExpanded) { if (always || (velocity > mMaximumMajorVelocity || (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) && velocity > -mMaximumMajorVelocity))) { // We are expanded, but they didn't move sufficiently to cause // us to retract. Animate back to the expanded position. mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are expanded and are now going to animate away. mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } } } else { if (!always && (velocity > mMaximumMajorVelocity || (position > (mVertical ? getHeight() : getWidth()) / 2 && velocity > -mMaximumMajorVelocity))) { // We are collapsed, and they moved enough to allow us to expand. mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are collapsed, but they didn't move sufficiently to cause // us to retract. Animate back to the collapsed position. mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } } } long now = SystemClock.uptimeMillis(); mAnimationLastTime = now; mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION; mAnimating = true; mHandler.removeMessages(MSG_ANIMATE); mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime); stopTracking();//停止動(dòng)畫 }
計(jì)算接下來(lái)活動(dòng)所須要的參量,發(fā)送handler履行后面的動(dòng)畫。
private void doAnimation() { if (mAnimating) { incrementAnimation(); if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1) { mAnimating = false; closeDrawer(); } else if (mAnimationPosition < mTopOffset) { mAnimating = false; openDrawer(); } else { moveHandle((int) mAnimationPosition); mCurrentAnimationTime += ANIMATION_FRAME_DURATION; mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);//輪回消息 } } } private void incrementAnimation() { long now = SystemClock.uptimeMillis(); float t = (now - mAnimationLastTime) / 1000.0f; // ms -> s final float position = mAnimationPosition; final float v = mAnimatedVelocity; // px/s final float a = mAnimatedAcceleration; // px/s/s mAnimationPosition = position + (v * t) + (0.5f * a * t * t); // px mAnimatedVelocity = v + (a * t); // px/s mAnimationLastTime = now; // ms }
private class SlidingHandler extends Handler { public void handleMessage(Message m) { switch (m.what) { case MSG_ANIMATE: doAnimation(); break; } } }
相關(guān)案例查看更多
相關(guān)閱讀
- 汽車回收管理系統(tǒng)
- 云南小程序設(shè)計(jì)
- 云南小程序開(kāi)發(fā)制作
- 云南小程序被騙蔣軍
- 小程序
- asp網(wǎng)站
- 網(wǎng)站維護(hù)
- 小程序制作
- 汽車報(bào)廢回收
- 百度推廣
- 云南網(wǎng)站建設(shè)公司
- 報(bào)廢車回收管理系統(tǒng)
- 云南省住房建設(shè)廳網(wǎng)站
- 網(wǎng)站建設(shè)哪家強(qiáng)
- 云南小程序開(kāi)發(fā)推薦
- 網(wǎng)站建設(shè)公司哪家好
- 網(wǎng)站小程序
- 商標(biāo)注冊(cè)
- 關(guān)鍵詞快速排名
- 報(bào)廢車拆解系統(tǒng)
- 云南網(wǎng)站制作
- 電商網(wǎng)站建設(shè)
- 云南小程序公司
- 模版信息
- 云南網(wǎng)站優(yōu)化公司
- SEO
- 云南衛(wèi)視小程序
- 云南軟件公司
- 服務(wù)器
- 小程序被攻擊