安卓中级控件(图形、选择按钮、文本输入、对话框)

图形定制

图形Drawable

Android把所有能够显示的图形都抽象为Drawable类(可绘制的)。这里的图形不止是图片,还包括色块、画板、背景等。
包含图片在内的图形文件放在res目录的各个drawable目录下,其中drawable目录一般保存描述性的XML文件,而图片文件一般放在具体分辨率的drawable目录下。例如:

  • drawable-ldpi里面存放低分辨率的图片(如240×320),现在基本没有这样的智能手机了。
  • drawable-mdpi里面存放中等分辨率的图片(如320×480),这样的智能手机已经很少了。
  • drawable-hdpi里面存放高分辨率的图片(如480×800),一般对应4英寸!4.5英寸的手机(但不绝对,同尺寸的手机有可能分辨率不同,手机分辨率就高不就低,因为分辨率低了屏幕就会有模糊的感觉)。
  • drawable-xhdpi里面存放加高分辨率的图片(如720×1280),一般对应5英寸~5.5英寸的手机。
  • drawable-xxhdpi里面存放超高分辨率的图片(如1080×1920),一般对应6英寸~6.5英寸的手机。
  • drawable-xxxhdpi里面存放超超高分辨率图片(如1440×2560),一般对应7英寸以上的手机。

基本上,分辨率每加大一级,宽度和高度就要增加二分之一或三分之一像素。如果各自目录存在同名图片,Android就会根据手机的分辨率分别适配对应文件夹里的图片。在开发App时,为了兼容不同的手机屏幕,在各个目录存放不同分辨率的图片,才能达到最合适的显示效果。例如,在drawable-hdpi里面放了一张背景图片bg.png(分辨率480×800),其他目录没放,使用分辨率为480×800的手机查看该App界面没有问题,但是使用分辨率为720×1280的手机查看该图片就会模糊,原因是Android为了让bg.png适配高分辨率的屏幕,强行把bg.png拉伸到了720×1280,拉伸的结果就是图片模糊了。
在XML布局文件中引用图片文件中引用图形文件可用“@drawable/不含扩展名的文件名称”这种形式,如各视图的background属性、ImageViewImageButton的src属性、TextViewButton四个方向的drawable***系列属性都可以引用图形文件。

形状图形

Shape图形又称形状图形,用来描述常见的几何形状,包括矩形、圆角矩形、圆形、椭圆等。用好形状图形可以让App页面不在呆板,还可以节省美工不少工作量。
形状图形的定义文件放在srawable目录下,它是以shape标签为根节点的XML描述文件。根节点下定义了6个节点,分别是size(尺寸)、stroke描边、corners(圆角)、solid(填充)、padding(间隔)、gradient(渐变),各节点的属性值主要是宽和高、半径、角度以及颜色。下面是形状各个节点及其属性的简要说明。

shape(形状)

Shape是形状图形文件的根节点,它描述了当前是哪几种几何图形。下面是shape节点的常用属性说明。

  • shape:字符串类型,表示图形的形状。形状取值说明见下表
形状类型说明
rectangle矩形。默认值
oval椭圆。此时corners节点会失效
Line直线。此时必须设置stroke节点,不然会报错
ring圆环

size(尺寸)

size是shape的下级节点,它描述了形状图形的宽高尺寸。若无size节点,则表示宽高与宿主视图一样大小。下面是size节点的常用属性说明。

  • height:像素类型,图形高度。
  • width:像素类型,图形宽度

stroke(描边)

stroke是shape的下级节点,它描述形状图形的描边规格。若无stroke节点,则表示不存在描边。下面是stroke节点的常用属性说明。

  • color:颜色类型,描边的颜色。
  • dashGap:像素类型,每段虚线之间的间隔。
  • dashWidth:像素类型,每段虚线的宽度。若dashGap和dashWidth有一个值为0,则描边为实线。
  • width:像素类型,描边的厚度。

corners(圆角)

corners是shape的下级节点,它描述了形状图形的圆角大小。若无corners节点,则表示没有圆角。下面是corners节点的常用属性说明。

  • bottomLeftRadius:像素类型,左下圆角的半径。
  • bottomRightRadius:像素类型,右下圆角的半径。
  • topLeftRadius:像素类型,左上圆角的半径。
  • topRightRadius:像素类型,右上圆角半径。
  • radius:像素类型,4个圆角的半径(若有上面4个圆角半径的定义,则不需要radius定义)。

solid(填充)

solid是shape的下级节点,它描述了形状图形的填充色彩。若无solid节点,则表示无填充颜色。下面是solid节点的常用属性说明。

  • color:颜色类型,内部填充的颜色。

padding(间隔)

padding是shape的下级节点,它描述了形状与周围边界的间隔。若无padding节点,则表示四周不设间隔。下面是padding节点的常用属性说明。

  • top:像素类型,与上方的间隔。
  • bottom:像素类型,与下方的间隔。
  • right:像素类型,与右边的间隔。
  • left:像素类型,与左边的间隔。

gradient(渐变)

gradient是shape的下级节点,它描述了形状图形的颜色渐变。若无gradient节点,则表示没有渐变效果。下面是gradient节点的常用属性说明。

  • angle:整型,渐变的起始角度。为0时表示始终的9点位置,值增大表示往逆时针方向旋转。
  • type:字符串类型,渐变类型。渐变类型取值见下表:
