主要内容:Android项目创建 + 运行 + 调试

创建Android项目




如果碰到 gradle 下载慢可以采用阿里云镜像来加速:
修改项目根目录下的文件 build.gradle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| buildscript { repositories { maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/'} maven { url 'https://maven.aliyun.com/repository/jcenter/'} mavenLocal() mavenCentral() } ... } allprojects { repositories { maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/google/'} maven { url 'https://maven.aliyun.com/repository/jcenter/'} mavenLocal() mavenCentral() } }
|
参考:gradle下载慢的解决方案
备注2:如果出现 gradle jdk 版本报错(似乎是 AS 升级导致)

项目创建成功:第一次创建项目等待时间较长

查看 APP 界面布局

运行Android程序
◼ 在 AVD 模拟器上运行程序(AVD 首次启动较慢)
◼ 在真实设备上运行程序(手机需要 USB 数据线连接和开启 USB 调试)
APP运行过程

APP运行结果

在真实设备上运行程序
◼ 连接准备:
◼ 先在手机设置中开启开发者选项:需开启 USB 调试、开启 USB 安装等功能 ==注:不同品牌的手机开启方式不太一样,需要查询一下==
◼ 然后将手机通过 USB 与电脑连接

选择真实设备运行:

Android程序调试
◼ 两种方法:
◼ Debug模式(断点调试)
◼ 日志模式(使用Log.d()将调试信息记录在系统日志中, 配合LogCat查看日志)
程序调试示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MainActivity extends AppCompatActivity { private int a; public void count() { a++; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); count(); System.out.println(a); } }
|
Debug模式

Debug 窗口:常用调试工具

日志模式


补充:Logcat 的一些匹配模式

Android编程基础
掌握基础编程:
◼ UI组件和布局
◼ 界面主题Theme
◼ 基于监听的事件处理机制
◼ Toast消息提示框
◼ Snackbar对话框
◼ AlertDialog对话框
◼ 基于回调的事件处理机制
◼ 代码重写封装

UI组件和布局

注:如果不存在约束布局,直接将组件拖入 XML 文件中,则会造成在程序运行时的置顶显示。
UI组件属性设置:

关于 Android 的尺寸单位
◼ 常用:
◼ sp (scaled pixels):常用于指定字体大小,当用户自定义手机字体大小(如小、正常、大、超大等),字体大小会随之改变
◼ dp (density-independent pixels):设备无关像素,这种尺寸单位在不同设备上的物理大小相同,非文字尺寸(如宽度/高度)使用 dp 单位
◼ 其他:
◼ px (pixel):像素,屏幕上显示数据的最基本的点
◼ pt (point):磅,1pt=1/72inch,通常用来作为字体的尺寸单位
◼ inch:英寸,1 英寸约等于 2.54 厘米,主要用来描述手机屏幕大小
关于背景的一些属性
◼ background:是一个可绘制的背景 (可以是一个资源的引用,如图片、可调整大小的 9-patch 位图、XML
状态描述等,或者是颜色如白色)
◼ backgroundTint:是应用到背景上的色彩(只能是颜色),默认会覆盖 background(叠加模式)
◼ backgroundTintMode:设置 backgroundTint 的模式,默认是叠加模式:mulitply
◼ 说明:由于 APP 的主题默认把按钮的 background 设置为 purple_500,所以通过 background 属性修改没有起作用
界面主题Theme
◼ 主题决定了 APP 的展示效果
◼ 可以为整个应用指定主题
◼ 可以为 Activity 单独指定主题
◼ 还可以为各个控件,如 Button、TextView,指定主题
主题定义
(1)界面的主题定义位于:res/values/themes.xml 文件


Primary、Secondary 等基准颜色对照

(2)应用程序的主题在 AndroidManifest.xml 的 theme 属性中引用:
AndroidManifest.xml:应用配置文件(或清单文件)

测试:修改界面主题为桥主题
(1)修改主题的 parent 属性:添加 .Bridge
(2)在桥主题下,可修改按钮的 background 颜色值(试一下)

了解:一些原生 MaterialComponents 主题

尝试添加自己的主题
1 2 3 4 5 6
| <style name="btnStyle"> <item name="android:backgroundTint">#03A9F4</item> <item name="android:textSize">18sp</item> <item name="android:layout_width">150dp</item> <item name="android:layout_height">80dp</item> </style>
|
将以上代码 copy 到 values/themes 包下的 themes.xml 文件中

再在 activity_main.xml 中指定自己新添的 themes

接着,我们可以看到自定义样式已经被覆盖了

基于监听的事件处理机制
◼ 事件:表示程序和用户之间的交互,例如:单击按钮、在文本框中输入、选中单选按钮等
◼ 事件处理:表示程序对事件的响应
◼ 当事件发生时,系统会自动捕捉这一事件,同时创建事件对象并把它们传给事件监听器,后者调用相应的事件处理程序,以使用户得到相应的回答
事件监听处理机制流程:事件监听机制:由事件源,事件以及事件监听器三类对象组成

事件监听示例:用匿名内部类实现(一次性,不能复用)
◼ 以按钮编程为例:
1 2 3 4 5 6 7
| Button btn = (Button) findViewById( R.id.按钮id ); btn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { } });
|
补充:用Lambda表达式实现更简洁
btn.setOnClickListener( v -> {
//处理代码
});
按钮编程示例:
1 2 3 4 5 6 7 8 9 10 11 12
| protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn1 = (Button) findViewById( R.id.btn_test ); btn1.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { TextView tv = (TextView) findViewById( R.id.msg ); tv.setText("武汉科技大学"); } }); }
|

