[Colección recomendada] 17 Sugerencias de diseño XML

Me inscribí para participar en el primer desafío del Proyecto Golden Stone: comparta el premio acumulado de 100,000, este es mi primer artículo, haga clic para ver los detalles del evento

prefacio

Cuando desarrollamos, el contacto más importante es el xmldiseño, recuerda el primero que escribimos Android Hello World, que se mostraba a través de activity_main.xml.

Aunque se escribe una gran cantidad de xml, y no hay dificultad técnica, pero este es a menudo el lugar que es más probable que ignoremos. No es difícil escribir xml, pero requiere un poco de esfuerzo para escribir un buen xml.

¿Qué es un buen diseño xml? Creo que hay dos puntos centrales, uno es 提升开发效率y el otro es 提升app性能. En torno a estos dos puntos, también he clasificado cuidadosamente 17algunas habilidades de diseño xml. Echémosle un vistazo. ¿Cuántas has dominado?

Espacio

El sitio web oficial lo describe así:

Space es una subclase de View liviana que se puede usar para crear espacios entre componentes en diseños genéricos.

Por qué es liviano, porque el drawmétodo Space está vacío, es decir, no se dibuja nada, solo el método onMeasure mide el ancho y el alto.

Echa un vistazo al código fuente:

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));
    }
}
复制代码

Por lo tanto, cuando Space actúa sobre el espaciado entre componentes, la eficiencia de renderizado es mayor, especialmente cuando el espaciado debe modificarse dinámicamente.

Por ejemplo, si desea modificar dinámicamente el margen de un componente, si usa el espacio como espaciado, solo necesita modificar el ancho o la altura del espacio. Debido a que el proceso de dibujo se reduce, es más eficiente que dibujar otros componentes

También es muy simple de usar:

<Space
    android:id="@+id/space"
    android:layout_width="20dp"
    android:layout_height="20dp"/>
复制代码

Si lo desea, Espacio puede reemplazar completamente el margen, pero no necesariamente el relleno, porque el relleno es relleno, si el relleno tiene un color de fondo, no puede usar Espacio en su lugar, porque el método de dibujo de Espacio no dibuja nada, por lo que habrá sin color de fondo, a menos que el color de fondo se establezca en la vista principal.

Guía

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. 箭头所指处即创建GuideLine的地方,当然也不止GuideLine,比如还有Barrier
  2. 第一个红框里是水平参考线,70%定位,用百分比能很好的解决适配问题,而我们常规的做法是使用LinearLayout嵌套然后设置子view的weight,虽然嵌套一层不多,但那也是嵌套,就像怀孕一样,你不能说只怀了一点点...
  3. 第二个红框里是垂直参考线,距离左边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" />
复制代码

效果是这样的:

这样其实完全看不出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是垂直方向的,如果我们想要改方向可以通过android:orientation属性,比如:

android:orientation="horizontal"
复制代码

这样就可以在编写xml的时候顺手就加上了,既可以查看预览效果,也避免了代码忘记设置的尴尬情况。

app:spanCount

上面的示例中RecyclerView的layoutManager指定了LinearLayoutManager,我们还可以指定为GridLayoutManager,但是GridLayoutManager默认的spanCount是1,如果我们需要设置spanCount为2,那该怎么预览呢,这时候就用到了app:spanCount属性,可以指定需要显示的列数。

app:spanCount="2"
复制代码

效果:

android:tint

着色器,这个属性在之前的包体积优化中有提到,可以减少图片数量,从而减小包大小

我们通常会用ImageView显示一张图片,比如原本是一个白色的返回icon,现在另一个地方要用黑色的了,就不需要使用黑白两张图了,而是使用tint来修改为黑色即可,当然,也有局限,适合纯色图片。

效果:

示例:

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:orientation="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"
        android:orientation="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 不显示

De hecho, por inferir otro ejemplo, además de la línea divisoria, el intervalo entre vistas también se puede implementar de esta manera, de modo que cada subvista tenga que escribir un margen.

android:animateLayoutChanges

animateLayoutChangesLa propiedad está en ViewGroup, principalmente al agregar y eliminar vistas secundarias, agregando una animación de gradiente predeterminada de 300 ms.

Código:

android:animateLayoutChanges="true"
复制代码

Efecto:

La operación predeterminada de agregar y quitar es relativamente contundente, y la experiencia será mucho mejor después de agregar la animación.

Por supuesto, si desea modificar la animación predeterminada, también es posible. ¿Cómo modificar? No hay nada más directo que aprender el código fuente.

Código fuente:

case R.styleable.ViewGroup_animateLayoutChanges:
    boolean animateLayoutChanges = a.getBoolean(attr, false);
    if (animateLayoutChanges) {
        setLayoutTransition(new LayoutTransition());
    }
    break;
复制代码

Cuando el valor de la propiedad animateLayoutChanges es verdadero, se llama al método y setLayoutTransitionse pasa un LayoutTransitionobjeto predeterminado.

Los objetos LayoutTransition se utilizan para construir animaciones, que son similares a las animaciones generales. Si está interesado, puede leer la documentación oficial o seguir el código fuente.

Después de personalizar el objeto LayoutTransition, ViewGroup.setLayoutTransition(LayoutTransition)puede llamarlo.

Android: primer plano

android:foreground="?android:attr/selectableItemBackground"
复制代码

Después de Android 5.0, después de agregar esta propiedad a la vista, habrá un efecto de onda de agua de manera predeterminada cuando se haga clic en ella. Generalmente, las vistas en las que se puede hacer clic tienen este efecto de forma predeterminada, como el botón, que generalmente se agrega a la vista de elementos personalizados. Esta propiedad es utilizado para mejorar la experiencia del usuario.

Al final

Como se indicó anteriormente, este artículo presenta un total de 17 atributos que son compatibles entre sí en el proceso de escritura xml en la 提升效率vida diaria 提升性能. Si también tiene experiencia, bienvenido a comentar y agregar.

Si este artículo es útil para usted, gracias por su apoyo ~

Github

github.com/yechaoa/Mat…

Documentación relacionada

Supongo que te gusta

Origin juejin.im/post/7145861715798802462
Recomendado
Clasificación