最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

RecyclerView 源碼淺析測(cè)量 布局 繪制 預(yù)布局

 更新時(shí)間:2022年12月21日 16:59:17   作者:孫先森Blog  
這篇文章主要介紹了RecyclerView 源碼淺析測(cè)量 布局 繪制 預(yù)布局,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

上一篇博客內(nèi)容對(duì) RecyclerView 回收復(fù)用機(jī)制相關(guān)源碼進(jìn)行了分析,本博客從自定義 View 三大流程 measure、layout、draw 的角度繼續(xù)對(duì) RecyclerView 相關(guān)部分源碼進(jìn)行分析。

onMeasure

onMeasure 中的邏輯大體上分為三種情況,先來(lái)看下源碼:

RecyclerView.java

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
    // 第一種情況:沒(méi)有設(shè)置 LayoutManager
    if (mLayout == null) {
        defaultOnMeasure(widthSpec, heightSpec);
        return;
    }
    // 第二種情況:設(shè)置的 LayoutManager 開(kāi)啟自動(dòng)測(cè)量
    if (mLayout.isAutoMeasureEnabled()) {
        // ...
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        // ...
        mLastAutoMeasureSkippedDueToExact =
                widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
        if (mLastAutoMeasureSkippedDueToExact || mAdapter == null) {
            return;
        }
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
        }
        mLayout.setMeasureSpecs(widthSpec, heightSpec);
        mState.mIsMeasuring = true;
        dispatchLayoutStep2();
        // 需要二次測(cè)量
        if (mLayout.shouldMeasureTwice()) {
            // ...
            dispatchLayoutStep2();
            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        }
        // ...
    } else {  // 第三種情況:設(shè)置的 LayoutManager 沒(méi)有開(kāi)啟自動(dòng)測(cè)量
        // ...
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        // ...
        mState.mInPreLayout = false; // clear
    }
}

源碼只貼出了重要部分,稍微總結(jié)下:

  • 沒(méi)有設(shè)置 LayoutManager 時(shí),調(diào)用 defaultOnMeasure 方法;
  • 設(shè)置 LayoutManager 并且開(kāi)啟自動(dòng)測(cè)量時(shí),調(diào)用 LayoutManager 的 onMeasure 方法,并且會(huì)執(zhí)行 dispatchLayoutStep1()、dispatchLayoutStep2();
  • 設(shè)置 LayoutManager 且沒(méi)有開(kāi)始自動(dòng)測(cè)量時(shí),僅調(diào)用了 LayoutManager 的 onMeasure 方法;

先來(lái)看一下 LayoutManager 的 onMeasure 方法:

RecyclerView.java

public abstract static class LayoutManager{
    // ...
    public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,int heightSpec) {
        mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
    }
}

默認(rèn)實(shí)現(xiàn)和第一種情況一樣,調(diào)用了 defaultOnMeasure 方法,而且 sdk 中給我們提供的三種 LayoutManager(LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager)均沒(méi)有重寫 onMeasure 方法。

接著就來(lái)看看 defaultOnMeasure 的源碼:

RecyclerView.java

void defaultOnMeasure(int widthSpec, int heightSpec) {
    // 通過(guò) LayoutManager.chooseSize 獲取的寬和高的值
    final int width = LayoutManager.chooseSize(widthSpec,
            getPaddingLeft() + getPaddingRight(), // 橫向內(nèi)邊距
            ViewCompat.getMinimumWidth(this)); // 反射獲取是否設(shè)置最小寬度
    final int height = LayoutManager.chooseSize(heightSpec,
            getPaddingTop() + getPaddingBottom(), // 縱向內(nèi)邊距
            ViewCompat.getMinimumHeight(this)); // 反射獲取是否設(shè)置最小寬度
    // 設(shè)置寬高
    setMeasuredDimension(width, height);
}

接著看一下 LayoutManager.chooseSize 是如何獲取寬高的:

public static int chooseSize(int spec, int desired, int min) {
    final int mode = View.MeasureSpec.getMode(spec);
    final int size = View.MeasureSpec.getSize(spec);
    switch (mode) {
        case View.MeasureSpec.EXACTLY:
            return size;
        case View.MeasureSpec.AT_MOST:
            return Math.min(size, Math.max(desired, min));
        case View.MeasureSpec.UNSPECIFIED:
        default:
            return Math.max(desired, min);
    }
}