渐变类型说明
linear线性渐变,默认值
radial放射渐变,其实颜色就是圆心颜色
sweep滚动渐变,即一个线段以某个断电为圆心做360°旋转
  • centerX:浮点型,圆形的X坐标。当android:type="linear"时不可用。
  • centerY:浮点型,圆形的Y坐标。当android:type="linear"时不可用。
  • gradientRadius:整型,渐变半径。当android:type="radial"时需要设置该属性。
  • centerColor:颜色类型,渐变的中间颜色。
  • startColor:颜色类型,渐变的起始颜色。
  • endColor:颜色类型,渐变的终止颜色。
  • useLevel:布尔类型,设置为true为无渐变色、false为有渐变色。

在实际开发中,形状图形主要使用3个节点:stroke(描边)、corners(圆角)和solid(填充)。至于shape根节点的属性一般不用设置(默认矩形即可)。
接下来演示一下形状图形的界面效果,首先右击drawable目录,并依次选择右键菜单的New->Drawable resource file,在弹窗中输入文件名称再单击OK按钮,即可自动生成一个XML描述文件。往该文件填入下面的圆角矩形内容定义:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!--  指定了形状内部的填充颜色  -->
    <solid android:color="#ffdd66" />
<!--  指定了形状轮廓的粗细与颜色  -->
    <stroke
        android:width="1dp"
        android:color="#aaaaaa" />
<!--  指定了形状4个圆角的半径  -->
    <corners android:radius="10dp" />
</shape>

接着再创建一个椭圆图形,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
<!--  指定了形状内部的填充颜色  -->
    <solid android:color="#ff66aa" />
<!--  指定了形状轮廓的粗细与颜色  -->
    <stroke
        android:width="1dp"
        android:color="#aaaaaa" />
</shape>

执行代码,切换两种图形显示,就能看到如下两种效果图:
椭圆图形
矩形图形

九宫格图片

将某张图片设置成视图背景时,如果图片尺寸太小,则系统会自动拉伸图片使之填满背景。但是一旦图片拉伸过大,其画面容易变得模糊,如下图:
在这里插入图片描述
如上图所示,上面的按钮的背景图片被拉得很宽,此时左右两边的边缘即变宽又变得模糊。需要注意的是,我们在xml文件里通过背景属性这样android:background="@drawable/button_normal_orig"直接设置背景图片可能显示不出来。这时只需要将按钮控件用<android.widget.Button代替<Button就可以了。
为了解决拉伸模糊问题,Android设计了点九图片。点九图片的扩展名是.png,文件后面常带有“.9”字样。因为该图片划分了3×3的九宫格区域,所以得名点九图片,也叫做九宫格图片。如果背景是个形状图形,其stroke节点的width属性已经设置了固定数值(如1dp),那么无论该图形被拉到多大,描边宽度始终是1dp。九宫格图片的实现原理与之类似,即拉伸图形时,只拉伸内部区域,不拉伸边缘线条。
为了演示九宫格图片的展示效果,利用Android Studio制作一张点九图片。首先在drawable目录下找到待加工的原始图片button_pressed_orig.png,右击它弹出右键菜单,如下图:
在这里插入图片描述
选择上图右键显示菜单的Create 9-Patch file...,并在随后弹出的对话框中单击OK按钮。接着drawable目录自动生成一个名为“button_pressed_orig.9.png”的图片,双击该文件,主界面右侧弹出如下图显示的点九图片的加工窗口:
在这里插入图片描述
上图的左侧窗口时图片加工区域,右侧窗口是图片预览区域,从上到下依次是纵向拉伸预览,横向拉伸预览、两方向同时拉升预览。在左侧窗口图片四周的马赛克处单击会出现一个黑点,把黑点左右或上下拖动会拖出一段黑线,不同方向上的黑线表示不同效果。
下图所示,界面上的黑线指的是水平方向的拉伸区域。在水平方向拉伸图片时,只有黑线区域内的图像会被拉伸,黑线以外的图像保持原状,从而保证左右两边的边框厚度不变。
在这里插入图片描述
下图所示,界面左边的黑线指的是垂直方向的拉伸区域。在垂直方向拉伸图片时,只有黑线区域内的图像会被拉伸,黑线以外的图像保持原状,从而保证上下两侧的边框厚度不变。
在这里插入图片描述
下图所示,界面下边的黑线指的是该图片作为控件背景时,控件内部的文字左右边界只能放在黑线区域内,这里Horizontal Padding的效果就相当于android:paddingLeftandroid:paddingRight
在这里插入图片描述
下图所示,界面右边的黑线指的是该图片作为控件背景时,控件内部的文字上下边界只能放在黑线区域内。这里Vertical Padding的效果相当于android:paddingTopandroid:paddingBottom
在这里插入图片描述
注意:如果点九图片被设置为视图背景,且该图片指定了Horizontal PaddingVertical Padding,那么视图内部将一直与视图边缘保持固定间距,无论怎么调整XML文件和Java代码都无法缩小,缘由时点九图片早已在水平和垂直都设置了padding。

状态列表图形

常见的图形文件一般为静态文件,但有时会用到动态文件,比如按钮控件的背景在正常情况下是凸起的,在按下时是凹陷的,从按下到弹起的过程,用户便知道点击了该按钮。根据不同的触摸情况变更形态,这种情况用到了Drawable的一个子类StateListDrawable(状态列表图形),它在XML文件中规定了不同状态所呈现的图形列表。
接下来演示一下状态列表图形的界面效果,右击drawable目录,并依次选择右键菜单的New->Drawable resource file,在弹窗中输入文件名再单击OK按钮,即可自动生成一个XML描述文件。往该文件填入下面的状态列表图形定义:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
        android:drawable="@drawable/button_pressed" />
    <item android:drawable="@drawable/button_normal" />
