博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
骨架屏(Skeleton Screen)在Android中的应用
阅读量:6278 次
发布时间:2019-06-22

本文共 9326 字,大约阅读时间需要 31 分钟。

 在如今获取用户成本越来越高的情况下,好的用户体验能够更好的留住用户。为了提升产品的用户体验,各种技术层出不穷,其中,尤以菊花图以及由它衍生出的各种加载动画最为突出。

 对于菊花图,想必是又爱又恨。而如今有了比菊花图设计体验更棒的方法,即常看到的Skeleton Screen Loading,中文叫做骨架屏

 那什么是骨架屏尼?它的语义如下:

即表示在页面完全渲染完成之前,用户会看到一个占位的样式,用以描绘了当前页面的大致框架,加载完成后,最终骨架屏中各个占位部分将被真实的数据替换。

 其效果图如下:

 本着不重复造轮子的思想,从
GitHub上找了一些骨架屏的实现。当然也可以自己来实现。其最核心就是占位和属性动画的实现。

  • 通过View或者Adapter的替换来实现骨架屏是最普遍的方案,该方案需要单独为骨架屏页面进行布局,如果页面过多或者比较复杂,写起来就还是蛮繁琐的。具体实现有、及等开源库。
  • 自定义一个View来对布局中的每个View进行一层包裹,当加载数据时则根据View来绘制骨架,否则显示正常UI。由于该方案需要将每个View包裹一层,所以会增加额外的布局层次。具体实现有等开源库。

 上面就是目前在Android上实现骨架屏的两种方案,下面以SkeletonSkeleton 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()复制代码

 用法基本上不变,主要变化就在showhide这两个方法中。

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-v7cardview-v7这两个库且版本可能较低,所以可能会存在版本冲突问题,解决方案如下。

dependencies {    implementation ('com.github.rasoulmiri:Skeleton:v1.0.9'){        exclude group: 'com.android.support'    }}复制代码

 先来看如何通过Skeleton AndroidRecyclerView上实现骨架屏。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
{ public PersonAdapter(final Context context, final List
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,也需要在构造方法里进行高度的测量。所以这样就会限制比较大。再来看布局文件的实现。

复制代码

 很明显增加了额外的布局层级。下面再来看通过Skeleton AndroidView上实现骨架屏。

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上实现骨架屏简单多了,当然,布局文件里也需要将控件进行一层包裹。

复制代码

 实现效果如下。

 上面介绍了
Skeleton Android的使用,它的原理基本上就是通过
SkeletonGroup
SkeletonView这两个控件来进行骨架的绘制。
SkeletonGroup
SkeletonView都是继承自
RelativeLayout的自定义控件,
SkeletonView起一个标识的作用,在
SkeletonGroup中会将
SkeletonView绘制成相应的长方形、圆形等骨架。

总结

 前面介绍了骨架屏在Android上的应用。它们的区别主要是需不需要自己来实现骨架屏布局。但是从使用上来说Skeleton要比Skeleton Android方便很多,扩展性也更好一点。当然我们也可以根据这两种方案的思想来自己实现骨架屏。

转载地址:http://ojfva.baihongyu.com/

你可能感兴趣的文章
常用链接
查看>>
pitfall override private method
查看>>
!important 和 * ----hack
查看>>
聊天界面图文混排
查看>>
控件的拖动
查看>>
svn eclipse unable to load default svn client的解决办法
查看>>
Android.mk 文件语法详解
查看>>
QT liunx 工具下载
查看>>
内核源码树
查看>>
Java 5 特性 Instrumentation 实践
查看>>
AppScan使用
查看>>
Java NIO框架Netty教程(三) 字符串消息收发(转)
查看>>
Ucenter 会员同步登录通讯原理
查看>>
php--------获取当前时间、时间戳
查看>>
Spring MVC中文文档翻译发布
查看>>
docker centos环境部署tomcat
查看>>
JavaScript 基础(九): 条件 语句
查看>>
Linux系统固定IP配置
查看>>
配置Quartz
查看>>
Linux 线程实现机制分析
查看>>