這段代碼就不用解釋了吧?自定義 View 時(shí)經(jīng)常會(huì)根據(jù) mode 不同來(lái)處理寬高的最終值。

測(cè)量這部分到目前為止的代碼都比較簡(jiǎn)單,測(cè)量對(duì)于寬高這部分并沒(méi)有特殊處理,剩余重要邏輯都在 dispatchLayoutStep1()、dispatchLayoutStep2() 方法中,這里先不對(duì)其進(jìn)行詳細(xì)解釋,因?yàn)橄旅娴?onLayout 中還有一個(gè) dispatchLayoutStep3() 方法。

稍微總結(jié)下,測(cè)量部分除非有特殊的自定義 LayoutManager 對(duì)寬高有自定義需求,一般情況都會(huì)走默認(rèn)的 defaultOnMeasure 方法,和大部分自定義 View 相同根據(jù) mode 確定寬高。

onLayout

自定義 View 的第二大流程 onLayout,直接看源碼:

RecyclerView.java

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
    dispatchLayout(); //  只有一處方法調(diào)用 分發(fā)布局
    TraceCompat.endSection();
    mFirstLayoutComplete = true;
}
void dispatchLayout() {
    // ...
    // 在 onMeasure 中這個(gè)值被設(shè)置為 true
    mState.mIsMeasuring = false;
    // ...
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else if (mAdapterHelper.hasUpdates()
            || needsRemeasureDueToExactSkip
            || mLayout.getWidth() != getWidth()
            || mLayout.getHeight() != getHeight()) {
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else {
        mLayout.setExactMeasureSpecsFrom(this);
    }
    dispatchLayoutStep3(); // mState.mLayoutStep 的值在里面被設(shè)置為 State.STEP_START
}

onLayout 中的邏輯并不復(fù)雜,邏輯都放在了 dispatchLayout 中,而 dispatchLayout 中又根據(jù)各種判斷確保了 dispatchLayoutStep1、dispatchLayoutStep2、dispatchLayoutStep3 都會(huì)執(zhí)行。至于這三個(gè)方法在最后一小節(jié)分析。

onDraw

RecyclerView 重寫了 draw 方法,那么就先看一下 draw 方法:

RecyclerView.java

public void draw(Canvas c) {
    super.draw(c);
    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        mItemDecorations.get(i).onDrawOver(c, this, mState);
    }
    // ...
}

draw 方法中獲取了所有的 ItemDecoration 也就是“分割線”,調(diào)用了其 onDrawOver 方法。關(guān)于 ItemDecoration 將和 LayoutManager 一起在下一篇博客中分析。

draw 的源碼中會(huì)繼續(xù)調(diào)用 onDraw 方法,繼續(xù)看一下 onDraw 方法:

RecyclerView.java

public void onDraw(Canvas c) {
    super.onDraw(c);
    // draw 方法中調(diào)用了 ItemDecoration 的 onDrawOver
    // onDraw 方法又調(diào)用了 ItemDecoration 的 onDraw 
    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        mItemDecorations.get(i).onDraw(c, this, mState);
    }
}

可以看出 draw 和 onDraw 主要對(duì)分割線進(jìn)行了繪制,由于 draw 方法先執(zhí)行,那么也就意味著 ItemDecoration 的 onDrawOver 方法會(huì)先繪制,之后再執(zhí)行其 onDraw 方法。

dispatchLayoutStep1、2、3

三大流程的整體代碼流程并不復(fù)雜,核心邏輯都在 dispatchLayoutStep1、2、3 這三個(gè)方法中,字面意思翻譯過(guò)來(lái)是“分發(fā)布局步驟1、2、3”,下面挨著來(lái)分析下。

dispatchLayoutStep1

概述:處理 Adapter 更新,決定哪個(gè)動(dòng)畫應(yīng)該被執(zhí)行,保存當(dāng)前的視圖信息,如果需要的話進(jìn)行預(yù)布局并保存相關(guān)信息。

RecyclerView.java

