主要内容:
◼ 界面布局
◼ 常用UI组件编程
◼ 多种监听器用法
◼ ImageView图像加载方法
◼ Adapter适配器
◼ 自定义布局
◼ Handler.postDelayed定时器
◼ CountDownTimer倒计时
◼ ExoPlayer媒体播放器

界面布局
◼ 新建一个Module(相当于新app)
◼ 应用名称为:UIDemo
◼ Activity默认:MainActivity
新建Module时注意命名:

界面布局
◼ 布局作用:
◼ 将应用的外观代码(xml)与控制其行为的代码(java)分开(表现层和控制层分离),调整用户界面时,无需更改程序的源代码。
◼ 基本布局:
◼ 约束布局 ConstraintLayout
◼ 线性布局 LinearLayout

1.ConstraintLayout
◼ 默认布局,依靠约束关系来确定位置
◼ 能够灵活定位和调整界面元素的大小
◼ 无任何嵌套,减少布局层级,优化渲染性能
◼ 能完全代替其他布局

基线对齐示例:

2.LinearLayout
◼ 线性布局:界面元素都在一行(水平方向)或一列(垂直方向),可嵌套
◼ 通过 android:orientation 属性指定布局方向:
◼ android:orientation="horizontal"
水平布局 (默认)
◼ android:orientation="vertical"
垂直布局
线性布局示例:

◼ 嵌入一个垂直布局
• android:orientation = “vertical”
• android:layout_width = “match_parent”
• android:layout_height = “wrap_content”
• android:gravity = “center_horizontal”
改为”horizontal” 水平布局
View 与 ViewGroup 的概念
◼ Android中所有的UI元素都是使用View和ViewGroup对象建立的。
◼ View是一个可以将一些信息绘制在屏幕上并与用户产生交互的对象。例如Button、TextView等
◼ ViewGroup是一个包含多个的View和ViewGroup的容器,用来定义UI布局。例如ConstraintLayout、LinearLayout、RadioGroup等。
◼ View类作为界面开发的超类,所有的界面开发都与View有关。

常用UI组件
基本属性
基本属性 |
描述 |
id |
组件唯一标识 |
layout_width |
组件的宽度 |
layout_height |
组件的高度 |
text |
文本内容 |
textColor |
字体颜色 |
textSize |
字体大小 ==注:字体大小默认单位为 sp,宽高默认单位是 dp== |
textStyle |
字体样式(normal|bold|italic) |
backgroundTint |
背景颜色 |
gravity |
内部元素对齐方式:left|center|right|center_horizontal|center_vertical等(父容器要求子元素如何对齐) |
layout_gravity |
容器中的子元素相对父容器的对齐方式(取值同上) |
layout_weight |
用来分配空间占比权重(常用于线性布局 + layout_width=”0dp”配合) |
layout_width 和 layout_height 为必填的2个属性,可以设置数值(dp),也可以使用:
wrap_content:根据内容自动扩展以适应内容大小
match_parent:控件大小填满整个父容器
ayout_weight 宽度占比示例:

1.TextView

常用属性 |
描述 |
background |
背景颜色(或背景图片) |
autoLink |
文本自动识别为web超链接、Email地址等
 |
autoLink 用法

常用方法
JAVA
1 2 3 4 5 6 7
| ◼ 示例: TextView tv = (TextView) findViewById(R.id.***); String str = tv.getText().toString(); tv.setText("hello"); tv.setTextSize(30); tv.setTextColor(Color.BLUE);
|
示例:TextViewActivity

activity_text_view.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".TextViewActivity"> <TextView android:id="@+id/tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="124dp" android:autoLink="web" android:text="https://www.wust.edu.cn" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="email" android:text="zhangzhi@wust.edu.cn" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.502" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv1" /> <TextView android:id="@+id/tv3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="phone" android:text="+8613012345678" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv2" /> <TextView android:id="@+id/tv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="map" android:text="620 Eighth Avenue New York, NY 10018" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv3" /> <TextView android:id="@+id/tv5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="60dp" android:autoLink="map" android:text="welcome" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.491" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv4" /> <Button android:id="@+id/btn_change" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="修改welcome" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv5" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
TextViewActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class TextViewActivity extends AppCompatActivity { Button btn; TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_text_view); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle("TextView示例"); actionBar.setDisplayHomeAsUpEnabled(true); tv=findViewById(R.id.tv5); btn=findViewById(R.id.btn_change); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tv.setText("武汉科技大学"); tv.setTextSize(22); tv.setTextColor(Color.BLUE); } }); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()){ case android.R.id.home: finish(); } return super.onOptionsItemSelected(item); } }
|
2.EditText

常用属性 |
描述 |
hint |
提示信息 |
textColorHint |
提示信息的颜色 |
inputType |
输入类型(number|textPassword|numberPassword … )
 |
maxLength |
文本最大长度 |
digits |
允许输入的字符 (如digits=”abcd” 只能输入abcd这4个小写字母) |
多行文本设置 |
|
inputType |
“textMultiLine” //显示多行 |
minLines |
“3” //最小显示3行 |
gravity |
“left|top” // 输入时光标位于左上角 |
inputType 取值不同:会使得输入键盘不同

常用方法
◼ 示例:
JAVA
1 2 3
| EditText et = (EditText) findViewById(R.id.***); String msg = et.getText().toString(); et.setText("hello");
|
示例:EditTextActivity

activity_edit_text.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".EditTextActivity"> <EditText android:id="@+id/et_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:ems="10" android:hint="11位手机号" android:inputType="phone" android:maxLength="11" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/et_password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:ems="10" android:hint="6位密码" android:inputType="numberPassword" android:maxLength="6" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et_phone" /> <EditText android:id="@+id/et_multitext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:ems="10" android:hint="个人简介" android:inputType="textMultiLine" android:minLines="3" android:gravity="top|left" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.502" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et_password" /> <Button android:id="@+id/btn_getdata" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="获取输入值" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et_multitext" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
EditTextActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class EditTextActivity extends AppCompatActivity { EditText phone; EditText password; EditText info; Button btn; String s_phone; String s_password; String s_info; View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { s_phone = phone.getText().toString(); s_password = password.getText().toString(); s_info = info.getText().toString(); new AlertDialog.Builder(EditTextActivity.this) .setTitle("输入的数据:") .setMessage("电话:" + s_phone + "\n密码:" + s_password + "\n个人简介:\n" + s_info) .setNegativeButton("取消", null) .show(); } }; public void initView() { phone = findViewById(R.id.et_phone); password = findViewById(R.id.et_password); info = findViewById(R.id.et_multitext); btn = findViewById(R.id.btn_getdata); btn.setOnClickListener(listener); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_text); initView(); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle("TextView示例"); actionBar.setDisplayHomeAsUpEnabled(true); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); } return super.onOptionsItemSelected(item); } }
|
3.ImageView