事件监听示例:用内部类实现(可复用)
◼ 以按钮编程为例:
1 2 3 4 5 6 7 8 9 10
| class BtnClickListener implements View.OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.按钮id1: … case R.id.按钮id2: … } } }
|
1 2 3 4 5 6
| Button btn1 = (Button) findViewById(R.id.按钮id1); btn1.setOnClickListener( new BtnClickListener() );
Button btn2 = (Button) findViewById(R.id.按钮id2); btn2.setOnClickListener( new BtnClickListener() );
|
按钮编程示例:
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 MainActivity extends AppCompatActivity { class BtnClickListener implements View.OnClickListener { @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_test: TextView tv = (TextView) findViewById(R.id.msg); tv.setText("武汉科技大学"); break; case R.id.btn_exit: finish(); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn1 = (Button) findViewById(R.id.btn_test); btn1.setOnClickListener( new BtnClickListener() ); Button btn2 = (Button) findViewById(R.id.btn_exit); btn2.setOnClickListener( new BtnClickListener() ); } }
|
Toast消息提示框
◼ Toast:是一种简易的消息提示框,用于在应用程序上浮动显示少量信息,以告知用户任务状态或操作结果,例如:发送成功,加载中,删除成功等。
◼ 主要特点:
◼ Toast 会显示在屏幕所有层的最上方
◼ Toast 会根据用户设置的显示时间后自动消失
◼ 同一时间只显示一条 Toast
◼ Toast 不会获得焦点(无法被点击),不影响用户输入

Toast创建方法
◼ 链式写法:
1
| Toast.makeText( context, text, duration ).show();
|

Toast编程示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn1 = (Button) findViewById( R.id.btn_test ); btn1.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { TextView tv = (TextView) findViewById( R.id.msg ); tv.setText("武汉科技大学"); Toast.makeText( MainActivity.this, "设置完成", Toast.LENGTH_SHORT ).show(); } }); }
|
Snackbar对话框
◼ Snackbar:能为用户提供快速弹出消息,且具有交互性
◼ 主要特点:
◼ 与 Toast 类似,都是为了给用户显示消息
◼ Snackbar是在屏幕底部显示消息(所有其他元素的上方)
◼ 可以与用户交互操作
◼ 同一时间只显示一条 Snackbar
Snackbar用法1:无交互情况
1
| Snackbar.make( view, text, duration ).show();
|
Snackbar用法2:有交互情况
1 2 3 4 5 6 7 8
| Snackbar.make( view, text, duration ) .setAction("按钮文字", new View.OnClickListener() { @Override public void onClick(View v) { } }) .show();
|

