# Android 笔记

# 安装 Android Studio

# 下载并安装

Android Studio (opens new window)

# 汉化补丁

JetBrains 系列软件汉化包 (opens new window)

# 实用插件

  • Android ButterKnife Zelezny(快速 findViewById)
  • Android WiFi ADB(通过 WIFI 用手机调试)

# 修复没有运行按钮

安装 NDK (opens new window)

解压后将解压目录增加至环境变量即完成安装

# 一键连接 MUMU 模拟器

@echo off
set adbPath=C:\Program Files (x86)\MuMu\emulator\nemu\vmonitor\bin
cd /d %adbPath%
taskkill /f /im adb_server.exe
taskkill /f /im adb.exe
"%adbPath%\adb_server.exe" connect 127.0.0.1:7555
goto :eof

# 生命周期

  1. onCreate() ,不可见状态

在 Activity 被创建时回调,第一个生命周期。我们一般在创建 Activity 时需要重写该方法做一些初始化的操作,如通过 setContentView 设置界面布局的资源,初始化所需要的组件信息等。

  1. onStart() ,可见状态

该方法回调表示 Activity 正在启动,此时 Activity 处于可见状态,只是还没有在前台显示,因此用户也无法交互。可以简单理解为 Activity 已显示却无法被用户看见。

  1. onResume() ,可见状态

Activity 已在在屏幕上显示 UI 并允许用户操作了。当 Activity 停止后(onPause、onStop 方法被调用),重新回到前台时也会调用 onResume 方法。可以在 onResume 方法中初始化一些资源,比如打开相机或开启动画。

  1. onPause() ,可见状态

Activity 正在停止(Paused 形态),通常接下来 onStop() 会被回调 。但通过流程图可见,另一种情况是 onPause() 执行后直接执行了 onResume 方法,这可能是用户点击 Home 键,让程序退回到主界面,程序在后台运行时又迅速地再回到到当前的 Activity,此时 onResume 方法就会被回调。我们可以在 onPause 方法中做一些数据存储、动画停止、资源回收等操作。另外,onPause 方法执行完成后,新 Activity 的 onResume 方法才会被执行。所以 onPause 不能太耗时,因为这可能会影响到新的 Activity 的显示。

  1. onStop() ,不可见状态

Activity 即将停止或者完全被覆盖(Stopped 形态),此时 Activity 不可见,仅在后台运行。同样地,在 onStop 方法可以做一些资源释放的操作,不能太耗时。

  1. onRestart(),可见状态

表示 Activity 正在重新启动,由不可见状态变为可见状态。这种情况,一般发生在用户打开了一个新的 Activity 时,之前的 Activity 就会被 onStop,接着又回到之前 Activity 页面时,之前的 Activity 的 onRestart 方法就会被回调。

  1. onDestroy() ,不可见状态

表示 Activity 正在被销毁,也是生命周期最后一个执行的方法,一般我们可以在此方法中做一些回收工作和最终的资源释放。

# 基础命令

# 打印输出

Log.i("打印内容")

# 退出程序

System.exit(0);

# 连续返回两次则退出程序

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (keyCode == KeyEvent.KEYCODE_BACK){
    if ((System.currentTimeMillis() - exitTime) >2000){
      Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
      exitTime = System.currentTimeMillis();
    }else {
        System.exit(0);
      }
      return true;
  }
  return super.onKeyDown(keyCode, event);
}

# 振动

//获取振动器
Vibrator vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
//振动,单位为毫秒
vibrator.vibrate(500);

增加权限: `<uses-permission android:name="android.permission. VIBRATE" />`

# 获取电量

//获取电量信息管理器
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
//输出当前电量(百分比)
Log.i("当前电量:" + batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))

# 直接拨打电话

//判断是否已申请权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},1);
}else {
    intent.setAction(intent.ACTION_CALL);
    intent.setData(Uri.parse("tel:电话号码"));
    startActivity(intent);
}

