少女祈祷中...

安卓开发基础的学习笔记

  1. Tocst的基础使用

    1
    Toast.makeText(MainActivity.this,text_show.toString(),Toast.LENGTH_SHORT).show();
  2. 如果您的应用以 Android 12(API 级别 31)或更高版本为目标平台,则消息框上限为两行文本,并且必须在文本旁边显示应用图标。请注意,此文本的行长因屏幕尺寸而异,因此最好尽可能缩短文本长度。
    所以只能用其他消息提示控件代替,如:AlretDialog

    1
    2
    3
    4
    5
    6
    7
    8
    //  定义AlertDialog以完全显示
    AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
    builder.setMessage(text_show);
    builder.setCancelable(true);
    AlertDialog dialog=builder.create();
    // 底部显示
    dialog.getWindow().setGravity(Gravity.BOTTOM);
    dialog.show();

    或者这样定义:

    AlertDialog(对话框)详解_菜鸟教程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    builder = new AlertDialog.Builder(mContext);
    alert = builder.setIcon(R.mipmap.ic_icon_fish)
    .setTitle("系统提示:")
    .setMessage("这是一个最普通的AlertDialog,\n带有三个按钮,分别是取消,中立和确定")
    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(mContext, "你点击了取消按钮~", Toast.LENGTH_SHORT).show();
    }
    })
    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(mContext, "你点击了确定按钮~", Toast.LENGTH_SHORT).show();
    }
    })
    .setNeutralButton("中立", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(mContext, "你点击了中立按钮~", Toast.LENGTH_SHORT).show();
    }
    }).create(); //创建AlertDialog对象
    alert.show();

  3. 控件绑定

    1
    2
    3
    4
    5
    Button btn = findViewById(R.id.btn_id);

    //或者先定义好对象类型再进行控件绑定
    private Button btn;
    btn = findViewById(R.id.btn_id);
  4. 监听器的绑定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    switch_s_hidden.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    if (switch_s_hidden.isChecked()){
    rg_s.setVisibility(View.INVISIBLE);
    switch_flag=false;
    }
    else{
    rg_s.setVisibility(View.VISIBLE);
    switch_flag=true;
    }
    }
    });

    //或者定义一个单独的类(在onCreate()之外),继承对应的监听器,再实例化调用
    class chk_lis implements CompoundButton.OnCheckedChangeListener{
    @Override
    public void onCheckedChanged(CompoundButton cButton,boolean b){
    @Todo
    }
    }
    chk_lis chk_lis1=new chk_lis();
    chk_l.setOnCheckedChangeListener(chk_lis1);
  5. 屏幕像素密度:
    需要注意的是,要适配不同密度的手机屏幕,dp还远远不够。

    • px:pixel缩写,意为像素。
    • dp(推荐使用):density-independent pixel缩写,有时被缩写为dip,意为密度无关像素。
    • sp(推荐对字体使用):scale-independent pixel缩写,意为缩放无关像素。
    • drawable、drawable-ldpi、mdpi、hdpi、xhdpi对应默认密度、中等密度、高密度、超高密度等。引用时只需要引用id即可,几个文件夹存入的图片同名且只能是小写不能包含空格。文件夹只是给人来分辨和方便存储,实际调用的时候还是根据图片的密度来调用。因此你甚至能把它们全放进同一个文件夹里,如果可以的话。
  6. ListView组件中含有CheckBox控件时可能会出现一些问题。比如刷动后已选中的选项会贝清除或未被选中的选项贝选中。可以在CheckBox的OnCheckChange监听器中添加一个保存状态的语句,并在后面再重新赋值一次CheckBox的状态。

    Android ListView CheckBox状态错乱_博客园

    • ListView动态计算高度&数据更新:
      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
      //	  定义为函数方便调用
      public void listAdapterSet(){
      try {
      List<Map<String, Object>> mapList = noteUpdate();
      if (mapList==null){
      int hhh=1/0;
      }
      // 重新设置adapter以刷新数据
      baseAdapter = new MyAdapter(MainActivity.this, mapList);
      listViewNote.setAdapter(baseAdapter);

      // 动态设置listView高度
      ViewGroup.LayoutParams layoutParams=listViewNote.getLayoutParams();
      int hS=0;
      int itemCount=baseAdapter.getCount();
      int qq=0;
      while (qq<itemCount){
      View view=baseAdapter.getView(qq,null,listViewNote);
      view.measure(0,0);
      qq++;
      hS+=view.getMeasuredHeight();
      if (qq==itemCount){
      // 似乎少算了一行
      hS+=view.getMeasuredHeight();
      }
      }
      hS=hS+itemCount*listViewNote.getDividerHeight();
      layoutParams.height=hS;
      listViewNote.setLayoutParams(layoutParams);
      } catch (Exception e) {
      Log.d("TAG", "onCreate: nullMapList");
      }
      }
  7. 调试语句:

    1
    Log.d("TAG", "onCheckedChanged: ");
  8. 无法使用switch的原因是模块中的R不是final类型,因此只能用if-else来代替。

  9. TabLayout与ViewPage2的绑定:

    1
    new TabLayoutMediator(tabLayout, viewPager2, true, (tab, position) -> tab.setText(titleList.get(position))).attach();

    另外,ViewPage2的触发器继承FragmentStateAdapter。因为两者绑定了,所以只有ViewPage2需要触发器。

    1
    ViewPageAdapter viewPageAdapter=new ViewPageAdapter(getActivity(),fragmentList);

    fragmentsList<Fragment>类型,需要在里面加入碎片以在ViewPage2中显示。另外fragment可以加入构造方法以实现单个类的多次使用。

  10. BottomNavigationView的使用:
    触发器的设置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
       bottomNavigationSelectedListener navListener = new bottomNavigationSelectedListener();
    bottomNav.setOnItemSelectedListener(navListener);
    // 设置默认选中页面
    bottomNav.setSelectedItemId(bottomNav.getMenu().getItem(0).getItemId());
    class bottomNavigationSelectedListener implements BottomNavigationView.OnItemSelectedListener {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    int id=item.getItemId();
    if (id==R.id.logout){

    } else if (id==R.id.scan) {

    } else if (id==R.id.imagePage) {

    }else {
    return false;
    }
    return true;
    }
    }
  11. ToolBar的使用:
    触发器的定义:

    1
    2
    3
    4
    5
    toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem menuItem) {
    return false;
    }

    使用中需要注意的是,toolbar需要导入的包是androidx.appcompat.widget.Toolbar,而不是android.widget.Toolbar
    布局文件同理,是androidx.appcompat.widget.Toolbar而不是Toobar

  12. 广播Broadcast与接收器Receiver:
    动态注册广播:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    broadcastReceiver = new BroadcastReceiver();
    IntentFilter filter = new IntentFilter();
    filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    filter.addAction("musicPlay");
    filter.addAction("musicPause");
    filter.addAction("broadcast");
    // 动态注册广播接收者
    registerReceiver(broadcastReceiver, filter);

    自定义广播:

    1
    2
    3
    4
    5
    6
    7
    Intent intent=new Intent("musicPlay");
    //传递参数,可要可不要
    Bundle bundle=new Bundle();
    bundle.putString("title",editTextTitle.getText().toString());
    bundle.putString("context",editTextContext.getText().toString());
    intent.putExtras(bundle);
    sendBroadcast(intent);

    接收器:

    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
    public class BroadcastReceiver extends android.content.BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
    Log.d("TAG", "onReceive:dsds ");
    // wifi开关
    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
    Log.d("TAG", "onReceive:WLAN ");
    int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1111);
    switch (wifiState) {
    case WifiManager.WIFI_STATE_DISABLED:
    Toast.makeText(context, "Wifi_Disable", Toast.LENGTH_LONG).show();
    break;
    case WifiManager.WIFI_STATE_ENABLING:
    Toast.makeText(context, "Wifi_Enable", Toast.LENGTH_LONG).show();
    break;
    }
    }
    //音乐播放,关闭音乐为mediaPlayer.stop();
    else if (intent.getAction().equals("musicPlay")){
    if (mediaPlayer!=null){
    mediaPlayer.stop();
    mediaPlayer=null;
    }
    mediaPlayer=MediaPlayer.create(context,R.raw.loop_65);
    mediaPlayer.start();
    }
    }

    注意在MainActivity中重写销毁方法以销毁广播:

    1
    2
    3
    4
    5
    6
    7
    @Override
    protected void onDestroy() {
    super.onDestroy();
    if (broadcastReceiver != null) {
    unregisterReceiver(broadcastReceiver);
    }
    }
  13. 通知:
    构造和相关代码:

    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
    Bundle bundle=intent.getExtras();
    String strTitle=bundle.getString("title");
    String strContext=bundle.getString("context");
    Intent intent1=new Intent(context,MainActivity.class);
    // 需要唯一的intent来进行传参,否则将无法传参
    // 即需要设置setData值方可传参
    intent1.setData(Uri.parse("custom://"+System.currentTimeMillis()));
    Bundle bundle1=new Bundle();
    bundle1.putString("title",strTitle);
    bundle1.putString("context",strContext);
    bundle1.putBoolean("isCast",true);
    intent1.putExtras(bundle1);
    // 设置为0时传入的参数会被覆盖(与这个无关,因为处理的是同样的intent)
    // Android12以上PendingIntent需要强制增加FLAG_IMMUTABLE或FLAG_MUTABLE
    PendingIntent pi = PendingIntent.getActivity(context, 0 ,intent1, PendingIntent.FLAG_IMMUTABLE);
    mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    //创建 NotificationChannel 对象
    NotificationChannel channel = new NotificationChannel("Channel_ID", "chat message",NotificationManager.IMPORTANCE_DEFAULT);
    //创建通知渠道
    mNotificationManager.createNotificationChannel(channel);
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,"Channel_ID")
    .setContentTitle(strTitle)
    .setContentText(strContext)
    .setWhen(System.currentTimeMillis())
    // 设置图标
    .setSmallIcon(R.drawable.ic_launcher_foreground)
    .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher_foreground))
    // 设置通知点击跳转
    .setContentIntent(pi);
    // 点击后消失
    // .setAutoCancel(true);

    //发送通知( id唯一,可用于更新通知时对应旧通知; 通过mBuilder.build()拿到notification对象 )
    mNotificationManager.notify(1,mBuilder.build());

    关闭通知:

    1
    mNotificationManager.cabcel(1);

    参考《Android的Notification研究》博客园 2012-03-07 17:44 荒土

  14. Sqlite的基础使用:

    • 定义相关类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      	public class DBOpenHelper extends SQLiteOpenHelper {
      public static final String SQL_TB_NAME="user";
      public static final String SQL_TB_Note_NAME="note";
      public DBOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
      super(context, name, factory, version);
      }

      @Override
      public void onCreate(SQLiteDatabase sqLiteDatabase) {
      // 此处必须是integer而不能是int,否则会出现无法创建的错误,ntext是以unicode存储的文本,大概4k字
      sqLiteDatabase.execSQL("create table if not exists "+SQL_TB_NAME+"(accountId integer primary key,password varchar)");
      sqLiteDatabase.execSQL("create table if not exists "+SQL_TB_Note_NAME+"(id integer primary key autoincrement,accountId int,title ntext,context ntext)");
      }

      @Override
      public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
      // 刷新数据库,即删除重建
      sqLiteDatabase.execSQL("drop table if not exists "+SQL_TB_NAME);
      sqLiteDatabase.execSQL("drop table if not exists "+SQL_TB_Note_NAME);
      onCreate(sqLiteDatabase);
      }
      }
    • 查询操作:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      	sqLiteOpenHelper = new DBOpenHelper(MainActivity.this, "Note.db", null, 1);
      sqLiteDatabase = sqLiteOpenHelper.getReadableDatabase();
      Cursor result = sqLiteDatabase.rawQuery("select * from note where accountId=" + account, null);
      if (result.getCount() <= 0) {
      int hhh = 1 / 0;
      } else {
      // cursor需要移动到指定行才能使用getString()方法来获取列数据
      // -1开始,0为第一行,但第一行为行名
      result.move(1);
      // 第1列
      pwd = result.getString(0);
      }
    • 更新操作:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      sqLiteDatabase=sqLiteOpenHelper.getWritableDatabase();
      ContentValues values=new ContentValues();
      values.put("accountId",account);
      values.put("title",editText.getText().toString());
      String where="id="+id;
      int i=sqLiteDatabase.update(DBOpenHelper.SQL_TB_Note_NAME,values,where,null);
      if (i>0){
      Log.d("TAG", "onClick:update ");
      Toast.makeText(MainActivity.this, "更新成功" , Toast.LENGTH_SHORT).show();
      }else {
      Log.d("TAG", "onClick:noUpdate ");
      Toast.makeText(MainActivity.this, "更新失败" , Toast.LENGTH_SHORT).show();
      }
    • 删除操作:

      1
      2
      3
      4
      5
      6
      7
      8
      long i=sqLiteDatabase.insert(DBOpenHelper.SQL_TB_Note_NAME,null,values);
      if (i==-1){
      Log.d("TAG", "onClick:NoInsert ");
      Toast.makeText(MainActivity.this, "添加失败" , Toast.LENGTH_SHORT).show();
      }else {
      Log.d("TAG", "onClick:insert");
      Toast.makeText(MainActivity.this, "添加成功" , Toast.LENGTH_SHORT).show();
      }
  15. 共享数据以实现记住密码的功能:

    • 获取共享数据:

      1
      2
      3
      4
      SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
      Boolean isRemember = prefs.getBoolean("rememberMe", false);
      String account = prefs.getString("account", "");
      String password = prefs.getString("password", "");
    • 发送数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	SharedPreferences.Editor editor = prefs.edit();
      if (chkBoxRememberMe.isChecked()) {
      // “记住密码”是否被选中
      editor.putBoolean("rememberMe", true);
      editor.putString("account", account);
      editor.putString("password", password);
      } else {
      editor.clear();
      }
      editor.apply();