Android补充知识点

基于教材 .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_VIEWACTION_DIALACTION_CALL
Data 数据 URI,如 Uri.parse("tel:10086")Uri.parse("https://...")
Type MIME 类型,如 "image/*""video/*"。可与 Data 配合 setDataAndType()
Category 附加类别,如 CATEGORY_DEFAULTCATEGORY_LAUNCHERCATEGORY_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

子菜单是在选项菜单基础上弹出的二级菜单:

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() 回到主线程