内容简介:在如今获取用户成本越来越高的情况下,好的用户体验能够更好的留住用户。为了提升产品的用户体验,各种技术层出不穷,其中,尤以菊花图以及由它衍生出的各种加载动画最为突出。对于菊花图,想必是又爱又恨。而如今有了比菊花图设计体验更棒的方法,即常看到的那什么是骨架屏尼?它的语义如下:
在如今获取用户成本越来越高的情况下,好的用户体验能够更好的留住用户。为了提升产品的用户体验,各种技术层出不穷,其中,尤以菊花图以及由它衍生出的各种加载动画最为突出。
对于菊花图,想必是又爱又恨。而如今有了比菊花图设计体验更棒的方法,即常看到的 Skeleton Screen Loading
,中文叫做 骨架屏 。
那什么是骨架屏尼?它的语义如下:
即表示在页面完全渲染完成之前,用户会看到一个占位的样式,用以描绘了当前页面的大致框架,加载完成后,最终骨架屏中各个占位部分将被真实的数据替换。
其效果图如下:
本着不重复造轮子的思想,从GitHub
上找了一些骨架屏的实现。当然也可以自己来实现。其最核心就是占位和属性动画的实现。
- 通过
View
或者Adapter
的替换来实现骨架屏是最普遍的方案,该方案需要单独为骨架屏页面进行布局,如果页面过多或者比较复杂,写起来就还是蛮繁琐的。具体实现有 ShimmerRecyclerView 、 Skeleton 及 spruce-android 等开源库。 - 自定义一个
View
来对布局中的每个View
进行一层包裹,当加载数据时则根据View
来绘制骨架,否则显示正常UI。由于该方案需要将每个View
包裹一层,所以会增加额外的布局层次。具体实现有 Skeleton Android 等开源库。
上面就是目前在Android上实现骨架屏的两种方案,下面以 Skeleton
及 Skeleton Android
为例进行讲解。
Skeleton
要想使用 Skeleton
,需要先导入以下两个库。
dependencies { implementation 'com.ethanhua:skeleton:1.1.2' //主要是动画的实现 implementation 'io.supercharge:shimmerlayout:2.1.0' } 复制代码
skeleton
不仅支持在 RecyclerView
上实现骨架屏,也支持在 View
上实现骨架屏。 先来看看在 RecyclerView
上的实现。
recyclerView = findViewById(R.id.recycler); recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); //实际Adapter NewsAdapter adapter = new NewsAdapter(); final SkeletonScreen skeletonScreen = Skeleton.bind(recyclerView) .adapter(adapter)//设置实际adapter .shimmer(true)//是否开启动画 .angle(30)//shimmer的倾斜角度 // .color(R.color.colorAccent)//shimmer的颜色 .frozen(true)//true则表示显示骨架屏时,RecyclerView不可滑动,否则可以滑动 .duration(1200)//动画时间,以毫秒为单位 .count(10)//显示骨架屏时item的个数 .load(R.layout.item_skeleton_news)//骨架屏UI .show(); //default count is 10 recyclerView.postDelayed(new Runnable() { @Override public void run() { skeletonScreen.hide(); } }, 10000);//延迟时间 复制代码
使用还是比较简单的,主要是对动画属性的设置。当调用 show
方法时就会显示骨架屏,调用 hide
就会隐藏骨架屏,显示正常UI。下面就来看看这两个方法的实现。
public class RecyclerViewSkeletonScreen implements SkeletonScreen { //实际Adapter private final RecyclerView.Adapter mActualAdapter; //骨架UI所需Adapter private final SkeletonAdapter mSkeletonAdapter; ... @Override public void show() { //将骨架UI的Adapter设置给RecyclerView mRecyclerView.setAdapter(mSkeletonAdapter); if (!mRecyclerView.isComputingLayout() && mRecyclerViewFrozen) { mRecyclerView.setLayoutFrozen(true); } } @Override public void hide() { //将正常UI的Adapter设置给RecyclerView mRecyclerView.setAdapter(mActualAdapter); } ... } 复制代码
从上面可以看出,在 RecycleView
上实现骨架屏是非常简单的,但需要为骨架屏单独实现一套布局,然后通过两个 Adapter
替换即可。 虽然骨架屏很多时候都是用在列表、表格中使用,但也有在 View
上使用的需求,下面就来看看如何在 View
上实现骨架屏。
View rootView = findViewById(R.id.rootView); skeletonScreen = Skeleton.bind(rootView) .load(R.layout.activity_view_skeleton)//骨架屏UI .duration(1000)//动画时间,以毫秒为单位 .shimmer(true)//是否开启动画 .color(R.color.shimmer_color)//shimmer的颜色 .angle(30)//shimmer的倾斜角度 .show(); MyHandler myHandler = new MyHandler(this); myHandler.sendEmptyMessageDelayed(1, 10000); //关闭骨架屏,显示正常UI skeletonScreen.hide() 复制代码
用法基本上不变,主要变化就在 show
与 hide
这两个方法中。
public class ViewSkeletonScreen implements SkeletonScreen { //View替换的 工具 类 private final ViewReplacer mViewReplacer; //实际View private final View mActualView; ... @Override public void show() { View skeletonLoadingView = generateSkeletonLoadingView(); if (skeletonLoadingView != null) { //使用骨架屏UI替换实际UI mViewReplacer.replace(skeletonLoadingView); } } @Override public void hide() { if (mViewReplacer.getTargetView() instanceof ShimmerLayout) { ((ShimmerLayout) mViewReplacer.getTargetView()).stopShimmerAnimation(); } //移除骨架屏UI,显示实际UI mViewReplacer.restore(); } ... } //View替换实现类 public class ViewReplacer { //实际UI所在的View private final View mSourceView; //骨架屏UI所在View private View mTargetView; ... public void replace(View targetView) { ... if (init()) { mTargetView = targetView; //移除当前View,即实际UI所在View mSourceParentView.removeView(mCurrentView); mTargetView.setId(mSourceViewId); //将骨架屏UI所在View添加进来 mSourceParentView.addView(mTargetView, mSourceViewIndexInParent, mSourceViewLayoutParams); mCurrentView = mTargetView; } } public void restore() { if (mSourceParentView != null) { //移除当前View,即骨架屏UI所在View mSourceParentView.removeView(mCurrentView); //将实际UI所在View添加进来 mSourceParentView.addView(mSourceView, mSourceViewIndexInParent, mSourceViewLayoutParams); mCurrentView = mSourceView; mTargetView = null; mTargetViewResID = -1; } } ... } 复制代码
实现效果如下。
从上面可以看出,在View
上实现骨架屏也是非常简单的,也需要为骨架屏单独写一套布局,然后通过两个
View
替换即可。 从使用及具体实现上可以发现
Skeleton
还是蛮简单的。但最大的缺点就是要专门为骨架屏实现一套布局,比较繁琐。
Skeleton Android
要想使用 Skeleton Android
,首先需要在项目根目录下的 build.gradle
导入存储 Skeleton Android
的仓库。
allprojects { repositories { ... maven { url 'https://jitpack.io' } } } 复制代码
然后在 app
目录下的 build.gradle
文件中导入下面这个库即可。
dependencies { compile 'com.github.rasoulmiri:Skeleton:v1.0.9' } 复制代码
这里有一点需要注意,引用该库会自动引用 appcompat-v7
及 cardview-v7
这两个库且版本可能较低,所以可能会存在版本冲突问题,解决方案如下。
dependencies { implementation ('com.github.rasoulmiri:Skeleton:v1.0.9'){ exclude group: 'com.android.support' } } 复制代码
先来看如何通过 Skeleton Android
在 RecyclerView
上实现骨架屏。 Skeleton Android
相比 Skeleton
最大的区别就是不需要专门为骨架屏实现一套布局,但使用起来就稍微复杂一些。
recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); list = new ArrayList<>(); adapter = new PersonAdapter(this, list, recyclerView, new IsCanSetAdapterListener() { @Override public void isCanSet() { recyclerView.setAdapter(adapter); } }); new Handler().postDelayed(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { list.add("str" + i); } adapter.addMoreDataAndSkeletonFinish(list); } }, 5000); //adapter的实现 public class PersonAdapter extends AdapterSkeleton<String, SimpleRcvViewHolder> { public PersonAdapter(final Context context, final List<String> items, final RecyclerView recyclerView, final IsCanSetAdapterListener IsCanSetAdapterListener) { this.context = context; this.items = items; this.isCanSetAdapterListener = IsCanSetAdapterListener; measureHeightRecyclerViewAndItem(recyclerView, R.layout.item_person);// Set height } @Override public SimpleRcvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new SimpleRcvViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false)); } @Override public void onBindViewHolder(@NonNull SimpleRcvViewHolder holder, int position) { SkeletonGroup skeletonGroup = holder.getView(R.id.skeleton_group); if (skeletonConfig.isSkeletonIsOn()) { //need show s for 2 cards skeletonGroup.setAutoPlay(true); return; } else { skeletonGroup.setShowSkeleton(false); skeletonGroup.finishAnimation(); } } @Override public int getItemCount() { return 50; } } 复制代码
在使用 Skeleton Android
时需要我们自定义的 Adapter
去继承 AdapterSkeleton
,也需要在构造方法里进行高度的测量。所以这样就会限制比较大。再来看布局文件的实现。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_grid_item"> <io.rmiri.skeleton.SkeletonGroup android:id="@+id/skeleton_group" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout ...> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView ... /> </io.rmiri.skeleton.SkeletonView> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView ... /> </io.rmiri.skeleton.SkeletonView> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView ... /> </io.rmiri.skeleton.SkeletonView> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView ... /> </io.rmiri.skeleton.SkeletonView> </LinearLayout> </io.rmiri.skeleton.SkeletonGroup> </LinearLayout> 复制代码
很明显增加了额外的布局层级。下面再来看通过 Skeleton Android
在 View
上实现骨架屏。
skeletonGroup = (SkeletonGroup) findViewById(R.id.skeletonGroup); textTv = (TextView) findViewById(R.id.textTv); skeletonGroup.setSkeletonListener(new SkeletonGroup.SkeletonListener() { @Override public void onStartAnimation() { } @Override public void onFinishAnimation() {//显示加载数据 textTv.setText("The Android O release ultimately became Android 8.0 Oreo, as predicted by pretty much everyone the first time they thought of a sweet"); } }); new Handler().postDelayed(new Runnable() { @Override public void run() { skeletonGroup.finishAnimation(); } }, 5000); 复制代码
比在 RecycleView
上实现骨架屏简单多了,当然,布局文件里也需要将控件进行一层包裹。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:Skeleton="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <TextView ... /> <io.rmiri.skeleton.SkeletonGroup android:id="@+id/skeletonGroup" android:layout_width="match_parent" android:layout_height="wrap_content" Skeleton:SK_BackgroundViewsColor="#EEEEEE" Skeleton:SK_animationAutoStart="true" Skeleton:SK_animationDirection="LTR" Skeleton:SK_animationDuration="1000" Skeleton:SK_animationFinishType="none" Skeleton:SK_animationNormalType="alpha" Skeleton:SK_backgroundMainColor="@android:color/transparent" Skeleton:SK_highLightColor="#DEDEDE"> <LinearLayout ...> <!--Rect--> <LinearLayout ...> <TextView .... /> <io.rmiri.skeleton.SkeletonView android:layout_width="match_parent" android:layout_height="wrap_content" Skeleton:SK_shapeType="rect"> <TextView ... /> </io.rmiri.skeleton.SkeletonView> </LinearLayout> <!--Oval--> <LinearLayout ...> <TextView ... /> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content" Skeleton:SK_shapeType="oval"> <android.support.v7.widget.AppCompatImageButton ... /> </io.rmiri.skeleton.SkeletonView> </LinearLayout> <!--Text--> <LinearLayout ...> <TextView ... /> <io.rmiri.skeleton.SkeletonView android:layout_width="wrap_content" android:layout_height="wrap_content" Skeleton:SK_shapeType="text" Skeleton:SK_textLineHeight="16dp" Skeleton:SK_textLineLastWidth="threeQuarters" Skeleton:SK_textLineNumber="5" Skeleton:SK_textLineSpaceVertical="4dp"> <TextView ... /> </io.rmiri.skeleton.SkeletonView> </LinearLayout> </LinearLayout> </io.rmiri.skeleton.SkeletonGroup> </LinearLayout> 复制代码
实现效果如下。
上面介绍了Skeleton Android
的使用,它的原理基本上就是通过
SkeletonGroup
及
SkeletonView
这两个控件来进行骨架的绘制。
SkeletonGroup
及
SkeletonView
都是继承自
RelativeLayout
的自定义控件,
SkeletonView
起一个标识的作用,在
SkeletonGroup
中会将
SkeletonView
绘制成相应的长方形、圆形等骨架。
总结
前面介绍了骨架屏在Android上的应用。它们的区别主要是需不需要自己来实现骨架屏布局。但是从使用上来说 Skeleton
要比 Skeleton Android
方便很多,扩展性也更好一点。当然我们也可以根据这两种方案的思想来自己实现骨架屏。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
区块链技术驱动金融
阿尔文德·纳拉亚南、约什·贝努、爱德华·费尔顿、安德鲁·米勒、史蒂文·戈德费德 / 林华、王勇 / 中信出版社,中信出版集团 / 2016-8-25 / CNY 79.00
从数字货币及智能合约技术层面,解读了区块链技术在金融领域的运用。“如果你正在寻找一本在技术层面解释比特币是如何运作的,并且你有一定计算机科学和编程的基本知识,这本书应该很适合你。” 《区块链:技术驱动金融》回答了一系列关于比特币如何运用区块链技术运作的问题,并且着重讲述了各种技术功能,以及未来会形成的网络。比特币是如何运作的?它因何而与众不同?你的比特币安全吗?比特币用户如何匿名?区块链如何......一起来看看 《区块链技术驱动金融》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
HTML 编码/解码
HTML 编码/解码