</selector>

上述XML文件的关键点是state_pressed属性,该属性表示按下状态,值为true表示按下时显示button_pressed图像,其余情况显示button_normal图像。
接下来做个验证,首先将“定制样式的按钮”这一按钮控件的android:background属性设置为@drawable/btn_nine_selector,然后再屏幕上点击按钮,观察发现按下按钮时的界面如下图所示:
定制样式按钮按下
未点击时的状态如下:
定制样式按钮未按下
状态列表图形不仅用于按钮控件,还可用于其他拥有多种状态的控件,这取决于开发者在XML文件中指定了哪种状态类型。各种状态类型取值说明取值如下表:

状态类型的属性名称说明适用的控件
state_pressed是否按下按钮Button
state_checked是否勾选复选框CheckBox、单选按钮RadioButton
state_foused是否获取焦点文本编辑框EditText
state_selected是否选中各控件通用

选择按钮

复选框CheckBox

在学习复选框之前,先了解一下CompoundButton类。在Android体系中,CompoundButton类是抽象的符合按钮,因为是抽象类,所以它不能直接使用。在实际开发中用的是CompoundButton的几个派生类,主要有复选框CheckBox、单选按钮RadioButton以及开关按钮Switch,这些派生类均可使用CompoundButton的属性和方法。加之CompoundButton本身继承了Button类,故以上几种按钮同时具备Button的属性和方法,它们之间的继承关系如下图所示:
在这里插入图片描述

CompoundButton在XML文件中主要使用下面两个属性。

  • checked:指定按钮的勾选状态,true表示勾选,false则表示未勾选。默认为未勾选。
  • button:指定左侧勾选图标的图形资源,如果不指定就使用系统的默认图标。

CompoundButton在Java代码中主要使用下列4个方法。

  • setChecked:设置按钮的勾选状态。
  • setButtonDrawable:设置左侧勾选图标的图形资源。
  • setOnCheckedChangeListener:设置勾选状态变化的监听器。
  • isChecked:判断按钮是否勾选。

复选框CheckBox是CompoundButton一个最简单的实现控件,点击复选框将它勾选,再次点击取消勾选。复选框对象调用setOnCheckedListener方法设置勾选监听器,这样在勾选和取消勾选时就会触发监听器的勾选事件。
为了验证,页面XML文件编写如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <CheckBox
        android:id="@+id/ck_system"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:checked="false"
        android:text="这是系统的CheckBox"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <android.widget.CheckBox
        android:id="@+id/ck_custom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:button="@drawable/checkbox_selector"
        android:padding="5dp"
        android:checked="false"
        android:text="这个CheckBox换了图标"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

复选框的状态有选中和没选中两种,图形XML文件checkbox_selector编写如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:drawable="@drawable/check_choose" />
    <item android:drawable="@drawable/check_unchoose" />
</selector>

页面对应的java代码编写如下:

// 该页面实现了接口OnCheckedChangeListener,意味着重写勾选监听器的onCheckedChanged方法
public class CheckBoxActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_check_box);
        // 给ck_system设置勾选监听器,一旦用户点击复选框,就触发监听器的onCheckedChanged方法
        ((CheckBox)findViewById(R.id.ck_system)).setOnCheckedChangeListener(this);
        // 给ck_custom设置勾选监听器,一旦用户点击复选框,就触发监听器的onCheckedChanged方法
        ((CheckBox)findViewById(R.id.ck_custom)).setOnCheckedChangeListener(this);
    }
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        String desc = String.format("您%s了这个CheckBox", b? "勾选":"取消勾选");
        compoundButton.setText(desc);
    }
}

运行App,不点击,两个复选框显示如下:
在这里插入图片描述
点击时,复选框变化如下:
在这里插入图片描述

开关按钮Switch

Switch是开关按钮,它像是一个高级版本的CheckBox,在选中与取消中时可展现的界面元素比复选框丰富。Switch控件新添加的XML属性说明如下:

  • textOn:设置右侧开启时的文本。
  • textOff:设置左侧关闭时的文本。
  • track:设置开关轨道的背景。
  • thumb:设置开关标识的图标。

虽然开关按钮时升级版的复选框,但它在实际开发中用的不多。原因之一是大家觉得Switch的默认界面不够美观,图标感觉有点小巧,效果如下:
在这里插入图片描述
不过我们可以使用CheckBox实现一个大气一点的开关按钮,通过借助状态列表图形,创建一个图形专用的XML文件,给状态列表指定选中与未选中时的开关图标,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:drawable="@drawable/switch_on" />
    <item android:drawable="@drawable/switch_off" />
</selector>

然后把CheckBox标签的background属性设置为@drawable/switch_selector,同时将button属性设置为@null。完整的CheckBox标签内容如下:

 <CheckBox
	android:id="@+id/ck_status"
	android:layout_width="60dp"
	android:layout_height="30dp"
	android:background="@drawable/switch_selector"
	android:button="@null" />

这里解释一下,为什么状态图片设置到android:background属性而不是android:button属性。因为android:button有局限性,无论图片多大,都只显示一个和我们设置的宽高大小的图片部分,因此我们在这还是设置图片到android:background。最后的效果如下图:
在这里插入图片描述

单选按钮RadioButton

