前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GitHub 精选| 如何优雅的实现骨架屏?

GitHub 精选| 如何优雅的实现骨架屏?

作者头像
路遥TM
发布2022-03-29 19:21:53
1.7K0
发布2022-03-29 19:21:53
举报
文章被收录于专栏:路遥的专栏

大家好,我是路遥,每周五给你推荐一个泛移动端优质 Github 项目。

Github 精选第二期的主角是 AndroidVeil,一个简单,灵活,低侵入的骨架屏方案

Author

https://github.com/skydoves

Url

https://github.com/skydoves/AndroidVeil

Language

Kotlin

Star

999

Fork

79

Issue

3 Open/10 Closed

Commits

113

Last Update

16 Nov 2021

License

Apache-2.0

以上数据截止至 2022 年 2 月 24 日。

预览

使用

首先,引入依赖。

在工程根目录的 build.gradle 文件添加:

代码语言:javascript
复制
allprojects {
    repositories {
        mavenCentral()
    }
}

在模块的 build.gradle 文件添加:

代码语言:javascript
复制
implementation "com.github.skydoves:androidveil:1.1.2"

AndroidVeil 支持在 RecyclerView 和任意布局中使用骨架屏效果。

在任意布局中使用

代码语言:javascript
复制
<com.skydoves.androidveil.VeilLayout
      android:id="@+id/veilLayout"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:veilLayout_veiled="true" // shows veils initially
      app:veilLayout_shimmerEnable="true" // 初始状态下是否开启骨架屏
      app:veilLayout_baseColor="@android:color/holo_green_dark" // sets shimmer base color
      app:veilLayout_highlightColor="@android:color/holo_green_light" // sets shimmer highlight color
      app:veilLayout_baseAlpha="0.6" // sets shimmer base alpha value
      app:veilLayout_highlightAlpha="1.0" // sets shimmer highlight alpha value
      app:veilLayout_dropOff="0.5"// sets how quickly the shimmer`s gradient drops-off
      app:veilLayout_radius="6dp" // sets a corner radius of the whole veiled items >

      <TextView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="And now here is my secret, a very simple secret"
         android:textColor="@android:color/white"
         android:textSize="22sp"/>

      <!-- Skip -->    

</com.skydoves.androidveil.VeilLayout>

VeilLayout 中的所有子 View 都会支持骨架屏的闪烁效果。聪明的你应该能想到是怎么实现的了吧!

如果在初始状态下没有开启骨架屏,也可以通过代码控制开关。

代码语言:javascript
复制
veilLayout.veil()
veilLayout.unVeil()

另外,也可以自由设置骨架屏的布局。

代码语言:javascript
复制
veilLayout.layout = R.layout.layout_item_test

在 RecyclerView 中使用

代码语言:javascript
复制
<com.skydoves.androidveil.VeilRecyclerFrameView
        android:id="@+id/veilRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:veilFrame_layout="@layout/item_profile" // sets to make veiling target layout
        app:veilFrame_veiled="true" // shows veils initially
        app:veilFrame_shimmerEnable="true" // sets shimmer enable
        app:veilFrame_baseColor="@android:color/holo_green_dark" // sets shimmer base color
        app:veilFrame_highlightColor="@android:color/holo_green_light" // sets shimmer highlight color
        app:veilFrame_baseAlpha="0.6" // sets shimmer base alpha value
        app:veilFrame_highlightAlpha="1.0" // sets shimmer highlight alpha value
        app:veilFrame_radius="8dp" // sets a corner radius of the whole veiled items />

VeilRecyclerFrameView 可以当作 RecyclerView 来使用。

代码语言:javascript
复制
veilRecyclerView.setAdapter(adapter) // 设置 adapter
veilRecyclerView.setLayoutManager(LinearLayoutManager(this)) // 设置 LayoutManager
veilRecyclerView.addVeiledItems(15) // 添加骨架屏 item 数量

同时也提供了代码开关骨架屏效果。

