目录
- 一、图形定制
- 1.1 图形Drawable
- 1.2 形状图形
- 1.3 状态列表图形
- 二、选择按钮
- 2.1 复选框
- 2.2 开关按钮
- 2.3 单选按钮
- 三、文本输入
- 3.1 编辑框
- 3.2 焦点变更监听器
- 3.3 文本变化监听器
- 四、对话框
- 4.1 提醒对话框
- 4.2 日期对话框
- 4.3 时间对话框
一、图形定制
1.1 图形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英寸以上的平板电脑
1.2 形状图形
Shape图形又称形状图形,可以用它绘制矩形、圆角矩形、圆形、椭圆等。
形状图形的定义文件放在drawable目录下,以shape为根标签,描述了当前是哪种几何图形,shape标签中有个shape属性,用来指定图形的形状。
shape节点下面有6个子标签:
- 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:像素类型,与下方的间隔。
- left:像素类型,与左边的间隔。
- right:像素类型,与右边的间隔。
- gradient(渐变)
gradient是shape的下级节点,它描述了形状图形的颜色渐变。若无gradient节点,则表示没有渐变效果。下面是gradient节点的常用属性说明。- angle:整型,渐变的起始角度。为0时表示时钟的9点位置,值增大表示往逆时针方向旋转。例
如,值为90表示6点位置,值为180表示3点位置,值为270表示0点/12点位置。 - type:字符串类型,渐变类型
- centerX:浮点型,圆心的X坐标。当android:type="linear"时不可用。
- centerY:浮点型,圆心的Y坐标。当android:type="linear"时不可用。
- gradientRadius:整型,渐变的半径。当android:type="radial"时需要设置该属性。
- centerColor:颜色类型,渐变的中间颜色。
- startColor:颜色类型,渐变的起始颜色。
- endColor:颜色类型,渐变的终止颜色。
- useLevel:布尔类型,设置为true为无渐变色、false为有渐变色。
- angle:整型,渐变的起始角度。为0时表示时钟的9点位置,值增大表示往逆时针方向旋转。例
下面是绘制一个矩形的代码:
<?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"/><!--指定了形状四个圆角的半径--><corners android:radius="10dp"/>
</shape>
1.3 状态列表图形
状态图形一般会用到按钮控件的背景,例如一个按钮被按下,就会有凹下去的感觉,这时就饿可以用Drawable的一个子类StateListDrawable(状态列表图形),在xml中规定不同状态时候呈现的图形列表。
下面演示如何制作一个状态列表:
在drawable目录创建一个xml文件,里面的根标签是selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@drawable/shape_oval_rose" android:state_pressed="true"/><item android:drawable="@drawable/shape_rect_gold"/>
</selector>
上面演示的是当按钮被按下时显示shape_oval_rose这个图形,默认显示shape_rect_gold图形
状态列表图形不仅用于按钮控件,还可用于其他拥有多种状态的控件,这取决于开发者在XML文件中指定了哪种状态类型。
二、选择按钮
CompoundButton类是抽象的复合按钮,所以不能直接使用,派生类主要有复选框CheckBox、单选按钮RadioButton以及开关按钮Switch,因为CompoundButton又继承了Button类,所以上面几个按钮也可也使用Button类的属性和方法。继承关系如下:
CompoundButton在XML文件中主要使用下面两个属性。
- checked:指定按钮的勾选状态,true表示勾选,false则表示未勾选。默认为未勾选。
- button:指定左侧勾选图标的图形资源,如果不指定就使用系统的默认图标。
CompoundButton在Java代码中主要使用下列4种方法。
- setChecked:设置按钮的勾选状态。
- setButtonDrawable:设置左侧勾选图标的图形资源。
- setOnCheckedChangeListener:设置勾选状态变化的监听器。
- isChecked:判断按钮是否勾选。
2.1 复选框
复选框CheckBox,点击勾选,再次点击取消勾选,复选框对象调用setOnCheckedChangeListener方法设置勾选监听器。
定义一个复选框:
<?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"><CheckBoxandroid:id="@+id/ck_system"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="这是系统默认的样式"/><CheckBoxandroid:id="@+id/ck_custom"android:layout_width="match_parent"android:layout_height="wrap_content"android:button="@drawable/checkbox_selector"android:text="这是自己定制的样式"android:checked="true"/>
</LinearLayout>
java代码:
public class CheckBoxActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_check_box);CheckBox ck_system = findViewById(R.id.ck_system);CheckBox ck_custom = findViewById(R.id.ck_custom);ck_system.setOnCheckedChangeListener(this);ck_custom.setOnCheckedChangeListener(this);}@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {String desc = String.format("你%s了这个CheckBox",isChecked?"勾选":"取消勾选");buttonView.setText(desc);}
}
2.2 开关按钮
Switch是开关按钮,Switch控件新添加的XML属性说明如下:
- textOn:设置右侧开启时的文本。
- textOff:设置左侧关闭时的文本。
- track:设置开关轨道的背景。
- thumb:设置开关标识的图标。
定义一个开关按钮:
<?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"><Switchandroid:id="@+id/sw_status"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Switch开关:"android:textSize="17sp"/><TextViewandroid:id="@+id/tv_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"/>
</LinearLayout>
java代码:
public class SwitchDefaultActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {private TextView tv_result;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_switch_default);tv_result = findViewById(R.id.tv_result);Switch sw_status = findViewById(R.id.sw_status);sw_status.setOnCheckedChangeListener(this);}@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {String s = String.format("Switch的状态是%s",isChecked?"开":"关");tv_result.setText(s);}
}
2.3 单选按钮
所谓单选按钮,指的是在一组按钮中选择其中一项,并且不能多选,这要求有个容器确定这组按钮的范围,这个容器便是单选组RadioGroup。单选组实质上是个布局,同一组RadioButton都要放在同一个RadioGroup节点下。RadioGroup提供了orientation属性指定下级控件的排列方向,该属为horizontal时,单选按钮在水平方向排列;该属性为vertical时,单选按钮在垂直方向排列。RadioGroup下面除了RadioButton,还可以挂载其他子控件(如TextView、ImageView等)。如此看来,单选组相当于特殊的线性布局。
RadioGroup在Java代码中的3个常用方法:
- check:选中指定资源编号的单选按钮。
- getCheckedRadioButtonId:获取已选中单选按钮的资源编号。
- setOnCheckedChangeListener:设置单选按钮勾选变化的监听器。
与CheckBox不同的是,RadioButton默认未选中,点击后显示选中,但是再次点击不会取消选中。只有点击同组的其他单选按钮时,原来选中的单选按钮才会取消选中。另需注意,单选按钮的选中事件不是由RadioButton处理,而是由RadioGroup处理。
定义一个单选按钮:
<?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"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="请选择你的性别"/><RadioGroupandroid:id="@+id/rg_gender"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rb_male"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:text="男"/><RadioButtonandroid:id="@+id/rb_female"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:text="女"/></RadioGroup><TextViewandroid:id="@+id/tv_result"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</LinearLayout>
java代码:
public class RadioHorizontalActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {private TextView tv_result;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_radio_horizontal);RadioGroup rg_gender = findViewById(R.id.rg_gender);tv_result = findViewById(R.id.tv_result);rg_gender.setOnCheckedChangeListener(this);}@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {switch (checkedId){case R.id.rb_male:tv_result.setText("你好帅气的男孩");break;case R.id.rb_female:tv_result.setText("你好漂亮的女孩");break;}}
}
三、文本输入
编辑框EditText上高效地输入文本
3.1 编辑框
编辑框EditText用于接收软键盘输入的文字,例如用户名、密码、评价内容等,它由文本视图派生而
来,除了TextView已有的各种属性和方法,EditText还支持下列XML属性。
-
inputType:指定输入的文本类型。若同时使用多种文本类型,则可使用竖线“|”把多种文本类型拼接起来。
-
maxLength:指定文本允许输入的最大长度。
-
hint:指定提示文本的内容。
-
textColorHint:指定提示文本的颜色
定义一个编辑框:
<?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"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="下面是登录信息"/><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入用户名"android:maxLength="6"android:inputType="text"/><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入密码"android:inputType="textPassword"/></LinearLayout>
当然EditText默认的下划线背景不甚好看,下面将利用状态列表图形将编辑框背景改为更加美观的圆角矩形。
首先编写圆角矩形的形状图形文件,它的XML定义文件示例如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" ><!-- 指定了形状内部的填充颜色 --><solid android:color="#ffffff" /><!-- 指定了形状轮廓的粗细与颜色 --><strokeandroid:width="1dp"android:color="#aaaaaa" /><!-- 指定了形状四个圆角的半径 --><corners android:radius="5dp" /><!-- 指定了形状四个方向的间距 --><paddingandroid:bottom="2dp"android:left="2dp"android:right="2dp"android:top="2dp" />
</shape>
上面定义的是在未输入时显示的样式,点击输入时的样式在shape_edit_focus.xml自行定义,和上面一样的代码,只需要改变一下线条的颜色即可。
接下来接着编写编辑框背景的状态列表图形文件,主要在selector节点下添加两个item,一个是聚焦时的图形,另一个是未聚焦时的图形。
<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>
EditText将background属性值设为@drawable/editext_selector,其背景由editext_selector.xml所定义的状态列表图形决定。
定义xml布局:
<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" ><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:inputType="text"android:hint="我的边框是圆角"android:background="@drawable/editext_selector"android:textColor="@color/black"android:textSize="17sp" />
</LinearLayout>
3.2 焦点变更监听器
~~~~~~ 焦点变更监听器来自于接口View.OnFocusChangeListener,若想注册该监听器,就要调用编辑框对象的setOnFocusChangeListener方法,即可在光标切换之时(获得光标和失去光标)触发焦点变更事件。下面是给密码框注册焦点变更监听器的代码例子:
布局效果,两个输入框,一个按钮:
java代码:
public class EditFocusActivity extends AppCompatActivity implements View.OnFocusChangeListener{private EditText et_phone;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_edit_focus);et_phone = findViewById(R.id.et_phone);EditText et_password = findViewById(R.id.et_password);et_password.setOnFocusChangeListener(this);}@Overridepublic void onFocusChange(View v, boolean hasFocus) {if (hasFocus){String phone = et_phone.getText().toString();//手机号不足11位if (TextUtils.isEmpty(phone) || phone.length() <11){//手机号码编辑框请求焦点,也就是把光标移回手机号码编辑框et_phone.requestFocus();Toast.makeText(this,"请输入11位手机号码",Toast.LENGTH_SHORT).show();}}}
}
3.3 文本变化监听器
当用户输入完密码,登录按钮有时候就被软键盘挡着了,下面演示如何在输入完密码之后自动关闭软键盘。
按下返回键就会关闭软键盘,但这是系统自己关闭的,而非开发者在代码中关闭。因为输入法软键盘由系统服务INPUT_METHOD_SERVICE管理,所以关闭软键盘也要由该服务处理,下面是使用系统服务关闭软键盘的代码例子
定义一个工具类:
public class ViewUtil {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:在文本改变之后触发。
使用addTextChangedListener方法注册文本监听器,代码实现:
public class EditHideActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_edit_hide);EditText et_phone = findViewById(R.id.et_phone);EditText et_password = findViewById(R.id.et_password);//注册监听器et_phone.addTextChangedListener(new HideTextWatcher(et_phone,11));et_password.addTextChangedListener(new HideTextWatcher(et_password,6));}//定义一个编辑框监听器, 在输入文本达到指定长度时自动隐藏输入法private class HideTextWatcher implements TextWatcher{private EditText mView;private int mMaxLength;public HideTextWatcher(EditText v,int maxLength){this.mView = v;this.mMaxLength = maxLength;}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}//在编辑框的输入文本变化后触发@Overridepublic void afterTextChanged(Editable s) {//获得已输入的文本字符串String str = s.toString();//输入文本达到11位(如手机号码),或者达到6位(如登录密码)时关闭输入法if (str.length() == mMaxLength){//隐藏软键盘ViewUtil.hideOneInputMethod(EditHideActivity.this,mView);}}}
}
四、对话框
4.1 提醒对话框
AlertDialog没有公开的构造方法,因此必须借助建造器AlertDialog.Builder才能完成参数设置AlertDialog.Builder的常用方法说明如下。
- setIcon:设置对话框的标题图标。
- setTitle:设置对话框的标题文本。
- setMessage:设置对话框的内容文本。
- setPositiveButton:设置肯定按钮的信息,包括按钮文本和点击监听器。
- setNegativeButton:设置否定按钮的信息,包括按钮文本和点击监听器。
- setNeutralButton:设置中性按钮的信息,包括按钮文本和点击监听器,该方法比较少用。
通过AlertDialog.Builder设置完对话框参数,还需调用建造器的create方法才能生成对话框实例。最后
调用对话框实例的show方法,在页面上弹出提醒对话框。
代码示例:
public class AlertDialogActivity extends AppCompatActivity implements View.OnClickListener{private TextView tv_result;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_alert_dialog);findViewById(R.id.btn_alert).setOnClickListener(this);tv_result = findViewById(R.id.tv_result);}@Overridepublic void onClick(View v) {//创建提醒对话框的建造器AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置对话框标题文本builder.setTitle("尊敬的用户");//设置对话框内容文本builder.setMessage("您真的要卸载我吗");//设置对话框的肯定按钮文本及其点击监听器builder.setPositiveButton("残忍卸载", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {tv_result.setText("虽然依依不舍,但也只能离开了");}});//设置对话框的否定按钮文本及其点击监听器builder.setNegativeButton("我再想想",(dialog,which)->{tv_result.setText("让我再陪你365天");});//根据建造器构建提醒对话框对象AlertDialog dialog = builder.create();//显示提醒对话框dialog.show();}
}
4.2 日期对话框
DatePickerDialog相当于在AlertDialog上装载了DatePicker,编码时只需调用构造方法设置当前的年、
月、日,然后调用show方法即可弹出日期对话框。日期选择事件则由监听器OnDateSetListener负责.
代码示例:
public class DatePickerActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener {private DatePicker dp_date;private TextView tv_date;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_date_picker);findViewById(R.id.btn_ok).setOnClickListener(this);findViewById(R.id.btn_date).setOnClickListener(this);dp_date = findViewById(R.id.dp_date);tv_date = findViewById(R.id.tv_date);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn_ok:String s = String.format("你选择的日期是%s年%s月%s日",dp_date.getYear(),dp_date.getMonth()+1,dp_date.getDayOfMonth());tv_date.setText(s);break;case R.id.btn_date:DatePickerDialog dialog = new DatePickerDialog(this,this,2020,11,25);//显示日期对话框dialog.show();break;}}@Overridepublic void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {String s = String.format("你选择的日期是%s年%s月%s日",year,month+1,dayOfMonth);tv_date.setText(s);}
}
4.3 时间对话框
TimePickerDialog为时间对话框。
代码示例:
public class TimePickerActivity extends AppCompatActivity implements View.OnClickListener, TimePickerDialog.OnTimeSetListener {private TextView tv_time;private TimePicker tp_time;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_time_picker);tv_time = findViewById(R.id.tv_time);tp_time = findViewById(R.id.tp_time);tp_time.setIs24HourView(true);findViewById(R.id.btn_ok).setOnClickListener(this);findViewById(R.id.btn_time).setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn_ok:String s = String.format("你选择的时间是%s时%s分",tp_time.getHour(),tp_time.getMinute());tv_time.setText(s);break;case R.id.btn_time://构建一个时间对话框,该对话框已经集成了时间选择器。TimePickerDialog dialog = new TimePickerDialog(this, android.R.style.Theme_Holo_Light_Dialog,this,8,0,true);dialog.show();break;}}@Overridepublic void onTimeSet(TimePicker view, int hourOfDay, int minute) {String s = String.format("你选择的时间是%s时%s分",hourOfDay,minute);tv_time.setText(s);}
}
TimePickerDialog dialog = new TimePickerDialog(this, android.R.style.Theme_Holo_Light_Dialog,this,8,0,true);
最后一个参数的true代码采用24小时制,false为12小时制。