单选按钮,指的是在一组按钮中选择一项不能多选。通过RadioGroup这个容器确定单选组的范围,同一组RadioButton都放在同一个RadioGroup下。可以通过RadioGroup的android:orientation属性指定下级控件的排列方向,该属性的值为horizontal时,单选按钮在水平方向排列;该属性的值为vertical时,单选按钮在垂直方向排列。
其实RadioGroup下面除了放RadioButton还可以放其他子控件(如TextView、ImageView等)。如此看来,单选按钮相当于特殊的线性布局,他们主要有以下两个区别:

  • 单选组多了管理单选按钮的功能,而线性布局不具备该功能。
  • 如果不指定orientation属性,那么单选组默认垂直排列,而线性布局默认水平排列

下面是RadioGroup在Java代码中3个常用的方法:

  • check:选中指定资源编号的单选按钮
  • getCheckedRadioButtonId:获取已经选中的单选按钮资源编号
  • setOnCheckedChangedListener:设置单选按钮勾选变化的监听器

与CheckBox不同的是,RadioButton默认未选中,点击后才会显示选中,再次点击不会取消选中。只有点击其他同组单选按钮时,原来选中的单选按钮才会取消选中。另外单选按钮的选中事件不是由RadioButton处理,而是由RadioGroup处理。
为了显示效果,我们活动页面XML文件编写如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请选择您的性别"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <RadioGroup
        android:id="@+id/rg_sex"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <RadioButton
            android:id="@+id/rb_male"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:checked="false"
            android:text=""
            android:textColor="@color/black"
            android:textSize="17sp" />
        <RadioButton
            android:id="@+id/rb_female"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:checked="false"
            android:text=""
            android:textColor="@color/black"
            android:textSize="17sp" />
    </RadioGroup>
    <TextView
        android:id="@+id/tv_sex"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

界面对应的Java代码编写如下:

public class RadioHorizontalActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
    private TextView tv_sex; // 声明一个文本视图对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_radio_horizontal);
        // 从布局文件中获取视图控件tv_sex
        tv_sex = findViewById(R.id.tv_sex);
        // 注册监听器,一旦点击组内单选按钮,就触发监听器的onCheckChanged方法
        ((RadioGroup)findViewById(R.id.rg_sex)).setOnCheckedChangeListener(this);
    }
    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
        if (R.id.rb_male == checkedId) {
            tv_sex.setText("性别为男");
        } else if (R.id.rb_female == checkedId) {
            tv_sex.setText("性别为女");
        }
    }
}

运行App,一开始没选中时页面显示如下,此时两个单选按钮均未选中:
在这里插入图片描述
单击单选按钮,则界面变化如下:
在这里插入图片描述

文本输入

编辑框EditText

编辑框EditText用于接收软键盘输入的文字,例如用户名、密码、评论内容等,它由文本视图派生而来,除了TextView已有的属性和方法外,EditText还支持下列XML属性。

  • inputType:指定输入的文本类型。输入类型的取值说明见下表,若同时使用多种类型,则可使用竖线(|)把多种文本类型拼接起来。
输入类型说明
text文本
textPassword文本密码。显示时用圆点(●)代替
number整型数
numberSigned带符号的数字。允许在开头带负号(-)
numberDecimal带小数点的数字
numberPassword数字密码。显示时用(●)代替
datetime时间日期格式。除了数字外,还允许输入横线(_)、斜杠(/)、空格( )、冒号(:)
date日期格式。除了数字外,还允许输入横线(_)和斜杠(/)
time时间格式。除了数字外,还允许输入冒号(:)
  • maxLength:指定文本允许输入的最大长度。
  • hint:指定提示文本的内容。
  • textColorHint:指定提示文本的颜色。

接下来进行效果验证,编写页面XML文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="下面是登录信息"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:maxLength="10"
        android:hint="请输入用户名"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:maxLength="8"
        android:hint="请输入密码"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="下面是手机信息"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:maxLength="11"
        android:hint="请输入11位手机号码"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="numberPassword"
        android:maxLength="11"
        android:hint="请输入6位服密码"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

运行App后,进入初始的编辑界面如下图:
在这里插入图片描述
在XML文件中通过inputType属性对各个输入框的输入内容进行了限制,输入内容后,界面显示如下:
在这里插入图片描述

在实际开发中,我们可能有自定义输入框边框的需求,在输入与为输入时可能会显示两种不同边框以表示不同状态。那么此时就可以通过background属性,对两种状态进行修改。下面将演示默认边框、无边框和圆角矩形边框三种形态。
首先编写未输入(为获取焦点)时圆角矩形的形状图形文件,该文件显示灰色圆角矩形轮廓,对应的XML文件定义如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--  指定形状内部填充的颜色  -->
    <solid android:color="#ffffff" />
    <!--  指定了形状轮廓的粗细与颜色  -->
    <stroke
        android:width="1dp"
        android:color="#aaaaaa" />
    <!--  指定了形状四个圆角的半径  -->
    <corners android:radius="5dp" />
    <!--  指定了形状四个方向的间距  -->
    <padding
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp" />
</shape>

然后编写的是输入(获取焦点)时圆角矩形的形状图形文件,该文件显示蓝色圆角矩形轮廓,对应的XML文件定义如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--  指定了形状内部的填充颜色  -->
    <solid android:color="#ffffff" />
    <!--  指定了形状轮廓的粗细与颜色  -->
    <stroke
        android:color="#0000ff"
        android:width="1dp" />
    <!--  指定了形状四个圆角的半径  -->
    <corners android:radius="5dp" />
    <!--  指定了圆角四个方向的间距  -->
    <padding
        android:top="2dp"
        android:right="2dp"
        android:left="2dp"
        android:bottom="2dp" />
