Android开发手册——Android UI和布局规范

文章系列

UI和布局规范

1.不得使用Shape设置圆角
弊端分析:

如上图:过去我们设置一个背景圆角,通常会首先定义一个资源如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/white" />
    <corners android:radius="@dimen/dimen_4" />
    <stroke
        android:width="@dimen/dimen_1"
        android:color="@color/color_E5E4E0" />
</shape>

当有圆角或者颜色变化,我们会再定义一个资源文件,当圆角的边框发生变化又会再定义一个资源,如下

弊端已经很明显

  1. 资源文件爆炸:为每个稍有不同的背景创建新的Shape资源文件,导致资源文件数量激增。这会使APK大小变得庞大,并且增加维护成本
  2. 可读性差:当需要进行修改时,维护者需要查找并辨识正确的资源文件。这对于新加入团队的开发者来说可能是一项挑战,因为他们需要花费更多的时间来理解和管理这些不同的资源文件
  3. 灵活性受限:使用 Shape 限制了对于背景样式的动态修改。例如,如果需要在运行时根据用户输入或者特定条件更改背景样式,Shape 的使用就显得捉襟见肘
替代方案

使用BasicUI库中的ShapeLinearLayout、ShapeTextView、ShapeConstraintLayout进行替换 github使用文档:TextView、LinearLayout和ConstraintLayout的封装

效果

ShapeTextView

这个支持设置背景颜色,背景的圆角,线条的颜色,线条的宽度,支持文字上下左右图片资源两者居中,减少布局嵌套

java 复制代码
        <com.peakmain.ui.widget.ShapeTextView
            android:id="@+id/shape_text_view"
            android:layout_width="@dimen/space_100"
            android:layout_height="@dimen/space_100"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:textColor="@color/color_white"
            android:textSize="28sp"
            tools:text="A"
            app:shapeTvRadius="@dimen/space_6"
            app:shapeTvBackgroundColor="#333333" />

一共有以下属性

java 复制代码
        <!--自定以CircleImageView属性-->
        <declare-styleable name="ShapeTextView">
            <!--设置线条的宽度-->
            <attr name="shapeTvStrokeWidth" format="dimension|reference" />
            <!--设置线条的颜色-->
            <attr name="shapeTvStrokeColor" format="color|reference" />
            <!--设置弧度-->
            <attr name="shapeTvRadius" format="dimension|reference" />
            <!--设置背景颜色-->
            <attr name="shapeTvBackgroundColor" format="color|reference" />
            <!--设置渐变开始颜色-->
            <attr name="shapeTvStartColor" format="color|reference" />
            <!--设置渐变结束颜色-->
            <attr name="shapeTvEndColor" format="color|reference" />
            <!--设置渐变方向-->
            <attr name="shapeTvOriention" format="integer">
                <enum name="LEFT_RIGHT" value="0" />
                <enum name="TOP_BOTTOM" value="1" />
            </attr>
            <!--RECTANGLE=0, OVAL=1, LINE=2, RING=3 默认是0-->
            <attr name="shapeTvShape" format="enum">
                <enum name="rectangle" value="0" />
                <enum name="oval" value="1" />
                <enum name="line" value="2" />
                <enum name="ring" value="3" />
            </attr>
            <!--是否开启点击后动画效果 默认false-->
            <attr name="shapeTvActiveMotion" format="boolean" />
            <!--按下后的背景颜色,也就是android:status_pressed功能-->
            <attr name="shapeTvPressedColor" format="color|reference" />
            <!--四周的圆角-->
            <attr name="shapeTvTopLeftRadius" format="dimension|reference" />
            <attr name="shapeTvTopRightRadius" format="dimension|reference" />
            <attr name="shapeTvBottomLeftRadius" format="dimension|reference" />
            <attr name="shapeTvBottomRightRadius" format="dimension|reference" />
        </declare-styleable>

ShapeLinearLayout

使用

xml 复制代码
    <com.peakmain.ui.widget.ShapeLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:shapeLlBackgroundColor="@color/white"
        app:shapeLlRadius="4dp" />