private void dispatchLayoutStep1() {
    // 確認(rèn)布局步驟 在 dispatchLayoutStep3 會(huì)設(shè)置為 STEP_START
    // 并且 onLayout 調(diào)用 dispatchLayoutStep1 之前也進(jìn)行了判斷
    mState.assertLayoutStep(State.STEP_START);
    // 獲取剩余的滾動(dòng)距離(橫豎向)
    fillRemainingScrollValues(mState);
    // onMeasure 中標(biāo)記為 true 這里再置為 false
    mState.mIsMeasuring = false;
    startInterceptRequestLayout();
    // ViewInfoStore 用于保存動(dòng)畫相關(guān)信息
    // 清除保存的信息 
    mViewInfoStore.clear();
    // 標(biāo)記進(jìn)入布局或者滾動(dòng)狀態(tài) 內(nèi)部是int類型進(jìn)行++操作
    onEnterLayoutOrScroll();
    // 適配器更新和動(dòng)畫預(yù)處理 設(shè)置 mState.mRunSimpleAnimations 和 mState.mRunPredictiveAnimations 的值
    processAdapterUpdatesAndSetAnimationFlags();
    // 保存焦點(diǎn)信息
    saveFocusInfo();
    // 一些信息保存
    mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
    mItemsAddedOrRemoved = mItemsChanged = false;
    // 預(yù)布局標(biāo)志 和 mRunPredictiveAnimation 有關(guān) 這里先記住 后面會(huì)解釋什么是預(yù)布局
    mState.mInPreLayout = mState.mRunPredictiveAnimations;
    mState.mItemCount = mAdapter.getItemCount();
    findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
    // 下面兩個(gè) if 都是動(dòng)畫預(yù)處理 保存信息等等
    // mRunSimpleAnimations 可以理解為 需要執(zhí)行動(dòng)畫
    if (mState.mRunSimpleAnimations) {
        // ...
        int count = mChildHelper.getChildCount();
        for (int i = 0; i < count; ++i) {
            // ...
            // 保存執(zhí)行動(dòng)畫所需的信息 (預(yù)布局時(shí)的信息)
            mViewInfoStore.addToPreLayout(holder, animationInfo);
            // ...
        }
    }
    // mRunPredictiveAnimations 可以理解為 需要執(zhí)行動(dòng)畫的情況下需要進(jìn)行預(yù)布局
    // 換而言之需要拿到動(dòng)畫執(zhí)行前后的各種信息(坐標(biāo)等等)
    if (mState.mRunPredictiveAnimations) {
        // ...
        // 這里如果需要預(yù)布局就調(diào)用 LayoutManager 的 onLayoutChildren 開(kāi)始布局
        // 注意 mState.mInPreLayout = mRunPredictiveAnimations
        // 當(dāng) mRunPredictiveAnimations 為 ture 時(shí) mInPreLayout 同樣為 true
        mLayout.onLayoutChildren(mRecycler, mState);
        // ...
    } else {
        clearOldPositions();
    }
    onExitLayoutOrScroll();
    // 和 startInterceptRequestLayout 成對(duì)使用 貌似是防止多次 requestLayout
    stopInterceptRequestLayout(false);
    // 標(biāo)記 STEP_START 完成 可以執(zhí)行 dispatchLayoutStep2
    mState.mLayoutStep = State.STEP_LAYOUT;
}

dispatchLayoutStep1 整體上都是預(yù)布局處理,對(duì)動(dòng)畫信息的保存等等,ViewInfoStore 是用于存儲(chǔ) item 動(dòng)畫相關(guān)信息,后面的博客中會(huì)分析。注意重點(diǎn),如果需要執(zhí)行動(dòng)畫將會(huì)執(zhí)行預(yù)布局,也就是調(diào)用 mLayout.onLayoutChildren 之前 mInPreLayout 為 true。 我并沒(méi)有每一行代碼都研究透徹,看源碼也大可不必讀懂每一行代碼,那樣會(huì)越陷越深。

dispatchLayoutStep2

概述:預(yù)布局狀態(tài)結(jié)束,開(kāi)始真正的布局。

RecyclerView.java

private void dispatchLayoutStep2() {
    startInterceptRequestLayout(); // 和 stopInterceptRequestLayout 成對(duì)出現(xiàn)
    onEnterLayoutOrScroll(); // 上面已經(jīng)說(shuō)過(guò)了
    // 判斷 State ,在 dispatchLayoutStep1 已經(jīng)標(biāo)記為 STEP_LAYOUT
    mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
    mAdapterHelper.consumeUpdatesInOnePass();
    mState.mItemCount = mAdapter.getItemCount();
    mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
    if (mPendingSavedState != null && mAdapter.canRestoreState()) {
        if (mPendingSavedState.mLayoutState != null) {
            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
        }
        mPendingSavedState = null;
    }
    // 預(yù)布局標(biāo)記為 fasle
    mState.mInPreLayout = false;
    // 開(kāi)始布局 這里是真正的測(cè)量和布局items
    mLayout.onLayoutChildren(mRecycler, mState);
    mState.mStructureChanged = false;
    mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
    // 標(biāo)記 STEP_ANIMATIONS
    mState.mLayoutStep = State.STEP_ANIMATIONS;
    onExitLayoutOrScroll();
    stopInterceptRequestLayout(false); // 和 startInterceptRequestLayout 成對(duì)出現(xiàn)
}