</shape>

接着编写编辑框的状态列表图形文件editext_selector,主要在selector节点下添加两个item:一个item设置了获取焦点时(android:state_focused="true")的图形为@drawable/shape_edit_focus;另一个item设置了图形@drawable/shape_edit_normal但未指定任何状态,表示其他状态都展示该图形。完整状态列表图形定义如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@drawable/shape_edit_focus" />
    <item android:drawable="@drawable/shape_edit_normal" />
</selector>

最后编写页面XML文件,一共有3个EditText控件:第一个采用默认编辑框;第二个将编辑框的background属性设置为@null表示不显示边框;第三个将background属性设置为@drawable/editext_selector,其背景由editext_selector.xml文件定义的状态列表图形决定。页面布局XML文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp"
    android:orientation="vertical">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:hint="这是默认边框"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:inputType="text"
        android:hint="我的边框不见了"
        android:background="@null"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:hint="我的边框是圆角"
        android:background="@drawable/editext_selector"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

运行App,更换边框背景的编辑框界面显示如下:
在这里插入图片描述

焦点变更监听

当点击编辑框后,EditText当即获取焦点,此时就会发生焦点状态变更触发onFocusChange方法。编辑框的焦点,直观地看就是编辑框地光标闪动,哪个编辑框有光标,焦点就落在哪里。需要注意的是,编辑框比较特殊,要点击两次才会触发点击事件,因为第一次点击触发地是焦点变更。因此对于编辑框来说,一般都注册焦点监听器,而非点击监听器。
下面是以输入手机号码以及密码为例,给密码框注册焦点变更监听器地代码例子:

// 从布局文件中获取密码编辑框et_password
EditText et_password = findViewById(R.id.et_password);
// 给密码编辑框注册一个焦点变化监听器,一旦焦点发生变化,就触发onFocusChange方法
et_password.setOnFocusChangeListener(this);

为了能成功注册焦点变更监听器,还需要让活动实现接口View.OnFocusChangeListener,并重写该接口方法。我们的代码逻辑是,当密码输入框获取焦点时,判断手机号长度是否正确,不正确则将焦点重新设置到手机号输入框。焦点变更监听器的代码如下:

// 焦点变更事件的处理方法,hasFocus表示当前控件是否获得焦点。
// 为什么光标进入事件不选onClick?因为要点两下才会触发onClick动作(第一下是切换焦点动作)
@Override
public void onFocusChange(View view, boolean hasFocus) {
    // 判断密码编辑框是否获得焦点。hasFocus为true表示获得焦点,为false表示失去焦点
    if (view.getId()==R.id.et_password && hasFocus) {
        String phone = et_phone.getText().toString();
        if (TextUtils.isEmpty(phone) || phone.length()<11) {
            // 手机号码编辑框请求焦点,也就是把光标移回手机号码编辑框
            et_phone.requestFocus();
            Toast.makeText(this, "请输入11位手机号码", Toast.LENGTH_SHORT).show();
        }
    }
}

运行App,当手机输入框数据长度不够时,点击密码输入框弹出提示如下:
在这里插入图片描述

文本变化监听器

有时我们在输入账号密码时,可能在我们输入内容后想直接点击登录而不必点击键盘的Enter键收起键盘后再点击登录。那么我们只需要在满足某些条件后关闭软键盘即可,例如手机号码输入满11位或者密码输入满6位,等等。
现在明确需求条件后,达到指定长度关闭软键盘功能,现在我们可以分解为功能点:一个是如何关闭软键盘;另一个是如何判断已输入的文字是否达到指定字数。分别说明如下:

  1. 如何关闭软键盘
    输入法键盘由系统服务Context.INPUT_METHOD_SERVICE管理,所以关闭键盘也要由该服务处理,下面是使用系统服务关闭软键盘代码样例:
public static void hideOneInputMethod(Activity act, View v) {
    // 从系统服务中获取输入法管理器
    InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
    // 关闭屏幕上的输入法软键盘
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

注意上述代码里面的视图对象v,虽然控件类型是View,当他必须是EditText类型才能正常关闭软键盘。

  • 如何判断已输入的文字是否达到指定位数
    该功能点需要实时监控当前输入的文本长度,这个监控操作用到文本监听器接口TextWatcher,该接口提供3个监控方法,具体如下:
  • beforeTextChanged:在文本改变之前触发
  • onTextChanged:在文本改变过程中触发
  • afterTextChanged:在文本改变之后触发

具体代码中需要自己实现接口TextWatcher,再调用编辑框对象的addTextChangedListener方法注册文本监听器。监听操作建议在afterTextChanged方法中完成,同时监听11位手机号和6位密码,一旦输入长度达到指定长度就关闭软键盘,详细监听器代码如下:

// 定义一个编辑框监听器,在输入文本达到指定长度时自动隐藏输入法
private class HideTextWatcher implements TextWatcher {
    private EditText mView; // 声明一个编辑框对象
    private int mMaxLength; // 声明一个最大长度变脸
    HideTextWatcher(EditText editText, int maxLength) {
        super();
        mView = editText;
        mMaxLength = maxLength;
    }

    // 在编辑框的输入文本变化前触发
    @Override
    public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
    }

    // 在编辑框的输入文本变化时触发
    @Override
    public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
    }

    // 在编辑框的输入文本变化后触发
    @Override
    public void afterTextChanged(Editable editable) {
        String str = editable.toString(); // 获取已输入的文本字符
        // 输入字符串长11位(手机号),或者达6位(密码)时关闭输入法
        if ((11 == str.length() && 11 == mMaxLength) || (6 == str.length() && 6 == mMaxLength)) {
            ViewUtil.hideOneInputMethod(EditHideActivity.this, mView);
        }
    }
}

