【发起收藏】17个XML结构小本领

源代码 2024-9-19 10:53:13 94 0 来自 中国
媒介

我们开发时打仗最多的就是xml结构了,还记得我们写Android的第一个Hello World吗,就是通过activity_main.xml表现出来的。
固然xml写的许多,而且也没有什么技能难度,但是,这也每每是我们最轻易忽略的地方,写xml不难,写出好的xml还是得下点功夫了。
什么算是好的xml结构呢,我以为核心有两点,一个是提升开发服从,另一个是提升app性能。围绕着这两点,我也经心整理出了17个xml结构小本领,下面一起来看看都有哪些,你又把握了几个呢?
Space

官网是这么先容的:
Space 是一个轻量级的 View 子类,可用于在通用结构中创建组件之间的间距。
为什么说是轻量级呢,是由于Space的draw方法是空的,也就是什么都不绘制,只有onMeasure方法丈量宽高。
来看下源码:
public final class Space extends View {    /**     * Draw nothing.     *     * @param canvas an unused parameter.     */    @Override    public void draw(Canvas canvas) {    }    //...    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));    }}以是Space作用于组件之间的间距时,绘制服从更高,特殊是在须要动态修改间距时,这点尤为表现。
比如你要动态修改组件的margin,如果用Space来当间距,只须要修改Space的宽度或高度即可,由于镌汰了绘制流程,以是比重绘其他组件更高效。
使用起来也很简单:
<Space    android:id="@+id/space"    android:layout_width="20dp"    android:layout_height="20dp"/>如果你想,Space完全可以更换margin,但是不肯定能更换padding,由于padding是内边距,如果padding有配景致的话,就不能用Space取代了,由于Space的draw方法什么都不绘制的缘故原由,以是也不会有配景致,除非配景致是在父view里设置的。
GuideLine

ConstraintLayout自2018年发布第一个正式版本以来,已经4年多了,它通过扁平化的结构方式,有效的办理了层级嵌套的题目,不但比RelativeLayout更机动,而且性能上更佳,再共同上可视化工具拖拽编辑,服从上也有大大的提升,如果你还没有效上,发起你肯定要实验一下。
而在使用ConstraintLayout的过程中,我发现有些同学总是会忽略GuideLine,只管ConstraintLayout已经非常好用了,但是有些结构仍旧显得有些「鸠拙」。而如果你能妙用GuideLine,你会发现,结构越来越简单,适配也越来越方便。
GuideLine是ConstraintLayout结构的辅助对象,仅用于结构定位使用,它被标志了View.GONE,并不会表现在设备上。
来看下源码:
public class Guideline extends View {    public Guideline(Context context) {        super(context);        super.setVisibility(View.GONE);    }    public Guideline(Context context, AttributeSet attrs) {        super(context, attrs);        super.setVisibility(View.GONE);    }    public Guideline(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        super.setVisibility(View.GONE);    }    public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr);        super.setVisibility(View.GONE);    }    //...    @SuppressLint("MissingSuperCall")    @Override    public void draw(Canvas canvas) {    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(0, 0);    }    //...}标志为View.GONE是这句super.setVisibility(View.GONE)设置的默认值,不表现还是由于draw方法为空,跟上面的Space同出一辙。
GuideLine可以通过3种差别的方式来辅助定位:

  • layout_constraintGuide_begin 指定距结构左侧或顶部的固定间隔
  • layout_constraintGuide_end 指定距结构右侧或底部的固定间隔
  • layout_constraintGuide_percent 指定结构宽度或高度的百分比
同时也可以指定差别的方向:

  • horizontal 垂直参考线
  • vertical 程度参考线
下面简单演示一下效果:
1.png

  • 箭头所指处即创建GuideLine的地方,固然也不止GuideLine,比如另有Barrier
  • 第一个红框里是程度参考线,70%定位,用百分比能很好的办理适配题目,而我们通例的做法是使用LinearLayout嵌套然后设置子view的weight,固然嵌套一层不多,但那也是嵌套,就像有身一样,你不能说只怀了一点点...
  • 第二个红框里是垂直参考线,间隔左边30dp,这种环境得当多个子view向一个目的间隔对齐,同样镌汰了层级嵌套题目,省得再嵌套一层设置padding,大概多个子view分别设置margin。而右边如果想要指定一个位置换行,可以了解一下Barrier~
xml代码就不贴了,已上传到Github,点击查察
include

当我们在写一个复杂的页面时,xml代码大概有几百行以致几千行,阅读起来总是很贫苦,如果又有许多的RelativeLayout嵌套的话,各个组件之间依靠关系错综复杂,看起来更是头大,这时间就可以思量抽取一波,用总分总的模式分为header、content、footer,进一步把内容区抽成一个一个的独立的子layout,然后使用include标签把它们分别引进根结构,这就跟我们项目架构设计一个意思,一个壳工程加n个子模块。子layout只须要负责处置惩罚好自己内部的结构,统筹交给父layout,如许总体就比力清楚,想了解细节再去看子layout即可。
比如:
<include layout="@layout/content_scrolling"/>content_scrolling便是我们抽出去的子layout。
tools:showIn