常用属性 |
描述 |
srcCompat |
图片源 |
scaleType |
图片缩放类型,常用取值:center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY、matrix等
 |
scaleType 取值
取值 |
描述 |
fitCenter |
保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView。缩放后图片居中显示。(默认) |
fitXY |
对X和Y方向独立缩放,直到图片铺满ImageView。这种方式可能会改变图片原本的宽高比,导致图片拉伸变形。 |
fitStart |
保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView。缩放后图片左上角对齐进行显示。 |
fitEnd |
保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView。缩放后图片右下角对齐进行显示。 |
center |
图片居中显示在ImageView中,不对图片进行缩放。 |
centerInside |
如果图片宽度<=ImageView宽度&&图片高度<=ImageView高度,不执行缩放,居中显示在ImageView中。其余情况按ScaleType.FIT_CENTER处理。 |
centerCrop |
保持图片的宽高比,等比例对图片进行X和Y方向缩放,直到每个方向都大于等于ImageView对应的尺寸。缩放后的图片居中显示,超出部分做裁 剪处理。 |
matrix |
使用Matrix绘制图片 |


常用方法
◼ 示例:
JAVA
1 2 3 4 5 6 7
| ImageView iv = (ImageView) findViewById(R.id.***); iv.setVisibility( View.INVISIBLE ); ❤ 动态加载图片的重要方法: (1) iv.setImageResource(R.drawable.图片资源) (2) iv.setImageDrawable( getDrawable(R.drawable.图片资源) ); (3) iv.setImageURI(Uri uri) / /通过图片URI地址加载图片 (4) iv.setImageBitmap(Bitmap bitmap)
|
示例:ImageViewActivity

定时器用法:Handler.postDelayed(Runnable, long) 方法
定时器基本架构
JAVA
1 2 3 4 5 6 7 8 9 10 11 12
| Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { handler.postDelayed( this, m ); } };
handler.postDelayed( runnable, n );
handler.removeCallbacks( runnable );
|
注:postDelayed 函数没每次调用只执行一次,所以要使用递归
activity_image_view.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ImageViewActivity"> <ImageView android:id="@+id/iv_photo" android:layout_width="174dp" android:layout_height="180dp" android:layout_marginTop="184dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.496" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/login" /> <Button android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="53dp" android:text="启动定时器" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/iv_photo" /> <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="关闭定时器" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.501" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_start" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
ImageViewActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ImageViewActivity extends AppCompatActivity { ImageView imageView; Button btn1, btn2; int idx = 0; int[ ] resIds = new int[ ]{ R.drawable.lstx_001, R.drawable.lstx_002, R.drawable.lstx_003, R.drawable.lstx_004 }; Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { imageView.setImageResource(resIds[idx]); if (idx < resIds.length - 1) { idx++; } else { idx = 0; } handler.postDelayed(this, 2000); } }; View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_start: handler.postDelayed(runnable, 2000); break; case R.id.btn_cancel: handler.removeCallbacks(runnable); break; } } }; public void initView() { imageView = findViewById(R.id.iv_photo); btn1 = findViewById(R.id.btn_start); btn2 = findViewById(R.id.btn_cancel); btn1.setOnClickListener(listener); btn2.setOnClickListener(listener); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_view); initView(); } }
|
按钮类型 |
描述 |
Button  |
普通按钮(仅包含文本) |
ImageButton  |
图片按钮(仅包含图标)
◼ srcCompat属性:设置图片源 ◼ scaleType属性:同ImageView ◼ 配合backgroundTint = “#00000000” (8个0) 可以透明png图片背景 ==会导致点击没有波纹效果== ◼ 动态修改图片:setImageDrawable(getDrawable(R.drawable.***)); |
Button组件  |
包含文本和图标按钮
◼ text属性:文本内容 ◼ drawableLeft属性:设置图片源(图片在文字左侧) |
drawableLeft 属性
drawableRight 属性:图片在文字右侧
drawableTop 属性:图片在文字上方
drawableBottom 属性:图片在文字下方
按钮监听器:单击事件响应
JAVA
1 2 3 4 5 6 7
| Button btn = (Button) findViewById(R.id.***); bt.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { } });
|
禁用按钮方法:
JAVA
1 2
| btn.setEnabled( false ); btn.setEnabled( true );
|
示例:ButtonActivity

(1) 文字颜色:Button 按下状态改变文字颜色

第 1 步:在 res/drawable/ 目录中新建一个 drawable 资源文件

第 2 步: btn.xml 代码
XML
1 2 3 4 5 6
| <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:color="#ff0" /> <item android:color="#fff" /> </selector>
|
第 3 步: Button 按钮设置
属性 |
值 |
textColor |
@drawable/btn 引用自定义的xml资源 |
注:手工强制添加:android:textColor=”@drawable/btn”
(2) ImageButton:根据按钮状态来更改背景

第 1 步:
先将三种图片复制到 res/drawable/ 目录中
◼ 在 res/drawable/ 目录中新建一个 drawable 资源文件:

第 2 步: button_custom.xml 代码
XML
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable = "@drawable/button_pressed" android:state_pressed = "true" /> <item android:drawable = "@drawable/button_focused" android:state_focused = "true" /> <item android:drawable = "@drawable/button_default" /> </selector>
|
◼ 第一个 <item> 定义按下(激活)按钮后使用的图片
◼ 第二个 <item> 定义按钮处于聚焦状态时(使用方向键突出显示按钮时)使用的图片
◼ 第三个 <item> 定义按钮处于默认状态时(既未按下,也未聚焦时)使用的图片
第 3 步: ImageButton按钮设置
属性 |
值 |
background |
#00000000(8个0,使得png图片背景透明)<- background 透明会让按钮紧密贴合图片 |
srcCompat |
@drawable/button_custom (引用自定义的xml资源) |
activity_button.mxl
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ButtonActivity"> <Button android:id="@+id/button" android:layout_width="131dp" android:layout_height="72dp" android:layout_marginTop="92dp" android:text="Button" android:textColor="@drawable/btn" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.135" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageButton android:id="@+id/imageButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="64dp" android:background="#00000000" app:srcCompat="@drawable/button_custom" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.482" app:layout_constraintStart_toEndOf="@+id/button" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
ButtonActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ButtonActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_button); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle("按钮示例"); actionBar.setDisplayHomeAsUpEnabled(true); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()){ case android.R.id.home: finish(); } return super.onOptionsItemSelected(item); } }
|
按钮类型 |
描述 |
ToggleButton  |
按钮开关,常用属性 textOn、textOff:开、关显示的文字 |
Switch  |
滑块开关,常用属性 switchMinWidth:开关最小宽度 |
通用属性:checked |
true:开启,false:关闭 |
开关的监听器:监听开关的状态改变
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch ( buttonView.getId() ) { case R.id.toggleButton: if ( isChecked ) else break; } } };
|
JAVA
1 2 3 4
| ToggleButton tb = (ToggleButton) findViewById(R.id.toggleButton); Switch sw = (Switch) findViewById(R.id.switch1); tb.setOnCheckedChangeListener(listener); sw.setOnCheckedChangeListener(listener);
|
示例:SwitchActivity