一共有以下几个属性

java 复制代码
        <!--自定义LinearLayout-->
        <declare-styleable name="ShapeLinearLayout">

            <attr name="shapeLlStrokeWidth" format="dimension|reference" />
            <attr name="shapeLlStrokeColor" format="color|reference" />
            <!--background color-->
            <attr name="shapeLlBackgroundColor" format="color|reference" />
            <!--background radius-->
            <attr name="shapeLlRadius" format="dimension|reference" />
            <attr name="shapeLlStartColor" format="color|reference" />
            <attr name="shapeLlEndColor" format="color|reference" />
            <!--四周的圆角-->
            <attr name="shapeLlTopLeftRadius" format="dimension|reference" />
            <attr name="shapeLlTopRightRadius" format="dimension|reference" />
            <attr name="shapeLlBottomLeftRadius" format="dimension|reference" />
            <attr name="shapeLlBottomRightRadius" format="dimension|reference" />
        </declare-styleable>

ShapeConstraintLayout

使用

xml 复制代码
    <com.peakmain.ui.widget.ShapeConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/dimen_150"
        android:paddingBottom="@dimen/dimen_150"
        app:shapeClBackgroundColor="@color/white"
        app:shapeClTopLeftRadius="@dimen/dimen_4"
        app:shapeClTopRightRadius="@dimen/dimen_4">

一共有以下属性

xml 复制代码
        <declare-styleable name="ShapeConstraintLayout">

            <attr name="shapeClStrokeWidth" format="dimension|reference" />
            <attr name="shapeClStrokeColor" format="color|reference" />
            <!--background color-->
            <attr name="shapeClBackgroundColor" format="color|reference" />
            <!--background radius-->
            <attr name="shapeClRadius" format="dimension|reference" />
            <attr name="shapeClStartColor" format="color|reference" />
            <attr name="shapeClEndColor" format="color|reference" />
            <!--四周的圆角-->
            <attr name="shapeClTopLeftRadius" format="dimension|reference" />
            <attr name="shapeClTopRightRadius" format="dimension|reference" />
            <attr name="shapeClBottomLeftRadius" format="dimension|reference" />
            <attr name="shapeClBottomRightRadius" format="dimension|reference" />
        </declare-styleable>
2. 使用自定义AlertDialog替换DialogFragment
  • 这里场景指的是点击的时候弹出Dialog的场景
  • 如果涉及到更复杂的逻辑,比如旋转屏幕或Activity生命周期变化时保持状态,或者需要与Activity进行交互,推荐使用DialogFragment
  • 使用的类是AlertDialog

使用

支持从底部弹出,支持宽度全屏,可设置动画

java 复制代码
      AlertDialog dialog = new AlertDialog.Builder(ImagePreviewActivity.this)
                .setContentView(R.layout.dialog_show_image_deal)
                .fromBottom(true)
                // Set click events for view
                .setOnClickListener(R.id.bt_logout, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                    }
                })
                //set animation
                .setAnimation(R.style.dialog_from_bottom_anim)
                //Eject from bottom
                .fromButtom(true)
                //set width  MATCH_PARENT
                .setFullWidth()
                .show();

java 复制代码
       AlertDialog dialog = new AlertDialog.Builder(ImagePreviewActivity.this)
                                    .setContentView(R.layout.dialog_show_image_deal)
                                    .fromButtom(true)
                                    // Set click events for view
                                    .addOnClickListener(R.id.bt_logout, new Function1<AlertDialog, Unit>() {
                                        @Override
                                        public Unit invoke(AlertDialog alertDialog) {
                                            //alertDialog
                                            return null;
                                        }
                                    })
                                    //set animation
                                    .setAnimation(R.style.dialog_from_bottom_anim)
                                    //Eject from bottom
                                    .fromButtom(true)
                                    //set width  MATCH_PARENT
                                    .setFullWidth()
                                    .show();
3.实战AlertDialog与ShapeConstraintLayout和ShapeTextView一起使用