写好监听器代码,还要给手机编辑框和密码编辑框分别注册监听器,注册代码示例如下:

// 获取编辑框et_phone
EditText et_phone = findViewById(R.id.et_phone);
// 获取编辑框et_password
EditText et_password = findViewById(R.id.et_password);
// 给手机号编辑框添加文本变化监听器
et_phone.addTextChangedListener(new HideTextWatcher(et_phone, 11));
// 给密码编辑框添加文本变化监听器
et_password.addTextChangedListener(new HideTextWatcher(et_password, 6));

运行App,输入手机号,但手机号长度没达到11位时软键盘依旧显示,如下图:
在这里插入图片描述
当输入总长度达11位后,软键盘收起,如下图:
在这里插入图片描述

对话框

提醒对话框AlertDialog

AlertDialog名为提醒对话框,它是Android中最常用的对话框,可以完成常见的交互操作,例如提示、确认、选择等功能。由于AlertDialog没有公开的构造方法,因此必须借助建造器AlertDialog.Builder才能完成参数设置,AlertDialog.Builder常用方法如下:

  • setIcon:设置对话框的标题图标。
  • setTitle:设置对话框的标题文本。
  • setMessage:设置对话框的内容文本。
  • setPositiveButton:设置肯定按钮的信息,包括按钮文本和监听器。
  • setNegativeButton:设置否定按钮的信息,包括按钮文本和监听器。
  • setNeturalButton:设置中性按钮的信息,包括按钮文本和点击监听器,该方法比较少用。

通过AlertDialog.Builder设置完成对话框参数,还需要调用建造器的create方法才能生成对话框实例。最后调用对话框实例的show方法,在页面上弹出提醒对话框。
下面是构建并显示提醒对话框的Java代码例子:

// 创建提示对话框的建造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("尊敬的用户"); // 设对话框标题
builder.setMessage("你真的要卸载我吗?"); // 设置对话框的提示内容
// 设置对话框的肯定按钮文本及其监听器
builder.setPositiveButton("残忍卸载", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int which) {
        tv_alert.setText("虽然依依不舍,但是只能离开了");
    }
});
// 设置对话框的否定按钮文本及其监听器
builder.setNegativeButton("我再想想", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int which) {
        tv_alert.setText("让我再陪你三百六十五个日夜");
    }
});
AlertDialog alert = builder.create(); // 根据构造器构建提醒对话框对象
alert.show();

提示对话框如下图所示,可以看见对话框由标题和内容,还有两个按钮:
在这里插入图片描述

日期对话框DatePickerDialog

系统专门提供了日期选择器DatePicker ,供用户选择具体的年、月、日。不过,DatePicker并非弹窗模式,而是在当前页面占据一块区域,并且不会自动关闭。使用习惯上,日期控件应该是需要时弹出,使用完毕后关闭。因此,界面上很少直接显示DatePicker,而是通过系统封装好的DatePickerDialog实现。
DatePickerDialog相当于在AlertDialog上装载了DatePicker,编码时只需要调用构造方法设置当前的年、月、日,然后调用show方法即可弹出日期对话框。日期选择事件则由监听器DatePickerDialog.OnDateSetListener负责响应,在该监听器的onDateSet方法中,开发者获取用户选择的具体日期,再做后续处理。需要注意的是,onDateSet方法的月份参数,它的起始值是从0开始的,也就是说一月份对应的数值是0而十二月份对应的数值是11,中间值以此类推。
在界面上嵌入DatePicker的效果如下图所示:
在这里插入图片描述
单独弹出日期对话框的效果如下图所示效果:
在这里插入图片描述
下面是使用日期对话框的Java代码例子,包括弹出日期对话框和处理日期监听事件:

// 该页面类实现了接口OnDateSetListener,意味着要重写日期监听器的onDateSet方法
public class DatePickerActivity extends AppCompatActivity implements
        View.OnClickListener, DatePickerDialog.OnDateSetListener {
    private TextView tv_date; // 声明一个文本控件
    private DatePicker dp_date; // 声明一个日期选择器对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_date_picker);
        // 从布局文件中获取文本控件tv_date
        tv_date = findViewById(R.id.tv_date);
        // 从布局文件中获取日期选择器dp_date
        dp_date = findViewById(R.id.dp_date);
        findViewById(R.id.btn_date).setOnClickListener(this);
        findViewById(R.id.btn_ok).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (R.id.btn_date == view.getId()) {
            // 获取日历的一个实例,里面包含了当前的年月日
            Calendar calendar = Calendar.getInstance();
            // 构建一个日期对话框,该对话框已经集成了日期选择器
            // DatePickerDialog的第二个参数指定了日期监听器
            DatePickerDialog dialog = new DatePickerDialog(this, this,
                    calendar.get(Calendar.YEAR),  // 年份
                    calendar.get(Calendar.MONTH), // 月份
                    calendar.get(Calendar.DATE)); // 日子
            dialog.show(); // 显示日期对话框
        } else if (R.id.btn_ok == view.getId()) {
            // 获取日期选择器dp_date设定的年月份
            String desc = String.format("您选择的日期是%d年%d月%d日",
                    dp_date.getYear(), dp_date.getMonth() + 1, dp_date.getDayOfMonth());
            tv_date.setText(desc);
        }
    }

    @Override
    public void onDateSet(DatePicker datePicker, int year, int monthOfYear, int dayOfMonth) {
        // 获取日期对话框设定的年月份
        String desc = String.format("您选择的日期是%d年%d月%d日",
                year, monthOfYear + 1, dayOfMonth);
        tv_date.setText(desc);
    }
}