activity_switch.xml
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".SwitchActivity"> <Switch android:id="@+id/switch1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="120dp" android:checked="false" android:switchMinWidth="60dp" android:text="夜间模式" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="88dp" android:text="Button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/switch1" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
SwitchActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class SwitchActivity extends AppCompatActivity { Switch sw; CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch ( buttonView.getId() ) { case R.id.switch1: if ( isChecked ) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); } else { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); } break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_switch); sw = (Switch) findViewById(R.id.switch1); sw.setOnCheckedChangeListener(listener); } }
|

◼ 单选按钮一般归属于某个组(RadioGroup),每组中只能有一个被选中(互斥)
◼ 创建过程:先新建 RadioGroup,然后将 RadioButton 拖放到组中(详见下页)

组件 |
属性描述 |
RadioGroup |
orientation属性:horizontal (水平排列) 或 vertical (垂直排列,默认) |
RadioButton |
text属性:按钮文本,checked属性:是否选中(true表示选中) |
用法示例:先创建 RadioGroup 组件

再创建 RadioButton 组件

RadioButton 的监听器:监听单击事件
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { boolean checked = ((RadioButton) view).isChecked(); switch( view.getId() ) { case R.id.rb_male: if ( checked ) Toast.makeText(getApplicationContext(),"选中男",Toast.LENGTH_SHORT).show(); break; case R.id.rb_female: if ( checked ) Toast.makeText(getApplicationContext(),"选中女",Toast.LENGTH_SHORT).show(); break; } } };
|
JAVA
1 2 3 4
| RadioButton rb1 = (RadioButton) findViewById(R.id.rb_male); RadioButton rb2 = (RadioButton) findViewById(R.id.rb_female); rb1.setOnClickListener(listener); rb2.setOnClickListener(listener);
|
RadioGroup的监听器:监听选择状态改变(常用)
JAVA
1 2 3 4 5 6 7 8
| RadioGroup.OnCheckedChangeListener listener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { RadioButton rb = (RadioButton) findViewById(checkedId); String ss = rb.getText().toString(); Toast.makeText(getApplicationContext(), "选中"+ss, Toast.LENGTH_SHORT).show(); } };
|
JAVA
1 2
| RadioGroup rbg=(RadioGroup)findViewById(R.id.radioGroup); rbg.setOnCheckedChangeListener( listener );
|
示例:RadioActivity

activity_radio.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".RadioActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="32dp" android:text="宋朝开国皇帝是哪一位?" android:textSize="20sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <RadioGroup android:id="@+id/radioGroup" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView"> <RadioButton android:id="@+id/radioButton1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="李渊" android:textSize="20sp" /> <RadioButton android:id="@+id/radioButton2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="赵匡胤" android:textSize="20sp" /> <RadioButton android:id="@+id/radioButton3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="朱元璋" android:textSize="20sp" /> <RadioButton android:id="@+id/radioButton4" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="努尔哈赤" android:textSize="20sp" /> </RadioGroup> </androidx.constraintlayout.widget.ConstraintLayout>
|
RadioActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class RadioActivity extends AppCompatActivity { RadioGroup rg; RadioGroup.OnCheckedChangeListener listener=new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { RadioButton rb = findViewById(checkedId); String answer = rb.getText().toString(); if( answer.equals("赵匡胤") ) Toast.makeText(RadioActivity.this,"答对了",Toast.LENGTH_SHORT).show(); else Toast.makeText(RadioActivity.this,"答错了",Toast.LENGTH_SHORT).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_radio); rg= findViewById(R.id.radioGroup); rg.setOnCheckedChangeListener(listener); } }
|
7.CheckBox
属性 |
描述 |
text |
按钮文本 |
checked |
是否选中(true为选中) |
CheckBox 的监听器:监听选择状态的改变
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch ( buttonView.getId() ) { case R.id.cb_music: if ( isChecked ) Toast.makeText(getApplicationContext(), "选择音乐", Toast.LENGTH_SHORT).show(); else Toast.makeText(getApplicationContext(), "取消选择音乐", Toast.LENGTH_SHORT).show(); break; case R.id.cb_movie: … } } };
|
JAVA
1 2 3 4 5 6
| CheckBox cb1 = (CheckBox) findViewById(R.id.cb_music); CheckBox cb2 = (CheckBox) findViewById(R.id.cb_movie); CheckBox cb3 = (CheckBox) findViewById(R.id.cb_sport); cb1.setOnCheckedChangeListener(listener); cb2.setOnCheckedChangeListener(listener); cb3.setOnCheckedChangeListener(listener);
|
示例:CheckBoxActivity 使用 List 存储选中项

activity_check_box.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?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" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constrainLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CheckBoxActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="44dp" android:layout_marginTop="76dp" android:text="选择爱好" android:textSize="20sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <CheckBox android:id="@+id/cb_music" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="40dp" android:layout_marginTop="120dp" android:text="音乐" android:textSize="20sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <CheckBox android:id="@+id/cb_movie" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="120dp" android:text="电影" android:textSize="20sp" app:layout_constraintStart_toEndOf="@+id/cb_music" app:layout_constraintTop_toTopOf="parent" /> <CheckBox android:id="@+id/cb_sport" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="120dp" android:text="体育" android:textSize="20sp" app:layout_constraintStart_toEndOf="@+id/cb_movie" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
CheckBoxActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class CheckBoxActivity extends AppCompatActivity { private List<String> list = new ArrayList<>(); CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if ( isChecked ) list.add( buttonView.getText().toString() ); else list.remove( buttonView.getText().toString() ); Log.d("flag", list.toString()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_check_box); CheckBox cb1 = (CheckBox) findViewById(R.id.cb_music); CheckBox cb2 = (CheckBox) findViewById(R.id.cb_movie); CheckBox cb3 = (CheckBox) findViewById(R.id.cb_sport); cb1.setOnCheckedChangeListener(listener); cb2.setOnCheckedChangeListener(listener); cb3.setOnCheckedChangeListener(listener); } }
|
8.SeekBar

属性/方法 |
描述 |
min |
滑动条的最小值(一般设置为 0) |
max |
滑动条的最大值(如 100) |
setMin(int) |
设置滑动条的最小值 |
setMax(int) |
设置滑动条的最大值 |
progress |
滑动条的当前值(如初始设置为50) |
setProgress(int) |
设置滑动条当前值 |
getProgress() |
返回滑动条当前值 |
thumb |
滑块的drawable(如设置滑块图片)
 |
