仿微博:實現(xiàn)網(wǎng)絡(luò)未加載數(shù)據(jù)時閃動UI待加載布局

思路剖析:

Skeleton(骨架)UI其實是一種在網(wǎng)絡(luò)數(shù)據(jù)未加載時,一種手動設(shè)置的UI布局凛辣,在網(wǎng)絡(luò)請求完成+數(shù)據(jù)加載完成后,替換UI布局。

因此需要兩套布局layout文件框弛,并且如果采用itemBinding方式,也需要兩套itemBinding對應(yīng)捕捂。

Tips:
這個骨架布局在哪里能快速找到呢瑟枫?相對應(yīng)顏色也不好找呀,Skeleton——github庫的example參考我是直接用的這個里的布局指攒,這是個庫慷妙,但我并未直接引用,下載example看過之后了解思路就是替換adapter+item布局允悦,因此思路清晰膝擂,跟上面我說的邏輯一致,只是本文時使用的itemBinding+Kotlin語言,它是采用adapter+Java語言猿挚。但它的skeleton布局可以直接拿來用或者改改適應(yīng)布局大小咐旧,我這里并未對布局大小做修改(懶~),效果為主绩蜻,ui為輔铣墨,功能實現(xiàn)就成!因此看到跟我的數(shù)據(jù)加載完成后布局大小不匹配很正常办绝,勿杠伊约。

國際慣例看看實現(xiàn)效果:

skeleton.gif

關(guān)掉網(wǎng)絡(luò)讓我們看一下加上閃動效果怎么樣:

shimmer.gif

具體實現(xiàn)

未加載時的布局,也就是我們的skeleton布局文件:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="130dp"
        android:layout_marginBottom="8dp"
        android:background="@android:color/transparent">

                <View
                    android:layout_width="0dp"
                    android:layout_height="1px"
                    android:background="@color/colorLine"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintLeft_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <View
                    android:id="@+id/img_news"
                    android:layout_width="90dp"
                    android:layout_height="0dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginTop="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginBottom="16dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <View
                    android:id="@+id/tv_title"
                    android:layout_width="0dp"
                    android:layout_height="16dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="50dp"
                    android:background="@color/light_transparent"
                    android:text="Google正式發(fā)布Android8.0"
                    app:layout_constraintLeft_toRightOf="@+id/img_news"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="@+id/img_news" />

                <View
                    android:id="@+id/tv_content"
                    android:layout_width="0dp"
                    android:layout_height="14dp"
                    android:layout_marginRight="16dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="@+id/img_news"
                    app:layout_constraintLeft_toLeftOf="@+id/tv_title"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="@+id/img_news" />

                <View
                    android:id="@+id/tv_time"
                    android:layout_width="0dp"
                    android:layout_height="12dp"
                    android:layout_marginRight="50dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="@+id/img_news"
                    app:layout_constraintLeft_toLeftOf="@+id/tv_title"
                    app:layout_constraintRight_toRightOf="parent" />

                <View
                    android:layout_width="0dp"
                    android:layout_height="1px"
                    android:background="@color/colorLine"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintLeft_toRightOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