时间对话框TimePickerDialog

安卓同样给开发者提供了时间选择。同样的,在实际开发中也不常直接用TimePicker,而是用封装好的时间选择器对话框TimePickerDialog。该对话框的用法类似DatePickerDialog,不同之处主要有以下两点:

  1. 构造方法传入的是当前的小时和分钟,最后一个参数表示是否采取24小时制,true表示小时的数值取值0~23;若为false则表示采取的12小时制。
  2. 时间选择监听器为TimePickerDialog.OnTimeSetListener,对应的实现方法为onTimeSet,在该方法中可获得用户选择得小时和分钟。

在界面上显示的TimePicker效果如下图所示:
在这里插入图片描述
单独弹出的小时和分钟按照钟表的形式显示如下:
在这里插入图片描述

下面是使用时间对话框的Java代码,包括弹出时间对话框和处理时间监听事件:

// 该页面类实现了接口OnTimeSetListener,意味着要重写时间监听器的onTimeSet方法
public class TimePickerActivity extends AppCompatActivity implements
        View.OnClickListener, TimePickerDialog.OnTimeSetListener {
    private TextView tv_time; // 声明一个文本对象
    private TimePicker tp_time; // 声明一个时间选择器对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_time_picker);
         // 从布局文件中获取文本对象tv_time
        tv_time = findViewById(R.id.tv_time);
        // 从布局文件中获取时间选择器对象tp_time
        tp_time = findViewById(R.id.tp_time);
        findViewById(R.id.btn_time).setOnClickListener(this);
        findViewById(R.id.btn_ok).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (R.id.btn_time == view.getId()) {
            // 获取日历的一个实例,里面包含当前的分秒
            Calendar calendar = Calendar.getInstance();
            // 创建一个对话框,该对话框已经集成了时间选择器
            // TimePickerDialog的第二个参数指定了时间监听器
            TimePickerDialog dialog = new TimePickerDialog(this, this,
                    calendar.get(Calendar.HOUR_OF_DAY), // 小时
                    calendar.get(Calendar.HOUR_OF_DAY), // 分钟
                    true);  // 使用24小时制;false表示12小时制
            dialog.show(); // 显示时间对话框
        } else if (R.id.btn_ok == view.getId()) {
            // 获取定时器tp_time的时间
            String desc = String.format("您选择的时间是%d时%d分",
                    tp_time.getHour(), tp_time.getMinute());
            tv_time.setText(desc);
        }
    }

    // 一旦点击时间对话框上的确定按钮,就会触发监听器的onTimeSet方法
    @Override
    public void onTimeSet(TimePicker timePicker, int hourOfDay, int minute) {
        // 获取时间对话框设定的小时和分钟
        String desc = String.format("您选择的时间是%d时%d分", hourOfDay, minute);
        tv_time.setText(desc);
    }
}

工程源码

所有样例代码都可点击工程源码下载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/587790.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

react完整项目搭建的思路

react完整项目搭建的思路 react完整项目搭建的思路1.使用creacte-react-app初始化项目2.安装所需插件:路由、网络、样式、组件库3.reactjs目录结构组织4. 配置路径别名4.配置路由5.网络配置,对axios进行封装》获取当前环境变量 6.配置代理解决跨域7.配置使用iconfont8.状态管理…

暗区突围steam叫什么 暗区突围无限steam上线时间测试申请预约教程

暗区突围steam叫什么 暗区突围无限steam上线时间 测试申请预约教程 最近暗区突围国际服的上线在游戏圈内引起了不小的波澜&#xff0c;这是一款由腾讯游戏开发的大逃杀类型游戏&#xff0c;玩家们需要在暗区内浴血搏杀&#xff0c;使用各种武器和道具活到最后&#xff0c;来取…

Linux系统中搭建Mosquitto MQTT服务并实现远程访问本地消息代理进行通信

文章目录 1. Linux 搭建 Mosquitto2. Linux 安装Cpolar3. 创建MQTT服务公网连接地址4. 客户端远程连接MQTT服务5. 代码调用MQTT服务6. 固定连接TCP公网地址7. 固定地址连接测试 今天和大家分享一下如何在Linux系统中搭建Mosquitto MQTT协议消息服务端,并结合Cpolar内网穿透工具…

Visual Source Safe 安装与使用教程

1.VSS 的工作原理: Microsott的 vss讲所有的项目源文件(包括各种文件类型)以特有的方式存入数据库。用户成员不能对该数据库中的文件进行直接的修改,而是由版本管理器将该项目的远程序或是子项目的程序拷贝到各个用户成员自己的工作目录下进行调试和修改,然后将修改后的项目…

OpenFeign修改HttpClient为Apache HttpClient 5

OpenFeign中http client 如果不做特殊配置&#xff0c;OpenFeign默认使用JDK自带的HttpURLConnection发送HTTP请求&#xff0c; 由于默认HttpURLConnection没有连接池、性能和效率比较低。所以修改为Apache HttpClient 5。 总结为两步&#xff1a; 加依赖改yml 具体操作请往…

