基于教材 .docx 对比现有复习提纲,补齐缺口内容。
§1 res 目录结构详解
| 子目录 | 用途 | 注意 |
|---|---|---|
| drawable | 存放图片资源(png/jpg/xml shape) | 无分辨率分级 |
| mipmap | 存放应用启动图标(ic_launcher) | 系统会根据设备密度自动选取不同分辨率的图标,不要把普通图片放这里 |
| layout | 存放布局 XML 文件 | Activity / Fragment / 自定义 View / 列表项布局 |
| values | 存放引用值:strings.xml(字符串)、colors.xml(颜色)、dimens.xml(尺寸)、styles.xml(样式) | 支持多语言 values-en/values-zh |
mipmap vs drawable 核心区别:mipmap 保留在不同密度下缩放时的清晰度,适合 Launcher Icon;drawable 中的资源在某些密度下可能被优化丢弃。
§2 Intent 六大属性 + Bundle
2.1 Intent 六大属性
| 属性 | 说明 |
|---|---|
| Component | 目标组件名,显式 Intent 的核心。setComponent() 或构造器传入 Class |
| Action | 要执行的动作,如 ACTION_VIEW、ACTION_DIAL、ACTION_CALL |
| Data | 数据 URI,如 Uri.parse("tel:10086")、Uri.parse("https://...") |
| Type | MIME 类型,如 "image/*"、"video/*"。可与 Data 配合 setDataAndType() |
| Category | 附加类别,如 CATEGORY_DEFAULT、CATEGORY_LAUNCHER、CATEGORY_BROWSABLE |
| Extras | 附加数据,通过 putExtra() 写入,getXxxExtra() 读取 |
Intent 匹配规则:隐式 Intent 的 action 必须匹配,category 必须全部包含在目标组件的 intent-filter 中,data/type 只匹配声明了的部分。
2.2 Bundle 类
Bundle 是键值对容器,用于在组件间传递数据:
java
// 发送方
Intent intent = new Intent(this, TargetActivity.class);
Bundle bundle = new Bundle();
bundle.putString("username", "张三");
bundle.putInt("age", 20);
bundle.putBoolean("isVip", true);
intent.putExtras(bundle); // 或直接 intent.putExtra("bundle", bundle)
startActivity(intent);
// 接收方
Bundle bundle = getIntent().getExtras();
String username = bundle.getString("username", ""); // 第二个参数是默认值
int age = bundle.getInt("age", 0);
Bundle 可存储类型:String/int/boolean/float/double/long/String\[\]/ArrayList/Parcelable/Serializable。
§3 百分比布局
需添加依赖
implementation 'androidx.percentlayout:percentlayout:1.0.0'(AndroidX)或com.android.support:percent(旧版支持库)。
3.1 PercentRelativeLayout
按父容器宽高的百分比定位,继承自 RelativeLayout:
xml
<androidx.percentlayout.widget.PercentRelativeLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="50%宽 10%高"
app:layout_widthPercent="50%"
app:layout_heightPercent="10%" />
</androidx.percentlayout.widget.PercentRelativeLayout>
3.2 PercentFrameLayout
按百分比叠加布局,继承自 FrameLayout,用法同上。
3.3 百分比属性速查
| 属性 | 含义 |
|---|---|
layout_widthPercent |
宽度占父宽百分比 |
layout_heightPercent |
高度占父高百分比 |
layout_marginPercent |
四边外边距统一百分比 |
layout_marginLeftPercent |
左侧外边距百分比 |
layout_marginTopPercent |
顶部外边距百分比 |
layout_marginRightPercent |
右侧外边距百分比 |
layout_marginBottomPercent |
底部外边距百分比 |
layout_marginStartPercent |
起始侧外边距(适配 RTL 布局) |
layout_marginEndPercent |
结束侧外边距 |
§4 FrameLayout 前景属性
FrameLayout 专有属性,其他布局没有:
| 属性 | 含义 |
|---|---|
android:foreground |
设置前景图像,覆盖在所有子控件之上,如 @drawable/border |
android:foregroundGravity |
前景图像的对齐方式,取值与 gravity 相同(center/fill/top 等) |
xml
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:foreground="@drawable/frame_overlay"
android:foregroundGravity="center">
<ImageView ... />
</FrameLayout>
§5 控件补充
5.1 ImageButton(图片按钮)
与 Button 区别:ImageButton 没有 android:text 属性,用 android:src 设置图片。
xml
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_play"
android:background="@null" <!-- 去除默认背景 -->
android:contentDescription="播放" /> <!-- 无障碍描述 -->
5.2 ToggleButton(开关按钮)
带文本的开关状态按钮,继承自 Button:
xml
<ToggleButton
android:textOn="开"
android:textOff="关"
android:checked="false" />
java
ToggleButton tb = findViewById(R.id.toggle);
boolean isOn = tb.isChecked(); // 获取状态
tb.setOnCheckedChangeListener((buttonView, isChecked) -> {
// isChecked 为 true 时表示"开"
});
5.3 EditText 属性补充
| 属性 | 含义 | |
|---|---|---|
android:maxLength |
最大输入字符数,如 "10" |
|
android:singleLine |
单行模式(已废弃,用 android:maxLines="1" 替代) |
|
android:password |
密码模式(已废弃,用 android:inputType="textPassword" 替代) |
|
android:numeric |
限制数字输入(已废弃,用 android:inputType="number" 替代) |
|
android:inputType |
输入类型:textPassword/number/phone/date/textEmailAddress 等,可组合 `text |
textMultiLine` |
5.4 TabHost(选项卡)
通过多个 Tab 标签切换不同内容面板:
java
TabHost tabHost = findViewById(android.R.id.tabhost);
tabHost.setup();
// 添加 Tab1
TabHost.TabSpec spec1 = tabHost.newTabSpec("tab1");
spec1.setIndicator("标签1"); // 标签文字
spec1.setContent(R.id.tab1_content); // 内容面板 id
tabHost.addTab(spec1);
// 添加 Tab2
tabHost.addTab(tabHost.newTabSpec("tab2")
.setIndicator("标签2")
.setContent(R.id.tab2_content));
注意 :TabHost 的 id 必须是
@android:id/tabhost,内容面板放在FrameLayout中。
5.5 ScrollView(滚动视图)
可包裹一个直接子控件(通常是 LinearLayout),当内容超出屏幕时提供垂直滚动:
xml
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"> <!-- 内容不足时仍填满 -->
<LinearLayout ...>
<!-- 多个控件 -->
</LinearLayout>
</ScrollView>
注意:
- ScrollView 只能有一个直接子控件
- 水平滚动用 HorizontalScrollView
- 大型列表不用 ScrollView 套 ListView,用 RecyclerView 更好
5.6 RecyclerView(循环器视图)
ListView 的升级替代品,强制使用 ViewHolder 模式,性能更好:
java
// 1. 定义适配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.VH> {
static class VH extends RecyclerView.ViewHolder {
TextView textView;
VH(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
}
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new VH(v);
}
@Override
public void onBindViewHolder(VH holder, int position) {
holder.textView.setText(data.get(position));
}
@Override
public int getItemCount() { return data.size(); }
}
// 2. 使用
RecyclerView rv = findViewById(R.id.recycler);
rv.setLayoutManager(new LinearLayoutManager(this)); // 必须设置
rv.setAdapter(new MyAdapter());
核心特点:
- 必须设置 LayoutManager:LinearLayoutManager(线性)、GridLayoutManager(网格)、StaggeredGridLayoutManager(瀑布流)
- ViewHolder 强制:ListView 可选,RecyclerView 必须
- 不内置 item 点击监听:需在 onBindViewHolder 中手动 setOnClickListener
§6 子菜单 SubMenu
子菜单是在选项菜单基础上弹出的二级菜单:
java
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// 一级菜单项
menu.add(0, 1, 0, "文件");
// 二级子菜单
SubMenu subMenu = menu.addSubMenu(0, 2, 0, "更多");
subMenu.add(0, 21, 0, "设置");
subMenu.add(0, 22, 0, "关于");
subMenu.add(0, 23, 0, "帮助");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 1: // 文件
case 21: // 设置
case 22: // 关于
}
return true;
}
三种菜单对比:
| 类型 | 触发方式 | 创建方法 | 绑定对象 |
|---|---|---|---|
| OptionsMenu | 按 Menu 键/三点 | onCreateOptionsMenu() |
Activity |
| ContextMenu | 长按 View | onCreateContextMenu() |
某个 View(需 registerForContextMenu) |
| SubMenu | 点击菜单项展开 | menu.addSubMenu() |
某菜单项下 |
§7 Toast 完整方法参考
java
// === 基本用法 ===
Toast.makeText(context, "提示", Toast.LENGTH_SHORT).show();
// === 自定义位置 ===
Toast toast = Toast.makeText(this, "居中", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0); // gravity, xOffset, yOffset
toast.show();
// === 自定义布局 ===
LayoutInflater inflater = getLayoutInflater();
View customView = inflater.inflate(R.layout.custom_toast, null);
Toast toast = new Toast(this);
toast.setView(customView); // 设置自定义 View
toast.setDuration(Toast.LENGTH_LONG);
toast.show();
// === 其他方法 ===
toast.cancel(); // 强制取消显示
View v = toast.getView(); // 获取内部 View
int x = toast.getXOffset(); // 获取 X 偏移
int y = toast.getYOffset(); // 获取 Y 偏移
toast.setText("新文字"); // 运行时修改文字
§8 线程创建两种方式
8.1 继承 Thread 类
java
class MyThread extends Thread {
@Override
public void run() {
// 耗时操作放这里
String result = fetchData();
// 通过 Handler 更新 UI
}
}
// 启动
MyThread thread = new MyThread();
thread.start(); // 调用 start(),不是 run()!
8.2 实现 Runnable 接口(推荐)
java
class MyRunnable implements Runnable {
@Override
public void run() {
// 耗时操作
}
}
// 启动
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
// 或匿名
new Thread(new Runnable() {
@Override
public void run() {
// ...
}
}).start();
// Lambda(Java 8+)
new Thread(() -> {
String data = fetchData();
}).start();
两种方式对比:
| 对比项 | 继承 Thread | 实现 Runnable |
|---|---|---|
| 资源共享 | 不共享 | 可共享同一个 Runnable 实例 |
| 扩展性 | 不能再继承其他类 | 还可以继承其他类 |
| 耦合度 | 高,线程和任务绑定 | 低,线程和任务分离 |
| 推荐度 | 少用 | 推荐 |
关键点:
- 必须调用
start()才能启动新线程,直接调run()只是普通方法调用,不创建新线程 - 子线程不能直接更新 UI,必须通过
Handler/runOnUiThread()/View.post()回到主线程