SeekBar 监听器:监听滑块进度变化
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } });
|
示例:SeekBarActivity

activity_seek_bar.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".SeekBarActivity"> <TextView android:id="@+id/seekbarvalue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:text="1×" android:textSize="16sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <SeekBar android:id="@+id/seekBar2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="80dp" android:layout_marginEnd="24dp" android:max="20" android:min="0" android:progress="10" android:thumb="@drawable/twitter" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
<ImageView android:id="@+id/photo2" android:layout_width="285dp" android:layout_height="371dp" android:layout_marginTop="32dp" android:scaleType="matrix" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/seekBar2" app:srcCompat="@drawable/alert" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
SeekBarActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class SeekBarActivity extends AppCompatActivity { SeekBar seekBar; TextView seekBarValue; ImageView imageView; Matrix matrix = new Matrix(); public void scale(float sx,float sy) { matrix.set( imageView.getImageMatrix() ); int x = imageView.getDrawable().getBounds().centerX(); int y = imageView.getDrawable().getBounds().centerY(); matrix.setScale( sx, sy , x, y ); imageView.setImageMatrix( matrix ); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_seek_bar); seekBar = (SeekBar) findViewById(R.id.seekBar2); seekBarValue = (TextView) findViewById(R.id.seekbarvalue); imageView = findViewById(R.id.photo2); imageView.setScaleType(ImageView.ScaleType.MATRIX); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { seekBarValue.setText( progress/10.0f + "×" ); scale( progress/10.0f, progress/10.0f ); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); } }
|
9.Spinner
常用属性和方法 |
描述 |
entries属性 |
选项的数据来源(静态填充数据) |
getSelectedItem() |
获取选中项(注:使用toString()转为字符串) |
getItemAtPosition(int) |
根据位置选择某项(注:使用toString()转为字符串) |
说明:默认情况下 Spinner 字体/颜色等不能直接设置,需自定义布局
Spinner 数据填充:静态填充
(1)先在 res/values/strings.xml 文件中声明字符串数组
XML
1 2 3 4 5 6
| <string-array name="major_array"> <item>软件工程</item> <item>计算机科学</item> <item>网络工程</item> <item>信息安全</item> </string-array>
|
(2)Spinner的entries属性设置为:@array/major_array ==静态引用==
Spinner 数据填充:动态填充(常用)
JAVA
1 2 3 4 5 6 7 8 9 10 11
| List<String> list = new ArrayList<String>();
list.add("软件工程"); list.add("计算机科学"); list.add("网络工程"); list.add("信息安全");
ArrayAdapter<String> adapter = new ArrayAdapter<String>( 当前Activity.this, android.R.layout.simple_list_item_1, list );
Spinner spinner = (Spinner)findViewById(R.id.spinner的id); spinner.setAdapter(adapter);
|
系统内置的常见布局:
android.R.layout.布局id |
描述 |
simple_list_item_1 |
每个列表项是一个普通的文字 |
simple_list_item_2 |
每个列表项是两个文本(一主一副) ->  |
simple_list_item_checked |
每个列表项会有一个选中项 |
simple_list_item_single_choice |
每个列表项后面会有一个radio按钮 |
simple_list_item_multiple_choice |
每个列表项后面会有一个checkbox按钮 |

什么是 Adapter
◼ Adapter:称为”适配器”,是用来帮助 UI 组件填充数据的中间桥梁。(也是一种 MVC 体系结构)

常见的 Adapter:

◼ BaseAdapter:是一个抽象类,继承它需要实现较多的方法,所以也就具有较高的灵活性。
◼ ArrayAdapter:支持泛型操作,最为简单,只能展示一行字。
◼ SimpleAdapter:有很好的扩充性,可以自定义出各种效果。
◼ SimpleCursorAdapter:与数据库的简单结合,可以方便地把数据库的内容以列表的形式展示出来。
使用自定义布局:详见示例1
JAVA
1 2 3 4 5 6 7 8 9 10
| List<String> list = new ArrayList<String>(); list.add("软件工程"); list.add("计算机科学"); list.add("网络工程"); list.add("信息安全");
ArrayAdapter<String> adapter = new ArrayAdapter<String>(当前Activity.this, R.layout.spinner_custom, R.id.tv_item, list); Spinner spinner = (Spinner)findViewById(R.id.spinner的id); spinner.setAdapter(adapter);
|
Spinner 监听器:监听选中项的变化
JAVA
1 2 3 4 5 6 7 8 9 10 11
| spinner.setOnItemSelectedListener( new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { } @Override public void onNothingSelected(AdapterView<?> parent) { } });
|
其中 onItemSelected 函数四个参数含义:
◼ parent:指 Spinner 对象
◼ view:选中项的视图对象
◼ position:选中项的位置值
◼ id:选中项的行 id 值(从 0 开始)
注:一般情况 position 和 id 值一样
注:Spinner 不要用 setOnItemClickListener 监听器,会闪退
示例1:Spinner1Activity

界面布局代码:activity_spinner1.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Spinner1Activity"> <TextView android:id="@+id/tv_major" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="52dp" android:layout_marginTop="104dp" android:text="专业选择:" android:textColor="@color/purple_500" android:textSize="18sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Spinner android:id="@+id/sp_major" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginStart="52dp" android:layout_marginTop="12dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_major" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
Spinner1Activity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Spinner1Activity extends AppCompatActivity { Spinner sp_major; TextView tv_major; List<String> list; ArrayAdapter<String> adapter; AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { tv_major.setText("专业选择:" + parent.getSelectedItem()); } @Override public void onNothingSelected(AdapterView<?> parent) { } }; public void initView(){ tv_major=findViewById(R.id.tv_major); sp_major=findViewById(R.id.sp_major); sp_major.setOnItemSelectedListener(listener); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_spinner1); initView(); list = new ArrayList<String>(); list.add("软件工程"); list.add("计算机科学"); list.add("网络工程");
adapter = new ArrayAdapter<String>( Spinner1Activity.this,R.layout.spinner_custom, R.id.tv_item, list ); sp_major.setAdapter(adapter); } }
|
自定义布局:在 res/layout 中新建布局资源文件

自定义布局的设计:

自定义布局的代码:sipnner_custom.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/spinner_custom" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="1dp" android:layout_marginTop="1dp" android:layout_marginEnd="1dp" android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent">
<TextView android:id="@+id/tv_item" android:layout_width="wrap_content" android:layout_height="40dp" android:layout_weight="1" android:gravity="center_vertical" android:text="item内容" android:textColor="@android:color/holo_green_dark" android:textSize="20sp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
|
示例2:Spinner2Activity