我们经常会遇到下述图片场景

  • 中间文字区域是个可滚动区域
  • 底部按钮我知道了,是一个背景圆角且固定在底部
  • 弹框不超过某个高度,比如屏幕高度的1/2或者2/3
  • 宽度一般是屏幕宽度的2/3或者4/5

一般布局写法如下

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:paddingBottom="@dimen/dp_16"
    android:layout_height="wrap_content">

    <!--  顶部-背景图   -->
    <ImageView
        android:id="@+id/iv_bg_top_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="@dimen/dp_94"
        android:background="@drawable/bg_announcement"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--  顶部-标题   -->
    <TextView
        android:id="@+id/tv_title_top_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_16"
        android:layout_marginTop="@dimen/dp_16"
        android:layout_marginRight="@dimen/dp_16"
        android:text="@string/libhotel_hotel_announcement"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_17"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--  中间部分:弹窗内容  -->
    <ScrollView
        android:id="@+id/sv_content_middle_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_bg_top_dialog_announcement">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <!--  中间部分:弹窗内容  -->
            <TextView
                android:id="@+id/tv_content_middle_dialog_announcement"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dp_16"
                android:layout_marginRight="@dimen/dp_16"
                android:paddingTop="@dimen/dp_16"
                android:text="酒店全房型不能升级,SOLO单房公共卫生间,敬请谅解"
                android:textColor="@color/color_194D53"
                android:textSize="@dimen/sp_13" />
        </RelativeLayout>
    </ScrollView>

    <!--  底部:按钮 我知道了 -->
    <TextView
        android:id="@+id/tv_i_know_bottom_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_16"
        android:layout_marginTop="@dimen/dp_16"
        android:layout_marginRight="@dimen/dp_16"
        android:background="@drawable/libhotel_bg_shape_rectangle_circle_6cbd9b_4"
        android:text="@string/libhotel_i_know"
        android:textColor="@color/white"
        android:gravity="center"
        android:paddingTop="@dimen/dp_12"
        android:paddingBottom="@dimen/dp_12"
        android:textSize="@dimen/sp_14"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/sv_content_middle_dialog_announcement" />

</androidx.constraintlayout.widget.ConstraintLayout>

正如我们第一条所说,不得使用shape绘制圆角,而应该使用ShapeTextView和ShapeConstraintLayout,以下是修改后的布局

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<com.peakmain.ui.widget.ShapeConstraintLayout 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:id="@+id/cl_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="@dimen/dp_16"
    app:shapeClBackgroundColor="@color/white"
    app:shapeClRadius="@dimen/dimen_8">

    <!--  顶部-背景图   -->
    <ImageView
        android:id="@+id/iv_bg_top_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="@dimen/dp_94"
        android:background="@drawable/bg_announcement"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--  顶部-标题   -->
    <TextView
        android:id="@+id/tv_title_top_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_16"
        android:layout_marginTop="@dimen/dp_16"
        android:layout_marginRight="@dimen/dp_16"
        android:text="@string/libhotel_hotel_announcement"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_17"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--  底部:按钮 我知道了 -->

    <ScrollView
        android:id="@+id/sv_content_middle_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/dimen_16"
        app:layout_constrainedHeight="true"
        app:layout_constraintBottom_toTopOf="@id/tv_i_know_bottom_dialog_announcement"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_bg_top_dialog_announcement">

        <TextView
            android:id="@+id/tv_content_middle_dialog_announcement"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp_16"
            android:layout_marginRight="@dimen/dp_16"
            android:paddingTop="@dimen/dp_16"
            android:textColor="@color/color_194D53"
            android:textSize="@dimen/sp_13"
            tools:text="测试数据"
           />
    </ScrollView>

    <com.peakmain.ui.widget.ShapeTextView
        android:id="@+id/tv_i_know_bottom_dialog_announcement"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_16"
        android:layout_marginRight="@dimen/dp_16"
        android:gravity="center"
        android:paddingTop="@dimen/dp_12"
        android:paddingBottom="@dimen/dp_12"
        android:text="@string/libhotel_i_know"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_14"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:shapeTvBackgroundColor="@color/color_6CBD9B"
        app:shapeTvRadius="@dimen/dimen_4" />