dispatchLayoutStep2 方法代碼不多,注意重點(diǎn),在調(diào)用 mLayout.onLayoutChildren(mRecycler, mState) 之前將 mState.mInPreLayout 預(yù)布局標(biāo)記為 false。

到這里可以看出預(yù)布局過(guò)程就發(fā)生在 dispatchLayoutStep1、2 之間。

dispatchLayoutStep3

概述:執(zhí)行 item 動(dòng)畫以及布局完成后的收尾工作。

RecyclerView.java

private void dispatchLayoutStep3() {
    // 判斷狀態(tài)是否為 STEP_ANIMATIONS
    mState.assertLayoutStep(State.STEP_ANIMATIONS);
    // 和 stopInterceptRequestLayout 成對(duì)出現(xiàn)
    startInterceptRequestLayout(); 
    // 和 onExitLayoutOrScroll 成對(duì)出現(xiàn)
    onEnterLayoutOrScroll();
    // 標(biāo)記為 STEP_START, 在步驟 1 中會(huì)判斷是否為 STEP_START
    mState.mLayoutStep = State.STEP_START;
    if (mState.mRunSimpleAnimations) {
        for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
            ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            // ...
            // 保存動(dòng)畫信息
            // 布局完成后的坐標(biāo)等等
            mViewInfoStore.addToPostLayout(holder, animationInfo);
            // ...
            }
        }
        // 執(zhí)行 item 動(dòng)畫
        mViewInfoStore.process(mViewInfoProcessCallback);
    }
    // 清理 mAttachedScrap 和 mChangedScraop 緩存
    mLayout.removeAndRecycleScrapInt(mRecycler);
    // item 數(shù)量
    mState.mPreviousLayoutItemCount = mState.mItemCount;
    // 相關(guān)標(biāo)記設(shè)為初始值
    mDataSetHasChangedAfterLayout = false;
    mDispatchItemsChangedEvent = false;
    mState.mRunSimpleAnimations = false;
    mState.mRunPredictiveAnimations = false;
    mLayout.mRequestedSimpleAnimations = false;
    // ...
    // 布局完成回調(diào)
    mLayout.onLayoutCompleted(mState);
    onExitLayoutOrScroll();
    stopInterceptRequestLayout(false);
    // 清理
    mViewInfoStore.clear();
    // ...
}

mAttachedScrap 和 mChangedScrap

在本系列博客第一篇分析回收復(fù)用源碼時(shí)對(duì) mAttachedScrap 和 mChangedScrap 并沒(méi)有詳細(xì)說(shuō)明,到這里可以對(duì)他們倆分析一下了。

在第一篇的回收復(fù)用中,回收部分源碼執(zhí)行時(shí),并沒(méi)有用到 mAttachedScrap 和 mChangedScrap,復(fù)用時(shí)卻優(yōu)先在他們倆容器中尋找緩存。現(xiàn)在在布局步驟3 dispatchLayoutStep3 中也對(duì)其進(jìn)行了清空,那么說(shuō)明在 dispatchLayoutStep3 之前對(duì)其肯定有過(guò)回收的操作。

布局步驟1、2、3中,大部分邏輯都在 mLayout.onLayoutChildren 中,但其是一個(gè)空實(shí)現(xiàn),所以,就以其開(kāi)發(fā)中最常用到的實(shí)現(xiàn)類 LinearLayoutManager 源碼來(lái)分析看看:

LinearLayoutManager.java

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state){
    // ...
    // 
    detachAndScrapAttachedViews(recycler);
    // ...
    // fill 在第一篇博客中提到了 填充布局 算是回收復(fù)用的入口
    fill(recycler, mLayoutState, state, false);
}
public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
    // 從這里可以看出將所有的可見(jiàn)的 item 都回收到了 mAttachedScrap 或者 mChangedScrap 中
    final int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
        final View v = getChildAt(i);
        scrapOrRecycleView(recycler, i, v);
    }
}