界面布局代码:activity_spinner2.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?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" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constrainLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Spinner2Activity"> <Spinner android:id="@+id/sp_province" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginStart="1dp" android:layout_marginTop="80dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Spinner android:id="@+id/sp_city" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="80dp" app:layout_constraintStart_toEndOf="@+id/sp_province" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
Spinner2Activity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class Spinner2Activity extends AppCompatActivity { Spinner sp_province; Spinner sp_city; List<String> provinceList = new ArrayList<String>( Arrays.asList("选择省","湖北省","湖南省") ); List<String> cityList = new ArrayList<String>( Arrays.asList("选择市") ); ArrayAdapter<String> adapter; public void setAdapter( Spinner spinner, List<String> list ) { adapter = new ArrayAdapter<String>( Spinner2Activity.this, android.R.layout.simple_list_item_1, list ); spinner.setAdapter(adapter); } public void initView() { sp_province = (Spinner)findViewById(R.id.sp_province); sp_city = (Spinner)findViewById(R.id.sp_city); setAdapter(sp_province,provinceList); setAdapter(sp_city,cityList); sp_province.setOnItemSelectedListener( listener ); }
private AdapterView.OnItemSelectedListener listener=new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if ( parent.getId() == R.id.sp_province ) { String pName = parent.getSelectedItem().toString(); switch (pName) { case "选择省": cityList.clear(); cityList.add("选择市"); break; case "湖北省": cityList.clear(); cityList.add("武汉"); cityList.add("宜昌"); break; case "湖南省": cityList.clear(); cityList.add("长沙"); cityList.add("湘潭"); break; } setAdapter(sp_city, cityList); sp_city.setOnItemSelectedListener(listener); } else if ( parent.getId() == R.id.sp_city ) { Toast.makeText(Spinner2Activity.this, "选中的是:" + sp_province.getSelectedItem() + sp_city.getSelectedItem(), Toast.LENGTH_SHORT).show(); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_spinner2); initView(); } }
|
10.ListView

常用属性和方法 |
描述 |
entries属性 |
选项的数据来源(静态填充) |
setChoiceMode(int) |
设置选中模式 ◼ 多选:ListView.CHOICE_MODE_MULTIPLE ◼ 单选:ListView.CHOICE_MODE_SINGLE(默认) |
getItemAtPosition(int) |
根据位置选择某项,使用toString()转为字符串 |
getCount() |
获得选项总数 |
getCheckedItemCount() |
获得选中个数 |
setSelector(资源id) |
设置高亮选中项的背景色或图片 |
常用布局配合选中模式

ListView数据动态填充:以多选为例
JAVA
1 2 3 4 5 6 7 8 9 10 11
| List<String> list = new ArrayList<String>(); list.add("Java"); list.add("C"); list.add("Python"); ArrayAdapter<String> adapter = new ArrayAdapter<String>( MainActivity.this, android.R.layout.simple_list_item_multiple_choice, list ); ListView listView = (ListView)findViewById(R.id.listView); listView.setAdapter(adapter); listView.setChoiceMode( ListView.CHOICE_MODE_MULTIPLE );
|

ListView 监听器:监听选项单击事件,以多选为例
JAVA
1 2 3 4 5 6 7 8 9 10 11 12
| listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListView lv = (ListView)parent; if ( ((CheckedTextView) view).isChecked() ) else } });
|
onItemClick 函数的四个参数含义:
◼ parent:指 ListView 对象
◼ view:击中项的视图对象
◼ position:击中项的位置值
◼ id:击中项的行 id 值(从 0 开始)
注:一般情况 position 和 id 值一样
注:ListView不使用OnItemSelectedListener (Spinner才用)
示例:ListViewActivity

activity_list_view.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ListViewActivity"> <TextView android:id="@+id/tv_skill" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="选择擅长的技术" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ListView android:id="@+id/lv_skill" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_skill" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
ListViewActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class ListViewActivity extends AppCompatActivity { ListView listView; ArrayAdapter<String> adapter; List<String> skill_list = new ArrayList<>(Arrays.asList("Java", "C", "Python")); List<String> selected_list = new ArrayList<>(); public void initView() { listView = findViewById(R.id.lv_skill); adapter= new ArrayAdapter<String>( ListViewActivity.this, android.R.layout.simple_list_item_multiple_choice, skill_list); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); listView.setOnItemClickListener(listener); } AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListView lv = (ListView) parent; if ( ((CheckedTextView) view).isChecked() ) { selected_list.add(lv.getItemAtPosition(position).toString()); } else { selected_list.remove(lv.getItemAtPosition(position).toString()); } Log.d("flag", selected_list.toString()); Log.d("flag", "一共选中" + lv.getCheckedItemCount() + "/" + lv.getCount()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_view); initView(); } }
|
11.AutoCompleteTextView

常用属性 |
描述 |
completionHint |
设置下拉菜单中的提示标题 |
completionThreshold |
指定用户至少输入多少个字符才会显示提示 |
singleLine |
true为单行,false为多行(默认) |
用法示例:动态填充
JAVA
1 2 3 4 5 6 7
| List<String> data = new ArrayList<>(Arrays.asList("Apple", "AMD", "Acer", "Intel", "Microsoft")); AutoCompleteTextView auto = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(当前Activity.this, android.R.layout.simple_dropdown_item_1line, data); auto.setThreshold(1); auto.setCompletionHint("提示信息"); auto.setAdapter(adapter);
|
示例:AutoCompleteActivity

activity_auto_complete.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".AutoCompleteActivity"> <AutoCompleteTextView android:id="@+id/autoCompleteTextView" android:layout_width="250dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="60dp" android:hint="输入公司名" android:singleLine="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/btn_search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="60dp" android:backgroundTint="@android:color/holo_green_light" android:text="搜索" android:textSize="20sp" app:layout_constraintStart_toEndOf="@+id/autoCompleteTextView" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
AutoCompleteActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class AutoCompleteActivity extends AppCompatActivity { AutoCompleteTextView auto; Button btn; List<String> data = new ArrayList<>(Arrays.asList("Apple", "AMD", "Acer", "Intel", "Microsoft")); ArrayAdapter<String> adapter; View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { String input = auto.getText().toString(); if( data.contains(input) ){ Toast.makeText(AutoCompleteActivity.this,"查询中...",Toast.LENGTH_SHORT).show(); }else{ data.add(input); adapter.add(input); adapter.notifyDataSetChanged(); Toast.makeText(AutoCompleteActivity.this,"已添加",Toast.LENGTH_SHORT).show(); } } }; public void initView() { btn = findViewById(R.id.btn_search); btn.setOnClickListener(listener); auto = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView); adapter = new ArrayAdapter<String>(AutoCompleteActivity.this, android.R.layout.simple_dropdown_item_1line, data); auto.setThreshold(1); auto.setCompletionHint("公司名称"); auto.setAdapter(adapter); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auto_complete); initView(); } }
|
12.DatePicker和TimePicker