OpenCV如何为等值线创建边界旋转框和椭圆(63)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV 为轮廓创建边界框和圆(62) 下一篇:OpenCV的图像矩(64) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::minAreaRect使用 OpenCV 函数 cv::fitEllipse cv::min…

【C++】学习笔记——string_2

文章目录 六、string类2. 反向迭代器const迭代器 string类对象的容量操作&#xff08;补&#xff09;size() 3. string类的元素访问4. string类的修改 未完待续 结合文档食用~ 六、string类 2. 反向迭代器 一般来说&#xff0c;迭代器都是正向的遍历容器&#xff0c;虽然可以…

Cmake的使用

一个c工程可能会涉及到很多的基础库&#xff0c;但是c不像python一样可以直接import&#xff0c;因此引入了Cmake&#xff0c;将多个库链接起来。 参考&#xff1a;CMake系列讲解 - 总目录&#xff08;由浅入深&#xff0c;实例讲解&#xff09;_cmake 项目目录-CSDN博客 【C】…

Unity类银河恶魔城学习记录 17-1,2 p166 Aliments fx p167 Blackhole additional vfx

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Entity.cs using System.Collections; using System.Collections.Generic; …

综合性练习(后端代码练习4)——图书管理系统

目录 一、准备工作 二、约定前后端交互接口 1、需求分析 2、接口定义 &#xff08;1&#xff09;登录接口 &#xff08;2&#xff09;图书列表接口 三、服务器代码 &#xff08;1&#xff09;创建一个UserController类&#xff0c;实现登录验证接口 &#xff…

OpenCV Mat对象与CImage对象间的数据传输实例

在用MFC写图像处理程序时&#xff0c;使用OpenCV可以做到事半功倍。但是&#xff0c;如果使用OpenCV4.0或OpenCV4.0以后版本&#xff0c;要显示图像可能会遇到麻烦&#xff0c;因为OpenCV去掉了原有的cvGetWindowHandle()函数&#xff0c;没法再用cvGetWindowHandle()函数来获取…

Linux进程管理与监控

一、相关概念 1、进程的的基本定义 在自身的虚拟地址空间运行的一个独立的程序&#xff0c;从操作系统的角度来看&#xff0c;所有在系统上运行的东西&#xff0c;都可以称为一个进程。 2、进程的分类 系统进程&#xff1a;可以执行内存资源分配和进程切换等管理工作&am…

C#知识|泛型集合List相关方法

哈喽&#xff0c;你好&#xff0c;我是雷工&#xff01; 以下为泛型集合List相关方法的学习笔记。 01 集合定义 集合定义的时候&#xff0c;无需规定元素的个数。 02 泛型说明 泛型表示一种程序特性&#xff0c;也就是在定义的时候&#xff0c;无需指定特定的类型&#xff…

C语言嵌入Lua解释器的方法

Lua语言是一个轻量的脚本语言&#xff0c;可以用很少的资源运行其解释器 C语言是一个很常用的语言&#xff0c;广泛用于嵌入式等底层场景 这两个语言结合&#xff0c;可以应用于嵌入式等多个场景。比如&#xff0c;一些硬件公司会允许开发者使用Lua语言操作其硬件 Lua的安装…

搭建Kafka源码环境并测试

文章目录 一、前言二、环境准备三、环境搭建3.1 JDK 环境搭建3.2 Scala 环境搭建3.2.1 配置 Scala 环境变量3.2.2 验证 3.3 Gradle 环境搭建3.3.1 配置 Gradle 环境变量3.3.2 验证 3.4 Zookeeper 环境搭建3.4.1 配置 Zookeeper 环境变量3.4.2 验证 3.5 Kafka 源码搭建3.5.1 导入…

数据可视化宝典:Matplotlib图形实战

在数据分析领域&#xff0c;图形化展示数据是非常重要的环节。Python中的matplotlib库是绘制各类图形的强大工具。本文将介绍如何使用matplotlib绘制折线图、直方图、饼图、散点图和柱状图等数据分析中常见的图形&#xff0c;并附上相应的代码示例&#xff0c;可以当初matplotl…

【C++】——类和对象(初始列表,Static成员,友元)

创作不易&#xff0c;多多支持&#xff01; &#xff01;&#x1f618;&#x1f618; 前言 因为前面的构造函数还有些地方不够清晰&#xff0c;所以这里需要再继续补充一些 一 初始化列表 1.1认识初始化 对于默认的构造函数来说&#xff0c;我们都知道它是起到了初始化的…

3-4STM32C8T6按键控制LED开与关

实物接线如下&#xff1a; 为了代码的简洁性&#xff0c;这里需要对LED与KEY进行封装如下&#xff1a; #include "stm32f10x.h" // Device headervoid LED_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GP…

WSL2-Ubuntu使用Conda配置百度飞浆paddlepaddle虚拟环境

0x00 缘起 本文将介绍在WSL2-Ubuntu系统中,使用Conda配置百度飞浆paddlepaddle虚拟环境中所出现的各种问题以及解决方法,最终运行"run_check()"通过测试。 在WSL2中配置paddlepaddle不像配置Pytorch那样顺滑,会出现各种问题(如:库的文件缺失、不知道如何匹配C…

工厂模式和策略模式区别

工厂模式和策略模式都是面向对象设计模式&#xff0c;但它们的目的和应用场景有所不同。 工厂模式是一种创建型设计模式&#xff0c;旨在通过使用一个工厂类来创建对象&#xff0c;而不是直接使用new关键字来创建对象。这样做可以使系统更容易扩展和维护&#xff0c;因为新的对…