在 onLayoutChildren 中對(duì)可見(jiàn)的 item 都進(jìn)行了回收操作,并且緊接著執(zhí)行了 fill 進(jìn)行了填充布局操作。由上述對(duì) dispatchLayout1、2、3 的源碼分析可以得知,dispatchLayout3 對(duì) mAttachedScrap 和 mChangedScrap 進(jìn)行了清空操作,dispatchLayout1 調(diào)用 onLayoutChildren 進(jìn)行預(yù)布局操作,而 dispatchLayout2 調(diào)用 onLayoutChildren 進(jìn)行真正的布局操作。

那么顯而易見(jiàn),mAttachedScrap 和 mChangedScrap 是對(duì)可見(jiàn) item 的緩存,目的在于預(yù)布局、真正的布局階段復(fù)用,不用重新綁定數(shù)據(jù)。

預(yù)布局

上述內(nèi)容中多次提到過(guò)預(yù)布局,到底什么是預(yù)布局?先大概說(shuō)一下預(yù)布局的使用場(chǎng)景,如下圖所示:

假如屏幕中有一個(gè) RecyclerView 且其有三個(gè) item,當(dāng)刪除 item3 時(shí),item4 會(huì)遞補(bǔ)出現(xiàn)在屏幕內(nèi)。這是開(kāi)發(fā)中非常常見(jiàn)的情況吧,一般執(zhí)行刪除或者新增操作,我們都會(huì)添加動(dòng)畫讓其顯得不生硬,那么思考下 item4 是什么時(shí)候添加到屏幕上的呢?

回到 LinearLayoutManager 的 fill 方法查看源碼:

LinearLayoutManager.java

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    //...
    // 可用空間
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    // 當(dāng) remainingSpace > 0 會(huì)繼續(xù)循環(huán)
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        // ...
        // 布局
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        // 計(jì)算可用空間
        // 注意這里的判斷條件
        if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
                || !state.isPreLayout()) {
            layoutState.mAvailable -= layoutChunkResult.mConsumed;
            remainingSpace -= layoutChunkResult.mConsumed;
        }
    }
}

在計(jì)算可用空間時(shí),有三個(gè)判斷條件:

!layoutChunkResult.mIgnoreConsumed

layoutState.mScrapList != null

!state.isPreLayout()

重點(diǎn)看 1 和 3,先說(shuō) 3 吧,如果是預(yù)布局狀態(tài),也就是 dispatchLayoutStep1 調(diào)用進(jìn)來(lái)時(shí)第三個(gè)條件是 false。至于條件 1 還需要看一下 layoutChunk 方法源碼:

LinearLayoutManager.java

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    // ...
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    // ...
    // 源碼最后部分有這么一處判斷
    // 如果 viewholder 被標(biāo)記為了移除或者改變 mIgnoreConsumed 設(shè)為 true
    if (params.isItemRemoved() || params.isItemChanged()) {
        result.mIgnoreConsumed = true;
    }
    result.mFocusable = view.hasFocusable();
}

看完這段代碼再回到上面圖示中的場(chǎng)景:

當(dāng) item3 被刪除時(shí),在預(yù)布局階段它所占用的空間會(huì)忽略不計(jì),那么 fill 方法中在計(jì)算可用空間時(shí)就會(huì)多走一次 while 循環(huán),從而多添加一個(gè) item。

那么 dispatchStep1 即可稱之為預(yù)布局階段,此時(shí)將要移除的 item3 以及即將添加到屏幕上的 item4 的預(yù)布局階段的位置信息等等保存,在 dispatchStep2 真正布局階段保存完成刪除操作后的位置信息等等,即可在 dispatchStep3 中根據(jù)兩個(gè)信息之間的差異做出對(duì)應(yīng)的 item 動(dòng)畫。關(guān)于動(dòng)畫部分后面博客還會(huì)分析,由于篇幅原因暫時(shí)理解到這里。

最后

本篇博客內(nèi)容從自定義 View 的三大流程角度開(kāi)始分析 RecyclerView 相關(guān)源碼,接著牽連出分發(fā)布局的三個(gè)階段以及對(duì)預(yù)布局的理解。關(guān)于動(dòng)畫部分沒(méi)有多提,后面動(dòng)畫部分會(huì)單獨(dú)一篇博客分析。