UI 组件代码
注:需手工输入代码
XML
1 2 3 4 5 6 7 8 9 10 11 12
| <DatePicker android:id="@+id/datePicker" android:layout_width="wrap_content" android:layout_height="wrap_content"> </DatePicker>
<TimePicker android:id="@+id/timePicker" android:layout_width="wrap_content" android:layout_height="wrap_content"> </TimePicker>
|
DatePicker 监听器:监听日期改变
JAVA
1 2 3 4 5 6 7
| DatePicker datePicker=(DatePicker) findViewById(R.id.datePicker); datePicker.setOnDateChangedListener(new DatePicker.OnDateChangedListener() { @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { } });
|
日期和时间选择对话框(常用)

日期选择对话框:
JAVA
1 2 3 4 5 6 7 8
| Calendar data = Calendar.getInstance(); DatePickerDialog dpd = new DatePickerDialog( 当前Activity.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int month, int day) { } }, data.get(Calendar.YEAR), data.get(Calendar.MONTH), data.get(Calendar.DAY_OF_MONTH) ); dpd.show();
|
时间选择对话框:
JAVA
1 2 3 4 5 6 7 8
| Calendar data = Calendar.getInstance(); TimePickerDialog tpd = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { } }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), true); tpd.show();
|
示例:日期和时间选择对话框

重要:EditText 单击触发 onclick 事件处理
◼ 默认情况下:EditText 单击只能获得焦点,并不触发 onclick 事件
◼ 如果要直接触发 onclick 事件,EditText 添加两个属性
• android:clickable = "true"
//允许单击
• android:focusableInTouchMode="false"
//不获得焦点,此时会触发 onclick 事件
activity_date_time_picker.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".DateTimePickerActivity"> <EditText android:id="@+id/et_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:ems="10" android:hint="输入日期" android:inputType="textPersonName" android:clickable="true" android:focusableInTouchMode="false" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/et_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="100dp" android:ems="10" android:hint="输入时间" android:inputType="textPersonName" android:textSize="20sp" android:clickable="true" android:focusableInTouchMode="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
DateTimePickerActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public class DateTimePickerActivity extends AppCompatActivity { EditText et_date, et_time; public void initView() { et_date = findViewById(R.id.et_date); et_time = findViewById(R.id.et_time); et_date.setOnClickListener(listener); et_time.setOnClickListener(listener); } View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.et_date: getDate(); break; case R.id.et_time: getTime(); break; } } };
public void getDate() { Calendar data = Calendar.getInstance(); DatePickerDialog dpd = new DatePickerDialog(DateTimePickerActivity.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int month, int day) { String str; str = String.valueOf(year) + "-" + String.valueOf(month+1) + "-" + String.valueOf(day); et_date.setText(str); } }, data.get(Calendar.YEAR), data.get(Calendar.MONTH), data.get(Calendar.DAY_OF_MONTH)); dpd.show(); }
public void getTime() { Calendar calendar = Calendar.getInstance(); TimePickerDialog tpd = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { String str; str = String.valueOf(hourOfDay) + ":" + String.valueOf(minute) ; et_time.setText(str); } }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), true); tpd.show(); }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_date_time_picker); initView(); } }
|
编程示例
模拟登录
基本步骤: app全屏显示
◼ Activity主题和配置
◼ Activity全屏
◼ 界面布局设计
◼ 按钮文字颜色(详见5.2节)
◼ UI编程

1.Activity 主题和配置
◼ 新建Activity: LoginActivity
◼ 在 themes.xml 中添加新主题:
XML
1 2 3 4 5 6 7
| <style name="Theme.Login" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <item name="colorPrimary">@android:color/holo_orange_dark</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorOnPrimary">@color/white</item> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> </style>
|

修改 Activity 配置
◼ 在 AndroidManifest.xml 修改 LoginActivity 配置:
JAVA
1 2 3 4 5 6 7 8 9 10 11
| <activity android:name=".LoginActivity" android:exported="true" android:icon="@drawable/login" android:label="用户登录" android:theme="@style/Theme.Login"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
|

运行APP:观察主题效果

2.Activity全屏
◼ LoginActivity 添加代码:
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class LoginActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch ( item.getItemId() ) { case android.R.id.home: finish(); } return super.onOptionsItemSelected(item); } }
|
运行结果:

3.界面布局设计

布局设计:ImageView 组件

布局设计:TextView组件(手机号标签)

布局设计:EditText组件(手机号文本框)

布局设计:TextView组件(密码标签)

布局设计:EditText组件(密码框)

布局设计:Button组件(登录按钮)

最终布局代码:activity_login.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".LoginActivity"> <ImageView android:id="@+id/photo" android:layout_width="165dp" android:layout_height="177dp" android:layout_marginStart="16dp" android:layout_marginTop="32dp" android:layout_marginEnd="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/login" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="50dp" android:text="手机号" android:textSize="24sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/photo" /> <EditText android:id="@+id/phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:ems="10" android:hint="11位手机号" android:inputType="phone" android:maxLength="11" android:textSize="24sp" app:layout_constraintBaseline_toBaselineOf="@+id/textView1" app:layout_constraintStart_toEndOf="@+id/textView1" />
<TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="25dp" android:text="密 码" android:textSize="24sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView1" />
<EditText android:id="@+id/password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:ems="10" android:hint="不超过10个字符" android:inputType="textPassword" android:maxLength="10" android:textSize="24sp" app:layout_constraintBaseline_toBaselineOf="@+id/textView2" app:layout_constraintStart_toEndOf="@+id/textView2" />
<Button android:id="@+id/login_btn" android:layout_width="379dp" android:layout_height="66dp" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="36dp" android:backgroundTint="@android:color/holo_green_light" android:text="登录" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
运行结果:

4.UI编程

获取输入的数据(LoginActivity代码)
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| private EditText phone; private EditText password; private Button login;
private String s_phone; private String s_password;
View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) login(); } };
public void init() { phone = (EditText) findViewById(R.id.phone); password = (EditText) findViewById(R.id.password); login = (Button) findViewById(R.id.login_btn); login.setOnClickListener(listener); } public void login() { s_phone = phone.getText().toString(); s_password = password.getText().toString(); String msg = "手机号=" + s_phone + ",密码=" + s_password; Log.d("flag", msg); Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); … init(); }
|
模拟验证码重发(10秒倒计时)
倒计时CountDownTimer用法
JAVA
1 2 3 4 5 6 7 8 9 10 11
| CountDownTimer countDownTimer = new CountDownTimer(参数1, 参数2) { @Override public void onTick(long millisUntilFinished) { … } @Override public void onFinish() { … } };
|
◼ 启动倒计时:countDownTimer.start();
◼ 取消倒计时:countDownTimer.cancel();
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class LoginActivity extends AppCompatActivity { …. private Button btn_send; public void init() { … btn_send = (Button)findViewById( R.id.btn_send ); btn_send.setOnClickListener( listener ); } CountDownTimer countDownTimer = new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { btn_send.setText( millisUntilFinished / 1000 +"秒后可重发" ); } @Override public void onFinish() { btn_send.setEnabled(true); btn_send.setText("发送验证码"); } }; View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { switch (view.getId()){ case R.id.login_btn: login(); break; case R.id.btn_send: btn_send.setEnabled(false); countDownTimer.start(); break; } } }; … }
|
图片查看
◼ 新建 Activity: PicShowActivity