代码语言:javascript
复制
veilRecyclerView.veil() 
veilRecyclerView.unVeil() 

但其实 VeilRecyclerFrameView 并不是 RecyclerView,如果想对 RecyclerView 设置更多效果,可以通过 getRecyclerView() 获取。

代码语言:javascript
复制
veilRecyclerView.getRecyclerView() // 真正的 RecyclerView
veilRecyclerView.getVeiledRecyclerView() // 骨架 RecyclerView

透过这两个方法,基本也能把实现原理猜的差不多。

原理

先看 RecyclerView 效果的。

代码语言:javascript
复制
class VeilRecyclerFrameView : RelativeLayout {
 // 用户展示数据用的 RecyclerView
  private val userRecyclerView: RecyclerView = RecyclerView(context)
  // 展示骨架屏的 RecyclerView
  private val veiledRecyclerView: RecyclerView = RecyclerView(context)
  private var veiledAdapter: VeiledAdapter? = null
  private var isVeiled = false

  ...
  
    private fun onCreate() {
    addView(this.userRecyclerView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
    addView(this.veiledRecyclerView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
    this.veiledRecyclerView.setHasFixedSize(true)
    applyOverScrollMode()
    when (this.isVeiled) {
      true -> visibleVeilRecyclerView()
      false -> visibleUserRecyclerView()
    }

    if (this.layout != -1) {
      setVeilLayout(this.layout)
    }
  }
  
  ...
}

其实就是用了两个 Rv,一个提供给用户展示数据,一个用于显示骨架屏效果。之前见过另一种做法是只用一个 Rv,动态切换 adapter 。你觉得哪种实现方法更好呢?欢迎在评论区留言。

VeilLayout 的实现方案稍微复杂一些,但也不难想象,根据原布局中的 View 层级结构,添加对应的骨架屏效果的布局即可。

代码语言:javascript
复制
  override fun onFinishInflate() {
    super.onFinishInflate()
    removeView(shimmerContainer)
    addView(shimmerContainer)
    addMaskElements(this)
  }

onFinishInflate() 方法在布局文件 inflate 结束之后调用,此时添加一个 shimmerContainer,它是一个 ShimmerFrameLayout 对象,是 Facebook 的一个开源库提供的。addMaskElements() 方法根据布局的原 View 层级结构,向 shimmerContainer 中添加对应的 View 。

代码语言:javascript
复制
  private fun addMaskElements(parent: ViewGroup) {
    (0 until parent.childCount).map { parent.getChildAt(it) }.forEach { child ->
      child.post {
        if (child is ViewGroup) {
          addMaskElements(child)
        } else {
          var marginX = 0f
          var marginY = 0f
          var grandParent = parent.parent
          ...
            }
          }

          // create a masked view
          View(context).apply {
            layoutParams = LayoutParams(child.width, child.height)
            x = marginX + parent.x + child.x
            y = marginY + parent.y + child.y
            setBackgroundColor(baseColor)

            background = drawable ?: GradientDrawable().apply {
              setColor(Color.DKGRAY)
              cornerRadius = radius
            }
            maskElements.add(this)
            shimmerContainer.addView(this)
          }
        }
      }
    }

    // Invalidate the whole masked view.
    invalidate()
   ...
  }

Something else

AndroidVeil 中的骨架屏闪烁效果采用了 Facebook 的开源库 shimmer-android ,毕竟有成熟的轮子,没必要自己再造一个。

另外推荐大家关注这个库的作者 skydoves , 贡献了很多优秀的开源项目。例如 Jetpack MVVM 示例项目 Pokedex,颜色选择框架 ColorPickerView,集成了 Glide, Coil, 和 Fresco 的 Jetpack Compose 图片加载库 landscapist 等等。

看看他的 Contribution 图表,简直堪称活在 Github 上,开源世界的活化石。

最后

这一期的介绍就到这里了,我们下周五见。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 路遥TM 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 预览
  • 使用
    • 在任意布局中使用
      • 在 RecyclerView 中使用
      • 原理
      • Something else
      • 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档