# 页面跳转

# 常规跳转

Intent intent = new Intent(this,跳转Activity.class);
startActivity(intent);

返回/结束页面:

finish();
当前Activity.this.finish();

提示

可使用 `intent.setFlags(intent. FLAG_ACTIVITY_NO_HISTORY);` 设置返回到桌面后再次访问时不保留此页面

# 页面间传递数据

传递:

Bundle bundle = new Bundle();
bundle.putCharSequence("键1","值1");
bundle.putCharSequence("键2","值2");

Intent intent = new Intent(this,跳转Activity.class);
intent.putExtras(bundle);
startActivity(intent);

获取:

Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String1 = bundle.getString("键1");
String2 = bundle.getString("键2");

# 获取页面的返回值

Intent intent = new Intent(this,跳转Activity.class);
startActivityForResult(intent,0x11);

@Override
protected void onActivityResult (int requestCode,int resultCode,Intent data){
  super.onActivityResult(requestCode,resultCode, data);
  if (requestCode == 0x11 && resultCode == 0x11) {
    Bundle bundle = data.getExtras ();
    int= bundle.getInt("键");
  }
}

跳转的页面:

Intent intent = getIntent();
Bundle bundle = intent.getExtras();
bundle.putInt("键","值");
intent.putExtras(bundle);
setResult(0x11,intent);
finish();

# 跳转至拨打电话界面

Intent intent = new Intent();
intent.setAction(intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:电话号码"));
startActivity(intent);

增加权限: `<uses-permission android:name="android.permission. CALL_PHONE"/>`

# 跳转至发送短信界面

Intent intent = new Intent();
intent.setAction(intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:短信号码"));
intent.putExtra("sms_body","短信内容");
startActivity(intent);

增加权限: `<uses-permission android:name="android.permission. SEND_SMS"/>`

# 跳转至桌面(不退出)

Intent intent = new Intent();
intent.setAction(intent.ACTION_MAIN);
intent.addCategory(intent.CATEGORY_HOME);
startActivity(intent);

# 跳转到网页(以默认浏览器打开)

Intent intent = new Intent();
intent.setAction(intent.ACTION_VIEW);
intent.setData(Uri.parse("网页地址"));
startActivity(intent);

# 显示 Toast 提示

Toast.makeText(MainActivity.this,"提示内容",Toast.LENGTH_SHORT).show();

# 对话框

# 取消/确定 对话框

//创建AlertDialog对象
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
//设置图标
//alertDialog.setIcon(R.drawable.ic_launcher_background);
alertDialog.setTitle("标题");
alertDialog.setMessage("内容");
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() {
  @Override
  public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(MainActivity.this, "点击了取消", Toast.LENGTH_SHORT).show();
  }
});
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
  @Override
  public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(MainActivity.this, "点击了确定", Toast.LENGTH_SHORT).show();
  }
});
alertDialog.show();

# 列表对话框

String[] item = new String[]{"列表项1","列表项2"};
//创建AlertDialog.Builder对象
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
//设置图标
//builder.setIcon(R.drawable.ic_launcher_background);
builder.setTitle("标题");
builder.setItems(item, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(MainActivity.this, "选择了:" + item[which], Toast.LENGTH_SHORT).show();
    }
});
builder.create().show();

# 单选 对话框

String[] items = new String[]{"列表项1", "列表项2"};
//创建AlertDialog.Builder对象
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
//设置图标
//builder.setIcon(R.drawable.ic_launcher_background);
builder.setSingleChoiceItems(items, 0, 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.create().show();

# 多选 对话框

final String[] item1 = new String[]{"列表项1", "列表项2"};
//记录各列表的状态
final boolean[] checkItem=new boolean[item1.length];
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
//设置图标
//builder.setIcon(R.drawable.ic_launcher_background);
builder.setTitle("标题");
builder.setMultiChoiceItems(item1, checkItem, new DialogInterface.OnMultiChoiceClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
        //改变被操作列表项的状态
        checkItem[which] = isChecked;
    }
});
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        String result = "";
        for (int i = 0; i < checkItem.length; i++) {
            if(checkItem[i]){
              result = result + " " + item1[i];
            }
        }
        if (result.equals("") == false){
            Toast.makeText(MainActivity.this, "选择了:" + result, Toast.LENGTH_SHORT).show();
        }
    }
});
builder.create().show();