图片准备:本例图片文件存放在 assets 文件夹中
◼ assets 文件夹:
◼ 可以存放如图片、音频、视频等资源
◼ 该目录下的文件在打包后会原封不动的保存在 apk 包中,不会被编译成二进制
◼ assets 可以有目录结构,也就是 assets 目录下可以再建立文件夹
◼ 注意:
◼ assets 文件夹下的文件不会被映射到 R.java 中,访问的时候需要 AssetManager 类
assets文件读取方法:
◼ 读取 assets 下的文件资源:
JAVA
1 2
| String[] fNames = getAssets().list("pic"); InputStream is = getAssets().open("filename");
|
新建 assets 目录
◼ 项目右键→New → Folder → Assets Folder

在assets目录中新建子目录:
◼ assets目录右键 → New → Directory

将图片资源复制到 assets/pic 下

activity_pic_show.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".PicShowActivity"> <TextView android:id="@+id/tv_pic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="选择图片查看" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ListView android:id="@+id/lv_pic" android:layout_width="match_parent" android:layout_height="300dp" android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_pic" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| public class PicShowActivity extends AppCompatActivity { ListView listView; ArrayAdapter<String> adapter; List<String> pic_list = new ArrayList<>(); public void initView() { listView = findViewById(R.id.lv_pic); pic_list = getPic(); adapter = new ArrayAdapter<String>( PicShowActivity.this, android.R.layout.simple_list_item_single_choice, pic_list); listView.setAdapter(adapter); listView.setChoiceMode( ListView.CHOICE_MODE_SINGLE ); listView.setOnItemClickListener(listener); } public List<String> getPic() { List<String> pList = new ArrayList<>(); try { String[] fNames = getAssets().list("pic"); for (String fn : fNames) { pList.add(fn); } } catch (IOException e) { throw new RuntimeException(e); } return pList; }
AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListView lv = (ListView) parent; lv.setSelector(R.color.purple_200); String pName = parent.getItemAtPosition(position).toString(); showPic(pName); } };
public void showPic(String pName) { try { InputStream is = getAssets().open("pic/" + pName); Bitmap bitmap = BitmapFactory.decodeStream(is); final Dialog dialog = new Dialog(PicShowActivity.this, android.R.style.Theme_Black_NoTitleBar_Fullscreen); ImageView imgView = getView(bitmap); dialog.setContentView(imgView); dialog.show(); imgView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); } catch (IOException e) { throw new RuntimeException(e); } }
private ImageView getView(Bitmap bitmap) { ImageView iv = new ImageView( PicShowActivity.this ); iv.setLayoutParams(new ActionBar.LayoutParams( ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT)); iv.setImageBitmap(bitmap); return iv; }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pic_show); initView(); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle("图片查看示例"); actionBar.setDisplayHomeAsUpEnabled(true); } }
|
音乐播放器
ExoPlayer+SeekBar+定时器
◼ 新建 Activity: MusicActivity

音乐准备:本例音乐放在 res/raw 目录下
◼ 在 res 中新建 raw 资源文件夹,然后将音乐(例如she.mp3)复制到 raw 文件夹

了解:asset 和 res/raw 文件夹区别
◼ 相同点:
◼ asset 和 res/raw 文件夹下都可以存放如音频、视频等资源
◼ 两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制
◼ 不同点:
◼ res/raw 中的文件会被映射到 R.java 文件中,访问的时候直接使用资源 ID,即 R.raw.资源id;
◼ assets 文件夹下的文件不会被映射到 R.java 中,访问的时候需要 AssetManager 类
◼ res/raw 不可以有目录结构,而 assets 则可以有目录结构,也就是 assets 目录下可以再建立文件夹
文件读取的区别
◼ 读取 res/raw 下的文件资源:
JAVA
1
| InputStream is = getResources().openRawResource( R.raw.资源id );
|
◼ 读取 assets 下的文件资源:
JAVA
1 2
| String[] fNames = getAssets().list("music"); InputStream is = getAssets().open("filename");
|
关于:ExoPlayer 媒体播放器
◼ Google 推出的开源媒体播放器 (可取代 MediaPlayer)
◼ 支持本地音视频的播放,同时也支持网络视频播放
◼ 使用前需要添加依赖:如下:
添加 ExoPlayer 依赖

