当我们使用RecyclerView和ListView进行布局的时候,为了能够看到布局的实际情况我们需要不停的运行和构建app然后在手机或模拟器上查看布局效果,为了能够看到实际的布局效果我们还需要自己生成模拟数据,或者从网络获取数据,有时候我们只是轻微的修改了一下布局,为了看到具体的布局效果,我们也需要重新运行和构建app,这大大降低了我们的开发效率。更多的时候我们可能遇到的是UI设计师已经给出了UI,但是接口并没有写好,这个时候我们就只能在运行时自己用代码生成一些数据。那么有没有什么方式能够让我们不依赖接口提供数据而快速的搭建和预览RecyclerView和ListView的布局效果的方法呢。接下来我将分享一些我最近刚学习到的快速开发和预览ListView和RecyclerView布局的方法,希望能够帮助你在实际的开发中节省一些时间。
当我们使用RecyclerView或ListView进行布局的时候,让人心烦的问题是我们不能在Studio的布局预览中看到他的真实布局样式。比如说我们现在有一个显示用户个人信息的列表,在这个列表的每一个item中包含了该用户的头像,居住的城市,和加入服务的时间,以及一些用户的描述数据。它的布局像下面这样:
res/layout/item_part1.xml
-------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toStartOf="@id/name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name" />
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@+id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="5"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@id/description"
app:layout_constraintEnd_toStartOf="@id/date"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/name" />
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="end"
app:layout_constraintBaseline_toBaselineOf="@id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/city" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:ellipsize="end"
android:maxLines="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/city" />
</android.support.constraint.ConstraintLayout>
现在我们打开android studio的布局设计器和预览器将看到如下的效果:
仅管在设计器和预览器中我们能够看到item 中的view的大体轮廓,假如我们将布局切换到我们的RecyclerView中我们将看到如下的情行:
我们看到在RecyclerView显示的地方提供了一列简单的item进行占位,RecyclerView和他的Item视图布局并没有建立什么联系,所以
我们无法预览item视图在RecyclerView中的真实效果,下边我们使用一个小技巧来实现在设计器和布局器中显示RecyclerView的真实item布局效果。布局文件如下:
res/layout/fragment_part.xml
-------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_part1" />
tools:listitem="@layout/item_part1"告诉RecyclerView使用布局item_part1作为它自己的item布局。添加这行代码后我们在android studio的预览器中看到的效果如下:
现在我们能够预览RecyclerView在预览器中的布局情况。但这依旧没有达到我们的实际期望。可能很多人都使用过tools:text="..."这个属性,他主要是协助我们在布局的时候快速预览TextView设置text属性的值的时候的显示效果。但是在这儿设置的text属性的值并不会被打包到真实的apk中。仅仅用于在android studio的布局预览器显示预览效果。下边我们给res/layout/item_part1.xml布局中的TextView都加上tools:text="..."属性代码如下:
res/layout/item_part1.xml
-----------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toStartOf="@id/name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name" />
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@+id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="5"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Mark Allison"/>
给RecyclerView的Item布局中的TextView添加tools:text="..."后的布局效果如下:
现在看上去是不是更加好看了一点,同样的我们可以使用tools:src给ImageView设置一张预览图,但是这样做会有一个问题,就是我们需要在我们的app中添加一些真实的图片,而这些图片我们仅仅是在布局的时候作为预览用的,apk在运行时并不会使用他们,但是这些图片会被打包进apk中,这增加了apk的大小。显然我们是难以接受的。那么有没有解决这个问题的方法呢,答案是有的。这就是我今天所要讲的主角,在android studio 3.0及之后的版本中新增了一个Sample Data目录。这个目录主要用于存放我们在布局阶段在android studio中的预览器中预览布局效果所用到的资源,比如我们可以存放TextView的tools:text="..."用到文本资源,ImageView的tools:src="..."用到的图片资源。这些资源并不会被打包打实际的apk中,他们并不会增加apk的大小。像我们之前在TextView的tools:text="..."设置文本后确实能够预览RecyclerView的布局情况,但是你有没有发现他所有的item相应位置显示的文本都是相同的,他并没有提供差异性。我们想看到的效果是他最终的一个效果。各个item中对应位置的TextView的值应该是不同的,对应位置的ImageView显示的图片也是不同的。甚至我想测试TextView中不同长度的文字对于布局的影响,比如文字短的时候是个什么样子,文字特别长的时候布局会不会乱,会不会挤到别的View或者遮盖别的View。这些需求使用Sample Data都能做到。
下边的布局文件使用了sample data目录中的数据,你可能会有疑惑的地方不要着急稍候我会作详细的讲解:
res/layout/item_part1.xml
-------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toStartOf="@id/name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@+id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="5"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@id/description"
app:layout_constraintEnd_toStartOf="@id/date"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/name"
tools:text="@tools:sample/cities" />
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="end"
app:layout_constraintBaseline_toBaselineOf="@id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/city"
tools:text="@tools:sample/date/ddmmyy" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:ellipsize="end"
android:maxLines="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/city"
tools:text="@tools:sample/lorem/random" />
</android.support.constraint.ConstraintLayout>
下边我们来看看在android studio的设计预览器中的最终效果
怎么样是不是很酷,在没有接口的情况下,我们不需要自己生成模拟数据,编写adapter就能在不运行app的情况下,实时的在android studio的设计预览器中对RecyclerView和ListView的实际效果进行预览。下边我将详细介绍sample data的使用方法。
首先我们需要在项目中创建一个sample data目录:
我们看到新创建的sampledata 目录位于app目录下
然后我们在sampledata目录下创建一个名称叫"names"的文件用以存放用户的名字(这里文件名称你可以任意取一个,并不一定非得叫"names")然后我们在这个文件里边输入一些人的名字,像下边这样:
sampledata/names
-------------------------------------------------------------------------
Mark Allison
Sir Reginald Fortescue Crumplington-Smythe
Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso
Millicent Marbles
然后我们将item布局中用于显示用户名的TextView的tools:text的值设为tools:text="@tools:sample/names"具体的代码如下:
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@+id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="5"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@sample/names"/>
同理我们在sampledata目录下在新建一个目录取名叫"avatars"(这个名字是任意的)然后我们在avatars目录下存放几个VectorDrawables资源文件。如下:
然后我们将显示用户头像的ImageView的tools:src设置为tools:src="@sample/avatars"。具体代码如下:
<ImageView
android:id="@+id/avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toStartOf="@id/name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name"
tools:src="@sample/avatars" />
然后我们此时在android studio 的预览器中预览一下RecyclerView的效果如下:
RecyclerView的实际显示效果就出来了,但是呢大家可能有注意到了,在上边我们为用户的用户名提供了一个names文件来提供用户的姓名数据,那我要给用户的地址提供数据不是得在新建一个为提供地址的文件,如果我有很多TextView那我不是要新建很多文件,这个一点都不高雅。别急其实呢sampledata是支持json格式的文件的。我们可以像我们的实际接口数据那样提供一些json格式的数据。
下边我们在sampledata目录中新建一个名为users.json文件,就像下边这样
然后呢我们在users.json文件中编辑一段json数据,就像下边这样:
{
"data": [
{
"city": "Hemel Hempstead, Hertfordshire, UK",
"avatar": "@sample/avatars"
},
{
"city": "Brokenwind, Aberdeenshire, UK",
"avatar": "@sample/avatars"
},
{
"city": "Málaga, España",
"avatar": "@sample/avatars"
},
{
"city": "Batchelors Bump, Essex, UK",
"avatar": "@sample/avatars"
}
]
}
然后我们可以在RecyclerView的item布局中引用这个json文件中的数据,像下边这样
res/layout/item_part1.xml
-------------------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toStartOf="@id/name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name"
tools:src="@sample/users.json/data/avatar" />
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@+id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="5"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@sample/names" />
<TextView
android:id="@+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@id/description"
app:layout_constraintEnd_toStartOf="@id/date"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/name"
tools:text="@sample/users.json/data/city" />
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="end"
app:layout_constraintBaseline_toBaselineOf="@id/city"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/city"
tools:text="@tools:sample/date/ddmmyy" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:ellipsize="end"
android:maxLines="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@+id/city"
tools:text="@tools:sample/lorem/random" />
</android.support.constraint.ConstraintLayout>
我们来看看在android studio的设计器中的预览效果
通过这种方式,我们可以在程序接口还未提供的时候,就先开始按照UI设计图进行实际的布局开发。我们在也不用每修改一点东西就运行程序查看RecyclerView和ListView的显示效果了。并且我们可以在sampledata的文件中按照自己的需要提供各种情况的数据,用以检查布局文件中的各个控件在各种取值情况了的显示情况,以排查在极端情况下可能出现的布局异常。以上就是我这篇文章所有的内容了,希望对您有所帮助。
源码地址:https://github.com/StylingAndroid/ToolTime/tree/Part2
参考文章:
https://blog.stylingandroid.com/tool-time-part-1-2
https://blog.stylingandroid.com/tool-time-part-2
网友评论