Snackbar用法示例
1 2 3 4 5 6 7 8 9 10
| Snackbar.make( findViewById( R.id.contraintLayout ), "确定退出吗?", Snackbar.LENGTH_SHORT) .setAction("确定", new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }) .setActionTextColor(Color.RED) .setBackgroundTint(Color.BLUE) .show();
|
R.id.contraintLayout:给布局设置的 id 值

AlertDialog对话框
◼ AlertDialog:可以在当前的界面上显示一个对话框
◼ 这个对话框是 置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力
◼ AlertDialog 一般是用于提示一些重要的内容或者警告信息

AlertDialog创建过程
- 创建构造器
AlertDialog.Builder
对象
- 构造器对象调用
setTitle
、setMessage
、setIcon
等方法构造对话框的标题、信息和图标等内容
- 根据需要调用
setPositive
、setNegative
方法设置确定、取消按钮
- 调用构造器对象的
show
方法显示对话框
AlertDialog编程框架:链式写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| new AlertDialog.Builder( 当前Activity.this ) .setIcon( R.drawable.图片资源id ) .setTitle("提示") .setMessage("确定退出吗?") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .setNegativeButton("取消", null) .show();
|
◼ setPositiveButton 方法:设置”确定”按钮功能
◼ setNegativeButton 方法:设置”取消”按钮功能

图片资源的导入方法

AlertDialog编程示例
下面的代码放在 onCreate 方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Button btn2 = (Button) findViewById( R.id.btn_exit ); btn2.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { new AlertDialog.Builder( MainActivity.this ) .setIcon( R.drawable.alert ) .setTitle("提示") .setMessage("确定退出吗?") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("取消", null) .setCancelable(false) .show(); } });
|

高级AlertDialog

(1) 带列表项的对话框:setItems 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Button btn3 = (Button) findViewById(R.id.list_0); btn3.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { final String[] items = new String[]{"选项1", "选项2", "选项3","选项4"}; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setIcon(android.R.drawable.ic_dialog_info); builder.setTitle("请选择选项:"); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "你选择了" + items[which], Toast.LENGTH_SHORT).show(); } }); builder.show(); } });
|

(2) 带单选列表项的对话框:setSingleChoiceItems 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Button btn3 = (Button) findViewById(R.id.list_0); btn3.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { final String[] items = new String[]{"Java", "C", "C++", "Python", "C#"}; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setIcon(android.R.drawable.ic_dialog_info); builder.setTitle("选择你最擅长的开发语言:"); builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "你选择了" + items[which], Toast.LENGTH_SHORT).show(); } }); builder.setPositiveButton("确定", null); builder.show(); } });
|

(3) 带多选列表项的对话框:setMultiChoiceItems 方法
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
| Button btn3 = (Button) findViewById(R.id.list_0); btn3.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { boolean[] checkedItems = new boolean[5]; final String[] items = new String[]{"Java", "C", "C++", "Python", "C#"}; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setIcon(android.R.drawable.ic_dialog_info); builder.setTitle("选择你喜欢的开发语言:"); builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { checkedItems[which] = isChecked; } }); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String result = ""; for (int i = 0; i < checkedItems.length; i++) { if (checkedItems[i]) { result += items[i] + ","; } } if (!"".equals(result)) { result = result.substring(0, result.length() - 1); Toast.makeText(MainActivity.this, "您选择了" + result , Toast.LENGTH_LONG).show(); } } }); builder.show(); } });
|

(4) 自定义布局的对话框:setView 方法