# 资源

# 字符串资源

\res\values\strings.xml

<resources>
    <string name="字符串名称">字符串</string>
</resources>

使用:

android:text="@string/字符串名称"
组件.setText(getResources().getString(R.string.字符串名称));

提示

`string` 是小写,不能大写

# 颜色资源

格式: `#透明度 R值 G值 B值` (各值间没有空格,RGB 值为十六进制,透明度可省略)

\res\values\strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="颜色名称">#透明度 R值 G值 B值</color>
</resources>

使用:

android:textColor="@color/颜色名称"
组件.setTextColor(getResources().getColor(R.color.颜色名称));

# 尺寸资源

  • dp: 设置资源大小
  • sp: 设置字体大小

\res\values\dimens.xml (如没有则需要自行建立)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="尺寸名称">尺寸dp</dimen>
</resources>

使用:

android:textSize="@dimen/尺寸名称"
组件.setTextSize(getResources().getDimension(R.dimen.尺寸名称));

# Drawable 资源

Drawable 目录 一般用来存放图片等文件

使用:

android:background="@drawable/资源名称"
组件.setBackground(getResources().getDrawable(R.drawable.资源名称(不带后缀));

# StateListDrawable 资源

StateListDrawable 资源一般用于控制焦点事件

# 主题资源

\res\values\styles.xml

<resources>
  <style name="主题名称" parent="AppTheme">
   <item name="属性"></item>
   <item name="android:属性"></item>
  </style>
</resources>

全局使用:

AndroidMainifest.xml

android:theme="@style/主题名称"

局部使用:

setTheme(R.style.主题名称)
setContentView(R.layout.activity_main);

提示

在 Java 代码中需要在 `setContentView` 方法前使用 `setTheme` ,否则不起作用

# 菜单资源

\res\menu\menu.xml (如没有则需要自行建立)

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/message1" android:title="标题"></item>
</menu>

# MediaPlay

# 播放音乐

try {
  mMediaPlayer = MediaPlayer.create(this, R.drawable.音乐资源名称);
  mMediaPlayer.start();
  } catch (Exception e) {}

# 消息推送

//创建通知基本内容
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_launcher_background)
        .setContentTitle("通知标题")
        //单行
        .setContentText("通知内容")
        //多行
        //.setStyle(new NotificationCompat.BigTextStyle().bigText("通知内容" +"通知内容" +"通知内容"))
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setAutoCancel(true);
//Android 8.0后需要设置Channel
createNotificationChannel();
//创建点击事件,启动详细页面
Intent intent1 = new Intent(this,MessageActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivities(this,0, new Intent[]{intent1},0);
builder.setContentIntent(pendingIntent);
//展示通知
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
// notificationId is a unique int for each notification that you must define
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
private void createNotificationChannel() {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    CharSequence name = getString(R.string.app_name);
    String description = getString(R.string.app_name);
    int importance = NotificationManager.IMPORTANCE_HIGH;
    NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
    channel.setDescription(description);
    NotificationManager notificationManager = getSystemService(NotificationManager.class);
    notificationManager.createNotificationChannel(channel);
  }
}

# 动态权限

//判断是否已申请权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.权限名称) != PackageManager.PERMISSION_GRANTED){
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.权限名称},1);
}

# 定位

# 事件监听

# 触摸监听

# 基于监听器(优先级较高)

组件.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
  //X: event.getX()
  //Y: event.getY()
  return false;
  }
});