網(wǎng)絡(luò)數(shù)據(jù)加載后的dataBinding布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:ignore="MissingDefaultResource">

    <data>
        <variable
            name="item"
            type="com.kc.home.response.DataX" />
        <variable
            name="viewModel"
            type="com.kc.home.HomeViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/home_list_item"
        android:layout_width="match_parent"
        android:layout_marginHorizontal="8dp"
        android:onClick="@{()->viewModel.onClickItem(item)}"
        android:layout_marginVertical="2dp"
        android:layout_height="wrap_content"
        android:background="@drawable/shape_white_radius_4dp">

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/left_Guideline"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="10dp" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/right_Guideline"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical"
            app:layout_constraintGuide_end="10dp" />

        <TextView
            android:id="@+id/tv_share_user"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{item.author}"
            tools:text="谷歌開發(fā)者"
            android:textSize="13sp"
            app:layout_constraintStart_toStartOf="@id/left_Guideline"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_share_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginRight="5dp"
            tools:text="時間"
            android:text="@{item.niceShareDate}"
            android:textSize="13sp"
            app:layout_constraintBaseline_toBaselineOf="@+id/tv_share_user"
            app:layout_constraintRight_toRightOf="@id/right_Guideline" />

        <TextView
            android:id="@+id/tv_home_item_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{item.title}"
            tools:text="標題標題標題標題標題標題標題標題標題標題標題標題標題標題"
            android:textColor="@color/black"
            android:textSize="15sp"
            app:layout_constraintEnd_toStartOf="@id/right_Guideline"
            app:layout_constraintStart_toEndOf="@id/left_Guideline"
            app:layout_constraintTop_toBottomOf="@+id/tv_share_user" />

        <TextView
            android:id="@+id/tv_super_chapterName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            tools:text="公眾號/郭霖"
            android:text='@{item.superChapterName+"/"+item.chapterName}'
            android:textSize="13sp"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="8dp"
            app:layout_constraintLeft_toLeftOf="@id/left_Guideline"
            app:layout_constraintTop_toBottomOf="@+id/tv_home_item_title" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

怎么切換兩種布局呢孕蝉?才用標識符在網(wǎng)絡(luò)完成后賦值屡律,借助標識符狀態(tài)切換兩種布局+itemBinding

<androidx.recyclerview.widget.RecyclerView
                itemBinding="@{viewModel.finishLoad?viewModel.itemBinding:viewModel.skeleton}"
                items="@{viewModel.finishLoad?viewModel.items:viewModel.skeletonItem}"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/white_dd"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                tools:itemCount="3"
                tools:listitem="@layout/item_home_article_layout" />

具體的網(wǎng)絡(luò)請求邏輯+標識符賦值操作在ViewModel中完成

class HomeViewModel(application: Application) :BasePageViewModel<DataX>(application) {

    var finishLoad = MutableLiveData(false)

    val skeleton = ItemBinding.of<String>(BR.item, R.layout.item_home_article_skeleton).bindExtra(BR.viewModel, this)
    val skeletonItem = ObservableArrayList<DataX>()
    init {
        initSkeleton()
        refresh()
    }

    override fun requestData(page: Int) {
        NetworkPortal.getService(HomeService::class.java)?.getHomeArticles2(page)?.enqueue(
            LiveDataCallback<HomeArticlesResp>(baseLiveData)
                .bindLoading()
                .bindSmartRefresh()
                .bindStateLayout()
                .doOnResponseSuccess { _, response ->
                    handleItemData(page, response.data.datas)
                    finishLoad.postValue(true)
                }
                .doOnAnyFail { toast("你的網(wǎng)絡(luò)似乎出了點問題喔~") }
        )
    }

    override fun getItemLayoutId(): Int {
        return R.layout.item_home_article_layout
    }

    fun onClickItem(item:DataX){
        ARouter.getInstance().build(RouterActivityPath.WebView.WEBVIEW_ACTIVITY)
            .withString("url",item.link)
            .navigation()
    }

    fun initSkeleton(){   //創(chuàng)建空對象
        val json = "[{},{},{},{},{},{},{}]"
        skeletonItem.addAll(Gson().fromJson<ArrayList<DataX>>(json,object :TypeToken<ArrayList<DataX>>(){}.type))
    }
}

最后:借助第三方庫完成閃動效果:

閃動框架布局github地址:https://github.com/team-supercharge/ShimmerLayout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="130dp"
        android:layout_marginBottom="8dp"
        android:background="@android:color/transparent">

        <io.supercharge.shimmerlayout.ShimmerLayout
            android:id="@+id/shimmerLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal"
            startShimmerAnimation="@{true}"
            app:shimmer_animation_duration="1200">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <View
                    android:layout_width="0dp"
                    android:layout_height="1px"
                    android:background="@color/colorLine"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintLeft_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <View
                    android:id="@+id/img_news"
                    android:layout_width="90dp"
                    android:layout_height="0dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginTop="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginBottom="16dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <View
                    android:id="@+id/tv_title"
                    android:layout_width="0dp"
                    android:layout_height="16dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="50dp"
                    android:background="@color/light_transparent"
                    android:text="Google正式發(fā)布Android8.0"
                    app:layout_constraintLeft_toRightOf="@+id/img_news"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="@+id/img_news" />

                <View
                    android:id="@+id/tv_content"
                    android:layout_width="0dp"
                    android:layout_height="14dp"
                    android:layout_marginRight="16dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="@+id/img_news"
                    app:layout_constraintLeft_toLeftOf="@+id/tv_title"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="@+id/img_news" />

                <View
                    android:id="@+id/tv_time"
                    android:layout_width="0dp"
                    android:layout_height="12dp"
                    android:layout_marginRight="50dp"
                    android:background="@color/light_transparent"
                    app:layout_constraintBottom_toBottomOf="@+id/img_news"
                    app:layout_constraintLeft_toLeftOf="@+id/tv_title"
                    app:layout_constraintRight_toRightOf="parent" />

                <View
                    android:layout_width="0dp"
                    android:layout_height="1px"
                    android:background="@color/colorLine"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintLeft_toRightOf="parent" />
            </androidx.constraintlayout.widget.ConstraintLayout>
        </io.supercharge.shimmerlayout.ShimmerLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

需要獲取id指定開啟閃動動畫效果,但由于我們dataBinding布局降淮,因此采用BindingAdapter方式做自定義屬性開啟

/**
 * @data on 5/25/21 10:22 AM
 * @auther KC
 * @describe 給RecyclerView的item布局增加id指定java代碼的方式超埋,骨架閃動效果實現(xiàn)。
 */
object ShimmerBindingAdapter {
    @JvmStatic
    @BindingAdapter(value = ["startShimmerAnimation"],requireAll = false)
    fun startAnimation(shimmerLayout: ShimmerLayout,state:Boolean){
        if (state){
            shimmerLayout.startShimmerAnimation()
        }
    }
}

之后給ShimmerLayout布局中:增加屬性:startShimmerAnimation="@{true}"佳鳖,完成霍殴!

圖示

skeleton.jpeg

github直通車demo

https://github.com/Kingcool759/KC_MVVMComponent

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市系吩,隨后出現(xiàn)的幾起案子来庭,更是在濱河造成了極大的恐慌,老刑警劉巖穿挨,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件月弛,死亡現(xiàn)場離奇詭異,居然都是意外死亡科盛,警方通過查閱死者的電腦和手機帽衙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贞绵,“玉大人佛寿,你說我怎么就攤上這事〉常” “怎么了冀泻?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜡饵。 經(jīng)常有香客問我弹渔,道長,這世上最難降的妖魔是什么溯祸? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任肢专,我火速辦了婚禮舞肆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘博杖。我一直安慰自己椿胯,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布剃根。 她就那樣靜靜地躺著哩盲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狈醉。 梳的紋絲不亂的頭發(fā)上廉油,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音苗傅,去河邊找鬼抒线。 笑死,一個胖子當著我的面吹牛渣慕,可吹牛的內(nèi)容都是我干的嘶炭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逊桦,長吁一口氣:“原來是場噩夢啊……” “哼眨猎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卫袒,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎单匣,沒想到半個月后夕凝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡户秤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年码秉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸡号。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡转砖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鲸伴,到底是詐尸還是另有隱情府蔗,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布汞窗,位于F島的核電站姓赤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仲吏。R本人自食惡果不足惜不铆,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一蝌焚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧誓斥,春花似錦只洒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泡垃,卻和暖如春析珊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔑穴。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工忠寻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人存和。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓奕剃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捐腿。 傳聞我的和親對象是個殘疾皇子纵朋,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容