以上就是RecyclerView 源碼淺析測(cè)量 布局 繪制 預(yù)布局的詳細(xì)內(nèi)容,更多關(guān)于RecyclerView測(cè)量 布局 繪制 預(yù)布局的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Jetpack Compose實(shí)現(xiàn)對(duì)話框和進(jìn)度條實(shí)例解析

    Jetpack Compose實(shí)現(xiàn)對(duì)話框和進(jìn)度條實(shí)例解析

    對(duì)話框和進(jìn)度條其實(shí)并無(wú)多大聯(lián)系,放在一起寫是因?yàn)閮烧叩膬?nèi)容都不多,所以湊到一起,對(duì)話框是我們平時(shí)開(kāi)發(fā)使用得比較多的組件,進(jìn)度條的使用頻率也很高,比如下載文件,上傳文件,處理任務(wù)時(shí)都可以使用進(jìn)度條
    2023-04-04
  • Android 控制wifi 相關(guān)操作實(shí)例

    Android 控制wifi 相關(guān)操作實(shí)例

    本篇文章主要介紹了Android 控制wifi 的開(kāi)發(fā)實(shí)例,并附有實(shí)例源碼等相關(guān)資料,需要的朋友可以參考下
    2016-07-07
  • Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的代碼詳解

    Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的代碼詳解

    小編經(jīng)常遇到這樣的需求類似進(jìn)入開(kāi)發(fā)者模式,即多次點(diǎn)擊后執(zhí)行操作。下面小編通過(guò)實(shí)例代碼給大家講解Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的相關(guān)知識(shí),感興趣的朋友跟隨小編一起學(xué)習(xí)吧
    2018-10-10
  • mui,h5+中實(shí)現(xiàn)控制頁(yè)面load顯示

    mui,h5+中實(shí)現(xiàn)控制頁(yè)面load顯示

    這篇文章主要介紹了mui,h5+中實(shí)現(xiàn)控制頁(yè)面load顯示的相關(guān)代碼寫法和運(yùn)用技巧,需要的朋友參考一下。
    2017-11-11
  • Android中布局保存成圖片實(shí)例代碼

    Android中布局保存成圖片實(shí)例代碼

    本篇文章主要介紹了Android中布局保存成圖片實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • 詳解如何在Flutter中集成華為認(rèn)證服務(wù)

    詳解如何在Flutter中集成華為認(rèn)證服務(wù)

    這篇文章主要介紹了詳解如何在Flutter中集成華為認(rèn)證服務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • android實(shí)現(xiàn)簡(jiǎn)單左滑刪除控件

    android實(shí)現(xiàn)簡(jiǎn)單左滑刪除控件

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)一個(gè)簡(jiǎn)單左滑刪除控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Android TextView顯示Html類解析的網(wǎng)頁(yè)和圖片及自定義標(biāo)簽用法示例

    Android TextView顯示Html類解析的網(wǎng)頁(yè)和圖片及自定義標(biāo)簽用法示例

    這篇文章主要介紹了Android TextView顯示Html類解析的網(wǎng)頁(yè)和圖片及自定義標(biāo)簽用法,實(shí)例分析了Android中TextView控件的使用技巧,需要的朋友可以參考下
    2016-07-07
  • Android打造流暢九宮格抽獎(jiǎng)活動(dòng)效果

    Android打造流暢九宮格抽獎(jiǎng)活動(dòng)效果

    抽獎(jiǎng)活動(dòng)有很多種形式,轉(zhuǎn)盤抽獎(jiǎng),九宮格抽獎(jiǎng),刮刮卡抽獎(jiǎng),這篇文章主要為大家詳細(xì)介紹了如何打造流暢九宮格抽獎(jiǎng)活動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • 分析Android 11.0Settings源碼之主界面加載

    分析Android 11.0Settings源碼之主界面加載

    這篇文章主要介紹了分析Android 11.0Settings源碼之主界面加載,對(duì)Android源碼感興趣的同學(xué),可以著重看一下
    2021-04-04

最新評(píng)論

美姑县| 疏勒县| 龙陵县| 克拉玛依市| 锡林郭勒盟| 郁南县| 沂源县| 嵊州市| 建平县| 汕尾市| 元阳县| 涪陵区| 旌德县| 澄江县| 临夏县| 平安县| 天门市| 芒康县| 阿荣旗| 深泽县| 柘城县| 淅川县| 东兰县| 手机| 施甸县| 鹤山市| 旬阳县| 虎林市| 滨海县| 巧家县| 全南县| 东光县| 甘泉县| 潜江市| 贡嘎县| 闽清县| 施秉县| 涟水县| 唐河县| 丹凤县| 武穴市|