# 基于回调

@Override
public boolean onTouchEvent(MotionEvent event) {
  Toast.makeText(this, "onTouchEvent方法", Toast.LENGTH_SHORT).show();
  return super.onTouchEvent(event);
}

# 物理按键监听

监听物理 音量键、电源键、菜单键、主屏键、返回键

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (keyCode == KeyEvent.按键码){

  }
  return super.onKeyDown(keyCode, event);
}

提示

按键码:

  • 菜单键: `KEYCODE_MENU`
  • 主屏键: `KEYCODE_HOME`
  • 返回键: `KEYCODE_BACK`
  • 音量加: `KEYCODE_VOLUME_UP`
  • 音量减: `KEYCODE_VOLUME_DOWN`
  • 电源键: `KEYCODE_POWER`

# 长按事件

组件.setOnLongClickListener(new View.OnLongClickListener() {
  @Override
  public boolean onLongClick(View v) {
    Toast.makeText(MainActivity.this, "长按事件", Toast.LENGTH_SHORT).show();
    return false;
  }
});

# 持久化技术

# 文件存储

所有的文件默认会存放于 /data/data/包名/files 目录下

# 写入文件

try {
  val output = openFileOutput("文件名", Context.MODE_PRIVATE)
  val writer = BufferedWriter(OutputStreamWriter(output))
  writer.use {
      it.write("文件内容")
  }
} catch (e: IOException) {
  e.printStackTrace()
}

# 读入文件

val content = StringBuilder()
try {
  val input = openFileInput("文件名")
  val reader = BufferedReader(InputStreamReader(input))
  reader.use {
    reader.forEachLine {
      content.append(it)
    }
  }
} catch (e: IOException) {
  e.printStackTrace()
}
return content.toString()

# SharedPreferences

SharedPreferences 使用键值对的方式存储数据

# 存储数据

SharedPreferences.Editor editer = getSharedPreferences("文件名(无后缀)", Context.MODE_PRIVATE).edit();
//字符串型
editer.putString("字段名", "值");
//整数型
editer.putInt("字段名",0);
// 布尔值
editer.putBoolean("字段名",true);
editer.apply();

# 读取数据

SharedPreferences preferences = getSharedPreferences("文件名(无后缀)", Context.MODE_PRIVATE);
Log.i(preferences.getString("字段名"),默认值);
Log.i(preferences.getInt("字段名"),默认值);
Log.i(preferences.getBoolean("字段名",默认值));

# 多线程

# 基本多线程

此多线程无法更新 UI 界面,只能处理逻辑代码

Thread {
  //延时(毫秒)
  Thread.sleep(100)
}.start()

# 使用 AsyncTask

AsyncTask 原理是基于异步消息处理机制,Android 里做好了很好的封装

//启动线程
类名().execute()

class 类名 : AsyncTask<Unit, Int, Boolean>(){
    override fun doInBackground(vararg params: Unit?): Boolean {
        //进行耗时操作(无法更新界面)

        //调用onProgressUpdate方法更新界面(可传参)
        publishProgress()

        //延时(毫秒)
        Thread.sleep(100)

        //返回值将会传到收尾操作
        return true
    }
    override fun onProgressUpdate(vararg values: Int?) {
        //进行更新UI操作,由doInBackground方法主动调用
    }
    override fun onPostExecute(result: Boolean?) {
        //进行收尾操作,result参数为耗时操作返回值
    }
}

# 使用 Handler (不推荐,麻烦)

val uptateText = 1
val handler = object : Handler() {
  override fun handleMessage(msg: Message) {
    when (msg.what) {
      //此处可更新UI
      uptateText -> textview_text.text = "123"
    }
  }
}

button.setOnClickListener {
    Thread {
      val message = Message()
      message.what = uptateText
      handler.sendMessage(message)
    }.start()
}
最后更新: 2022/9/12 16:01:28