这个属性一样平常是共同include标签使用的。当我们把子layout抽出去之后,它的结构是相对独立的效果,但是总归要include到根结构的,如果能在子layout结构的时间看到它在父layout内里的效果,那就事半功倍了。
上面的content_scrolling.xml:
现实上结构只有一个TextView,但是在预览视图中还可以看到FloatingActionButton,这就是使用了tools:showIn属性,当子layout嵌入在父layout中时,只须要使用tools:showIn在子layout的根结构指定父layout,就可以及时预览在父layout中的效果了。
<?xml version="1.0" encoding="utf-8"?><androidx.core.widget.NestedScrollView    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="match_parent"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.yechaoa.materialdesign.activity.CollapsingToolbarActivity"    tools:showIn="@layout/activity_collapsing_toolbar">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_margin="@dimen/text_margin"        android:text="@string/large_text"/></androidx.core.widget.NestedScrollView>即:tools:showIn="@layout/activity_collapsing_toolbar"。
ViewStub

ViewStub是一个轻量级的条件视图组件。在做雷同页面秒开这类性能优化时,是比力常见的延长加载本领。
轻量级是由于ViewStub跟Space一样draw方法为空。
条件视图的场景比如,当我们须要根据条件判定来表现哪个view的时间,一样平常都会把每个场景的view都写在页面中,然后根据条件分别设置view的visibility,如许做的缺点是,纵然view是View.GONE,但是在页面渲染加载的时间仍会实例化创建对象,并初始化它的属性,很明显这是浪费资源的,以是这个时间用ViewStub是一个很好的优化本领。
当我们明白知道须要表现哪个view的时间,通过ViewStub把现实视图inflate进来,如许就制止了资源浪费。
只有调用了ViewStub.inflate()的时间结构才会加载,才会创建对象实例化。
示例:
    <ViewStub        android:id="@+id/stub_import"        android:inflatedId="@+id/panel_import"        android:layout="@layout/progress_overlay"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_gravity="bottom" />inflate:
    findViewById<View>(R.id.stub_import).visibility = View.VISIBLE    // or    val importPanel: View = findViewById<ViewStub>(R.id.stub_import).inflate()tools:text

TextView是我们使用的最多的一个组件了,经常有如许的需求,“标题表现不下用...取代”,是不是很认识。
如果标题是一个动态数据,默认表现app name,拿到数据后再更新。这种环境,一样平常都是在android:text内里加字符来调试,调试完了再改成默认的app name,实在也不消这么贫苦,直接默认app name,然后使用tools:text属性就可以预览字符超限的效果。
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/item_textView"    android:layout_width="100dp"    android:layout_height="wrap_content"    android:maxLines="1"    android:ellipsize="end"    android:text="TextView"    tools:text="TextViewTextView" />默认文案还是用android:text表现,超限的效果用tools:text预览即可,现实效果还是android:text,tools:text只是方便我们调试预览,进步服从,镌汰编译期待时间。
tools:visibility

这个属性是用来预览不表现的View。
比如在“个人中心”页面须要在昵称背面给个文案提示“开通会员”,默认不表现,即android:visibility="gone",判定不是会员后才表现文案,但是在开发的过程中须要调试会员和非会员的两种表现效果,即可以通过tools:visibility="visible"来预览表现的效果,省得再编译运行造数据了,方便又提效。
代码示例:
        <TextView            tools:visibility="visible"            android:visibility="gone"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@color/greenPrimary"            android:gravity="center"            android:padding="10dp"            android:text="开通会员"            android:textColor="@color/white" />RecyclerView

RecyclerView也是我们使用非常高频的一个组件了,一样平常会在xml中这么界说RecyclerView:
    <androidx.recyclerview.widget.RecyclerView        android:id="@+id/recycleView"        android:layout_width="match_parent"        android:layout_height="wrap_content" />效果是如许的:
4.png 如许实在完全看不出RecyclerView在页面中表现的效果,只能每次编译运行看效果,而每次编译运行无疑会耗费我们许多名贵的时间,下面就先容几个可以资助大家提效的属性。
tools:listitem

我们可以通过设置tools:listitem属性来预览item的表现效果,tools:listitem属性指定的是一个layout
tools:listitem="@layout/item_main"效果:
tools:itemCount

预览item在RecyclerView中表现设置数目的效果,比如:
tools:itemCount="3"即会表现3个item的效果。
tools:listheader

tools:listheader="@layout/item_header"效果同tools:listitem
tools:listfooter

效果同tools:listitem
tools:listfooter="@layout/item_footer"app:layoutManager

上面RecyclerView的效果是默认垂直方向的,我们都知道RecyclerView必须要设置一个layoutManager才可以表现出来,我们通常会用代码来设置,比如:
mBinding.recycleView.layoutManager = GridLayoutManager(this, 2)现实上layoutManager也是可以在xml中通过app:layoutManager属性来设置的,比如:
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"默认的LinearLayoutManager是垂直方向的,如果我们想要改方向可以通过androidrientation属性,比如:
androidrientation="horizontal"如许就可以在编写xml的时间随手就加上了,既可以查察预览效果,也制止了代码忘记设置的尴尬环境。
app:spanCount