自定义的布局:login.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"> <TextView android:id="@+id/tip2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="30dp" android:layout_marginTop="30dp" android:text="密 码" android:textSize="20sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tip1" /> <TextView android:id="@+id/tip1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="30dp" android:layout_marginTop="30dp" android:text="用户名" android:textSize="20sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:ems="10" android:inputType="textPersonName" app:layout_constraintBaseline_toBaselineOf="@+id/tip1" app:layout_constraintStart_toEndOf="@+id/tip1" /> <EditText android:id="@+id/password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:ems="10" android:inputType="textPassword" app:layout_constraintBaseline_toBaselineOf="@+id/tip2" app:layout_constraintStart_toEndOf="@+id/tip2" /> </androidx.constraintlayout.widget.ConstraintLayout>
|
在 MainActivity.java 中添加以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setIcon(android.R.drawable.ic_dialog_info); builder.setTitle("登录");
LayoutInflater factory = LayoutInflater.from(MainActivity.this); final View dialogView = factory.inflate( R.layout.login, null ); builder.setView(dialogView); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { EditText un = (EditText) dialogView.findViewById(R.id.username); EditText pwd = (EditText) dialogView.findViewById(R.id.password); Toast.makeText(getApplicationContext(), "姓名 :" + un.getText().toString()+ "\n密码:"+ pwd.getText().toString(),Toast.LENGTH_SHORT).show(); } }); builder.show();
|
程序登录验证页面
进入后显示刚才的输入
基于回调的事件监听机制
回调是一种双向的调用模式
◼ 回调(方法回调):是将功能定义与功能分开的一种手段,是一种解耦合的设计思想
◼ Java回调是通过接口来实现的,系统通过在不同的状态下”回调”实现类,从而达到接口和实现的分离
◼ 如何理解回调:
◼ 比如:你放学回家,问老妈饭做好没,你妈说还没有;然后你跟她说:老妈,我先在外面玩一下,饭好了就叫我哈!
◼ 分析:你和老妈约定了一个接口,你通过这个接口叫老妈做饭,当饭做好时,你老妈又通过这个接口来反馈你:”饭做好了” (回调方法)

回调事件监听示例:以Activity的onKeyDown监听为例
◼ onKeyDown 方法:在 Activity ==强调== 中监听手机屏幕上的按键
◼ 比如:监听返回键,提示退出
onKeyDown 监听基本框架:
1 2 3 4 5 6 7 8 9 10
| @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == event.某个键盘码 ) { } return super.onKeyDown(keyCode, event); }
|
关于返回值:
◼ 当返回true时,表示已经完整地处理了这个事件,并不希望其他的回调方法再次进行处理
◼ 当返回false时,表示并没有完全处理完该事件,更希望其他回调方法继续对其进行处理
onKeyDown示例1:监听返回键(支持手势),AlertDialog提示退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == event.KEYCODE_BACK ) { new AlertDialog.Builder(MainActivity.this) .setIcon(R.drawable.alert) .setTitle("提示") .setMessage("确定退出吗?") .setPositiveButton("确定",new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("取消", null) .show(); } return true; }
|
onKeyDown示例2:2秒之内按再次按返回键退出,否则进行提示
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private long clickTime = 0L; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == event.KEYCODE_BACK ) { if (System.currentTimeMillis() - clickTime > 2000) { Toast.makeText(this, "再按一次退出!", Toast.LENGTH_SHORT).show(); clickTime = System.currentTimeMillis(); } else { finish(); } return true; } return super.onKeyDown(keyCode, event); }
|
代码重新封装
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
| public class MainActivity extends AppCompatActivity { private Button btn1; private Button btn2; private TextView tv; private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { switch ( view.getId() ) { case R.id.btn_test: test(); break; case R.id.btn_exit: alert(); break; } } }; public void init() { btn1 = (Button) findViewById(R.id.btn_test); btn2 = (Button) findViewById(R.id.btn_exit); tv = (TextView) findViewById(R.id.msg); btn1.setOnClickListener( listener ); btn2.setOnClickListener( listener ); } public void test() { tv.setText("武汉科技大学"); Toast.makeText(MainActivity.this, "设置完成", Toast.LENGTH_SHORT).show(); } public void alert() { new AlertDialog.Builder(MainActivity.this) .setIcon(R.drawable.alert) .setTitle("提示") .setMessage("确定退出吗?") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("取消", null) .show(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == event.KEYCODE_BACK) { alert(); } return true; } } }
|