媒体播放器常用方法
方法 |
描述 |
创建ExoPlayer对象 |
ExoPlayer player = new ExoPlayer.Builder( MusicActivity.this ).build(); |
setMediaItem() |
添加媒体(支持本地和远程资源) |
prepare() |
在播放前调用此方法完成播放准备工作 |
start() |
开始或继续播放媒体 |
pause() |
暂停播放 |
stop() |
停止播放 |
release() |
释放播放器对象相关资源 |
isPlaying() |
判断当前播放器是否正在播放 |
seekTo() |
从指定位置开始播放 |
getDuration() |
获取媒体文件的时长(毫秒) |
getContentPosition() |
获取媒体播放的当前位置(毫秒) |
addListener() |
添加监听器(如onPlaybackStateChanged播放状态监听等,比较多) |
setRepeatMode() |
设置循环播放模式:ExoPlayer.REPEAT_MODE_ONE 单曲循环 ExoPlayer.REPEAT_MODE_ALL 全部循环 |
activity_music.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <?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" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constrainLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MusicActivity"> <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="100dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageButton android:id="@+id/btn_player" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="64dp" android:backgroundTint="@android:color/holo_orange_light" android:scaleType="fitCenter" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/seekBar" app:srcCompat="@android:drawable/ic_media_play" /> <TextView android:id="@+id/seekBarHint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="当前进度" android:textSize="18sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/seekBar" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
MusicActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| public class MusicActivity extends AppCompatActivity { SeekBar seekBar; TextView seekBarHint; ImageButton btn_player; ExoPlayer player; private Timer timer; private boolean prepared; private class ProgressUpdate extends TimerTask { @Override public void run() { runOnUiThread( new Runnable() { @Override public void run() { long position = player.getContentPosition(); Log.d("flag", "pos=" + position); seekBar.setProgress((int) position); seekBarHint.setText( format(position) ); } }); } } public String format(long position) { SimpleDateFormat sdf = new SimpleDateFormat("mm:ss"); String timeStr = sdf.format(position); return timeStr; } public void initView(){ btn_player = (ImageButton) findViewById(R.id.btn_player); seekBar = (SeekBar) findViewById(R.id.seekBar); seekBarHint = (TextView) findViewById(R.id.seekBarHint); }
public void initExoPlayer() { player = new ExoPlayer.Builder(MusicActivity.this).build(); MediaItem mediaItem = MediaItem.fromUri( RawResourceDataSource.buildRawResourceUri( R.raw.she )); player.setMediaItem(mediaItem); player.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); player.prepare(); } Player.Listener listener1 = new Player.Listener() { @Override public void onPlaybackStateChanged(int playbackState) { if (playbackState == ExoPlayer.STATE_READY) { prepared = true; long realDurationMillis = player.getDuration(); seekBar.setMax((int) realDurationMillis); } } }; View.OnClickListener listener2 = new View.OnClickListener() { @Override public void onClick(View v) { if ( !prepared ) return; if ( player.isPlaying() ) { player.pause(); btn_player.setImageResource(android.R.drawable.ic_media_play); timer.cancel(); timer = new Timer(); } else { player.play(); btn_player.setImageResource(android.R.drawable.ic_media_pause); timer = new Timer(); timer.schedule(new ProgressUpdate(), 300, 500); } } }; SeekBar.OnSeekBarChangeListener listener3 = new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (prepared && fromUser) { player.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { seekBarHint.setText(format(seekBar.getProgress())); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_music); initView(); prepared = false; initExoPlayer();
player.addListener(listener1); btn_player.setOnClickListener(listener2); seekBar.setOnSeekBarChangeListener(listener3); } @Override protected void onDestroy() { super.onDestroy(); player.stop(); player.release(); } }
|
Activity间的跳转
在 Android 中,Activity 跳转是指从一个 Activity 转到另一个 Activity 的过程。可以通过 Intent 来实现 Activity 之间的跳转。一般情况下,使用startActivity() 方法来启动一个新的 Activity,也可以使用 startActivityForResult() 方法来启动一个新的 Activity 并返回结果。在跳转时,需要指定目标 Activity 的类名或 Intent 对象。同时,也可以在 Intent 中传递参数,以便在目标 Activity 中使用。
下面给出一个通过 Intent 来实现 Activity 间跳转的案例。
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class TestActivity extends AppCompatActivity { private Intent intent2;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test);
intent2=new Intent(); intent2.setComponent(new ComponentName(TestActivity.this, LoginActivity.class));
startActivity(intent2); } }
|
一个用于跳转到多个 Activity 的案例
activity_test.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".TextViewActivity"> <TextView android:id="@+id/tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="124dp" android:autoLink="web" android:text="https://www.wust.edu.cn" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="email" android:text="zhangzhi@wust.edu.cn" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.502" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv1" /> <TextView android:id="@+id/tv3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="phone" android:text="+8613012345678" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv2" /> <TextView android:id="@+id/tv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:autoLink="map" android:text="620 Eighth Avenue New York, NY 10018" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv3" /> <TextView android:id="@+id/tv5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="60dp" android:autoLink="map" android:text="welcome" android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.491" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv4" /> <Button android:id="@+id/btn_change" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="修改welcome" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv5" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
TestActivity.java
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| public class TestActivity extends AppCompatActivity {
class BtnClickListener implements View.OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.activity_auto_complete: Intent intent0 = new Intent(TestActivity.this, AutoCompleteActivity.class); startActivity(intent0); break; case R.id.activity_button: Intent intent1 = new Intent(TestActivity.this, ButtonActivity.class); startActivity(intent1); break; case R.id.activity_check_box: Intent intent2 = new Intent(TestActivity.this, CheckBoxActivity.class); startActivity(intent2); break; case R.id.activity_date_time_picker: Intent intent3 = new Intent(TestActivity.this, DateTimePickerActivity.class); startActivity(intent3); break; case R.id.activity_edit_text: Intent intent4 = new Intent(TestActivity.this, EditTextActivity.class); startActivity(intent4); break; case R.id.activity_image_view: Intent intent5 = new Intent(TestActivity.this, ImageViewActivity.class); startActivity(intent5); break; case R.id.activity_list_view: Intent intent6 = new Intent(TestActivity.this, ListViewActivity.class); startActivity(intent6); break; case R.id.activity_login: Intent intent7 = new Intent(TestActivity.this, LoginActivity.class); startActivity(intent7); break; case R.id.activity_music: Intent intent8 = new Intent(TestActivity.this, MusicActivity.class); startActivity(intent8); break; case R.id.activity_pic_show: Intent intent9 = new Intent(TestActivity.this, PicShowActivity.class); startActivity(intent9); break; case R.id.activity_radio: Intent intent10 = new Intent(TestActivity.this, RadioActivity.class); startActivity(intent10); break; case R.id.activity_seek_bar: Intent intent11 = new Intent(TestActivity.this, SeekBarActivity.class); startActivity(intent11); break; case R.id.activity_spinner1: Intent intent12 = new Intent(TestActivity.this, Spinner1Activity.class); startActivity(intent12); break; case R.id.activity_spinner2: Intent intent13 = new Intent(TestActivity.this, Spinner2Activity.class); startActivity(intent13); break; case R.id.activity_switch: Intent intent14 = new Intent(TestActivity.this, SwitchActivity.class); startActivity(intent14); break; } } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test);
BtnClickListener btnClickListener = new BtnClickListener();
findViewById(R.id.activity_auto_complete).setOnClickListener(btnClickListener);
findViewById(R.id.activity_button).setOnClickListener(btnClickListener);
findViewById(R.id.activity_check_box).setOnClickListener(btnClickListener);
findViewById(R.id.activity_date_time_picker).setOnClickListener(btnClickListener);
findViewById(R.id.activity_edit_text).setOnClickListener(btnClickListener);
findViewById(R.id.activity_image_view).setOnClickListener(btnClickListener);
findViewById(R.id.activity_list_view).setOnClickListener(btnClickListener);
findViewById(R.id.activity_login).setOnClickListener(btnClickListener);
findViewById(R.id.activity_music).setOnClickListener(btnClickListener);
findViewById(R.id.activity_pic_show).setOnClickListener(btnClickListener);
findViewById(R.id.activity_radio).setOnClickListener(btnClickListener);
findViewById(R.id.activity_seek_bar).setOnClickListener(btnClickListener);
findViewById(R.id.activity_spinner1).setOnClickListener(btnClickListener);
findViewById(R.id.activity_spinner2).setOnClickListener(btnClickListener);
findViewById(R.id.activity_switch).setOnClickListener(btnClickListener); } }
|
v1.5.2