上面的示例中RecyclerView的layoutManager指定了LinearLayoutManager,我们还可以指定为GridLayoutManager,但是GridLayoutManager默认的spanCount是1,如果我们须要设置spanCount为2,那该怎么预览呢,这时间就用到了app:spanCount属性,可以指定须要表现的列数。
app:spanCount="2"效果:
6.png android:tint

着色器,这个属性在之前的包体积优化中有提到,可以镌汰图片数目,从而减小包巨细。
我们通常会用ImageView表现一张图片,比如本来是一个白色的返回icon,现在另一个地方要用玄色的了,就不须要使用优劣两张图了,而是使用tint来修改为玄色即可,固然,也有范围,得当纯色图片。
效果:
示例:
        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="30dp"            androidrientation="horizontal">            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:background="@color/black"                android:src="@mipmap/ic_back" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:background="@color/red"                android:src="@mipmap/ic_back"                app:tint="@color/black" />        </LinearLayout>在appcompat的高版本中已经改用app:tint取代。
代码方式修改tint:
mBinding.imageView.imageTintList = ContextCompat.getColorStateList(this, R.color.greenPrimary)除了tint另有backgroundTint,效果同理。
使用场景除了上面的示例外,还可以在点赞、收藏这类场景的表现上使用。
android:divider

LinearLayout也是我们使用非常高频的一个Layout,下面先容两个个少为人知的属性。
信赖许多人都用View写太过割线的效果,雷同如许:
        <TextView />        <View            android:layout_width="match_parent"            android:layout_height="1dp"            android:background="#EEEEEE" />        <TextView />如上,当有多个TextView之间须要添加分割线的时间,就只能一个一个复制,复制实在也没什么,就是代码看起来不优雅。
实在有个比力优雅的办法,LinearLayout可以通过android:divider属性添加分割线,结合android:showDividers属性即可到达效果。
xml:
    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="20dp"        android:background="@drawable/shape_radius5_white"        android:divider="@drawable/shape_divider_linear"        androidrientation="vertical"        android:showDividers="middle" >        <TextView            style="@style/MyTextView"            android:text="删除个人信息"            app:drawableStartCompat="@mipmap/ic_helper" />        <TextView            style="@style/MyTextView"            android:text="注销账户"            app:drawableStartCompat="@mipmap/ic_helper" />        <TextView            android:id="@+id/tv_about"            style="@style/MyTextView"            android:text="关于我们"            app:drawableStartCompat="@mipmap/ic_helper" />    </LinearLayout>shape_divider_linear是分割线的样式:
<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android">    <item        android:left="50dp" >        <shape android:shape="rectangle">            <solid android:color="#F6F6F6" />            <size android:height="1dp" />        </shape>    </item></layer-list>效果:
showDividers有4个选项:

  • middle 每两个组件间表现分隔线
  • beginning 开始处表现分隔线
  • end 末端处表现分隔线
  • none 不表现
实在闻一知十,除了分割线,View之间的隔断也可以这么实现,省得每个子view都要写margin。
android:animateLayoutChanges

animateLayoutChanges属性是ViewGroup内里的,主要是在子view的添加和移除时,添加一个默认300ms的渐变动画。
代码:
android:animateLayoutChanges="true"效果:
默认添加移除操作是比力生硬的,加上动画之后体验上会好许多。
固然,如果你想修改默认动画也是可以的。怎么修改?没有比学习源码更直接的了。
源码:
case R.styleable.ViewGroup_animateLayoutChanges:    boolean animateLayoutChanges = a.getBoolean(attr, false);    if (animateLayoutChanges) {        setLayoutTransition(new LayoutTransition());    }    break;当animateLayoutChanges属性值为true时,调用setLayoutTransition方法,并传入一个默认的LayoutTransition对象。
LayoutTransition对象用于构造动画,跟一样平常的动画使用差不多,感爱好的可以看下官方文档大概跟下源码。
自界说LayoutTransition对象之后,调用ViewGroup.setLayoutTransition(LayoutTransition)即可。
android:foreground

android:foreground="?android:attr/selectableItemBackground"在Android5.0以后,给View加上这个属性之后,点击时默认会有一个水波纹的效果,一样平常可点击的View默认都有这个效果,比如Button,一样平常通常会在自界说的item view上加上这个属性用来提升用户体验。
末了

如上,本文一共先容了17个在一样寻常编写xml的过程中对提升服从和提升性能的属性,如果你也故意得,欢迎品评增补。
如果本文对你有一丢丢资助,也感谢点赞支持~
Github

github.com/yechaoa/Mat…
干系文档


  • Space
  • Guideline
  • ConstraintLayout
  • ViewSub
  • tools
  • LayoutTransition
作者:yechaoa
链接:https://juejin.cn/post/7145861715798802462
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-4-19 19:26, Processed in 0.194904 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表