</com.peakmain.ui.widget.ShapeConstraintLayout>

我简化下代码,对设置圆角的TextView代码进行对比

Dialog设置最大高度

kotlin 复制代码
private fun dealWithAnnounceDialog(content: String) {
    val announceDialog = AlertDialog.Builder(this)
        .setContentView(R.layout.libhotel_dialog_announcement)
        .setText(R.id.tv_content_middle_dialog_announcement, content)
        .setWidthAndHeight(SizeUtils.screenWidth * 4 / 5, ViewGroup.LayoutParams.WRAP_CONTENT)
        .addOnClickListener(R.id.tv_i_know_bottom_dialog_announcement) {
            it?.dismiss()
        }
        .create()
    val view = announceDialog.getView<ConstraintLayout>(R.id.cl_root)
    view?.addOnGlobalLayoutListener {
        val layoutParams = view.layoutParams
        val height = view.measuredHeight
        if (height > SizeUtils.screenHeight * 1 / 2) {
            layoutParams?.height = SizeUtils.screenHeight * 1 / 2
            view.layoutParams = layoutParams
        }
    }
    announceDialog.show()

}

设置高度最大为屏幕的高度的1/2

4.Glide设置圆角
kotlin 复制代码
 ImageLoader.getInstance().displayImage(this, data.get(0).getUrl(), mImageView);
  • 占位图的使用
kotlin 复制代码
 ImageLoader.getInstance().displayImage(this, data.get(1).getUrl(), mImageView, R.mipmap.ic_default_portrait);
  • 圆角图片
kotlin 复制代码
 ImageLoader.getInstance().displayImageRound(this, data.get(2).getUrl(), mImageView,50 ,0);
  • 指定每个角是否需要圆角,设置它们为 true 或 false 来控制四个角是否圆角化
ini 复制代码
       ImageLoader.instance.displayImageRound(
                            item.image,
                            imageView,
                            R.drawable.icon_loading,
                            leftTop = true,
                            rightTop = true,
                            leftBottom = false,
                            false,
                            SizeUtils.dp2px(8f).toFloat()//圆角大小
                        )
  • 指定图片的大小
kotlin 复制代码
ImageLoader.getInstance().displayImage(this,data.get(4).getUrl(),mImageView,800,800,0);
  • 切换图片加载库
scss 复制代码
ImageLoader.getInstance().exchangeImageLoader(切换的库)
  • 设置user-agent,默认是userAgent的值是Android
arduino 复制代码
  ImageLoader.instance.userAgent("自定义UserAgent")

结束

  • 先写这些吧,后面会继续完善其文章。
  • 更多封装UI大家可以看 BasicUI的github
相关推荐
韩仔搭建42 分钟前
第二章:安卓端启动流程详解与疑难杂症调试手册
android·ui·娱乐
A-花开堪折44 分钟前
Android7 Input(七)App与input系统服务建立连接
android
冰糖葫芦三剑客1 小时前
Android 自定义悬浮拖动吸附按钮
android
吃汉堡吃到饱1 小时前
【Android】从Choreographer到UI渲染(二)
android·ui
微信公众号:AI创造财富1 小时前
显示的图标跟UI界面对应不上。
android·ui
aningxiaoxixi1 小时前
安卓 Audio Stream 类型
android
奔跑吧 android2 小时前
【android bluetooth 协议分析 01】【HCI 层介绍 3】【NUMBER_OF_COMPLETED_PACKETS 事件介绍】
android·bluetooth·hci·bt·gd·aosp13
_龙小鱼_4 小时前
Kotlin扩展简化Android动画开发
android·开发语言·kotlin
奔跑吧 android5 小时前
【android bluetooth 协议分析 01】【HCI 层介绍 6】【WriteLeHostSupport命令介绍】
android·bluetooth·bt·gd·aosp13·writelehostsup·hcicmd
uwvwko5 小时前
ctfshow——web入门254~258
android·前端·web·ctf·反序列化