安卓学习笔记

控件

控件1.TextView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/tv_one"
        android:text="@string/tv_one"
        android:textColor="@color/teal_700"
        android:textStyle="bold"
        android:textSize="25sp"
        android:background="@color/purple_200"
        android:gravity="center_horizontal"
        android:layout_width="200dp"
        android:="200dp"/>

</LinearLayout>

id:组件id
text:文本内容
gravity:文本对齐方式
layout_width:盒子宽度
layout_height:盒子高度
@string/tv_one定义在res/values/strings.xml中 可以设定字符串 通过name取值
@color/teal_700同理

带阴影的TextView

android:shadowColor:设置阴影颜色,需要和shadowRadius一起使用
android:shadowRadius:设置阴影的模糊程度,设为0.1就变成字体颜色,建议3.0
android:shadowDx:设置阴影在水平方向的偏移
android:shadowDy:设置阴影在竖直方向的偏移
    <TextView
        android:id="@+id/tv_one"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:gravity="center_horizontal"
        android:shadowColor="@color/red"
        android:shadowRadius="3.0"
        android:text="@string/tv_one"
        android:shadowDx="10.0"
        android:shadowDy="10.0"
        android:textColor="@color/teal_700"
        android:textSize="25sp"
        android:textStyle="bold" />

跑马灯效果的TextView

android:singleLine="true" //单行显示文本
android:ellipsize="marquee" //跑马灯显示省略文本
android:marqueeRepeatLimit="marquee_forever" //无限循环
android:focusable="true" //可以获得焦点
android:focusableInTouchMode="true"> //触屏获得焦点

获取焦点方法1.

android:clickable="true"

添加点击获得焦点

获取焦点方法2.

public class MyTextView extends TextView {
    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean isFocused() {
        return true;
    }
}

重写TextView方法,默认一直获取焦点

方法3.

<requestFocus/>

加载就获取焦点

控件2.Button

<Button
    android:text="点击"
    android:textColor="@color/white"
    android:background="@drawable/btn_selector"
    android:backgroundTint="@color/btn_color_selector"
    android:layout_width="200dp"
    android:layout_height="100dp"/>

@drawable可绘制图片

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/ic_baseline_account_balance_24" android:state_pressed="true"/>

    <item android:drawable="@drawable/ic_baseline_accessibility_24"/>

</selector>

在drawable创建btn_selector.xml设置按压一个图片,释放一个图片

selector选择器,可以为颜色,图片等

Button事件处理

public class MainActivity extends AppCompatActivity {

    private static final String TAG="leo";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.btn);

        //点击事件
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e(TAG, "onClick: ");
            }
        });

        //长按事件
        btn.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                Log.e(TAG, "OnLongClick: ");
                return false;
            }
        });

        //触摸事件
        btn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Log.e(TAG, "onTouch: "+motionEvent.getAction());
                return false;
            }
        });
    }
}

当返回值为false时,才会执行其他事件,若为true时,执行完此事件,不再接着执行

也可以自定义事件

<Button
    android:text="点击"
    android:textColor="@color/white"
    android:background="@drawable/btn_selector"
    android:backgroundTint="@color/btn_color_selector"
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:onClick="myOnclick"
    android:id="@+id/btn"
    />
public void myOnclick(View view) {
    Log.e(TAG, "OnLongClick: ");
}

控件3.EditText

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <EditText
        android:id="@+id/et"
        android:hint="请输入用户名"
        android:inputType="phone"
        android:textColorHint="#95a1aa"
        android:layout_width="200dp"
        android:layout_height="100dp"/>

    <EditText
        android:hint="请输入用户名"
        android:inputType="textPassword"
        android:textColorHint="#95a1aa"
        android:drawableLeft="@drawable/ic_baseline_person_24"
        android:drawablePadding="5dp"
        android:background="@color/white"
        android:paddingLeft="5dp"
        android:layout_width="200dp"
        android:layout_height="100dp"/>
    <Button
        android:id="@+id/btn"
        android:text="获取用户名"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
    </LinearLayout>
hint:隐式提醒,输入内容时消失
inputType:输入文本的内容
drawableLeft:输入框的图片
drawablePadding:图片与输入值间距
paddingLeft:图标与左侧间距
 android:orientation="vertical":自适应布局

通过按钮获取输入框内容

public class MainActivity extends AppCompatActivity {

    private EditText et;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.btn);
        et = findViewById(R.id.et);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String text = et.getText().toString();
                Log.e("leo", "输入的内容"+text);
            }
        });
    }
}
/*
设置按钮监听,然后点击获取输入框的文本值
*/

控件4.ImageView

<ImageView
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:src="@drawable/wuxing2"
    android:scaleType="fitCenter"

    />   
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/wuxing2"
    android:maxHeight="200dp"
    android:maxWidth="200dp"
    android:adjustViewBounds="true"
    />
  android:scaleType="fitCenter" :进行图片缩放
    android:maxHeight="200dp"
    android:maxWidth="200dp":设置最大宽高 前提开启adjustViewBounds
android:adjustViewBounds="true":可以调整View的界限

控件5.ProgressBar

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <ProgressBar
        android:id="@+id/pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="leonclick"
        android:text="显示或者隐藏进度条" />

    <ProgressBar
        android:id="@+id/pd2"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:layout_width="300dp"
        android:layout_height="wrap_content"/>
    <Button
        android:onClick="loclick"
        android:text="模拟下载"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <ProgressBar
        android:indeterminate="true"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:layout_width="300dp"
        android:layout_height="wrap_content"/>
</LinearLayout>


 style="?android:attr/progressBarStyleHorizontal":设置成水平进度条
  android:indeterminate="true":将进度条设置成不加载精度的进度条

点击事件

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;
    private ProgressBar progressBar2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = findViewById(R.id.pb);
        progressBar2 = findViewById(R.id.pd2);
    }

    public void leonclick(View view) {
        if(progressBar.getVisibility()==View.GONE){
            progressBar.setVisibility(View.VISIBLE);
        }else{
            progressBar.setVisibility(View.GONE);
        }
    }

    public void loclick(View view) {
        int progress = progressBar2.getProgress();
        progress+=10;
        progressBar2.setProgress(progress);
    }
}
/*
progressBar.getVisibility()==View.GONE:获取进度条的显示效果是否为隐藏
 progressBar2.getProgress()获取进度条的数值

*/

控件6.Notificationn

按钮

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发出通知"
        android:onClick="sendNotification"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="取消通知"
        android:onClick="cacelNotification"
        />

</LinearLayout>
public class MainActivity extends AppCompatActivity {

    private NotificationManager manager;
    private Notification notification;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		//调用Context的getSystemService()方法获取,getSystemService()方法接收一个字符串参数用于确定获取系统那一个服务,这里是Context.NOTIFICATION_SERVICE。
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            //该手机的操作系统版本号大于等于安卓6
            NotificationChannel channel = new NotificationChannel("leo", "测试通知", NotificationManager.IMPORTANCE_HIGH);//id需要和NotificationCompat的id一致  NotificationChannel的用户可见名称 和通知强度
            manager.createNotificationChannel(channel);//创建通知chan链
        }

        Intent intent = new Intent(this, NotificationActivity.class); //通信跳转到NotificationActivity
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        //跳转到一个activity组件
        notification = new NotificationCompat.Builder(this,"leo") 
                .setContentTitle("官方通知") //设置通知标题
                .setContentText("开始学习安卓开发了") //设置通知内容
                .setSmallIcon(R.drawable.ic_baseline_person_24) //设置通知小图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.wuxing))//设置通知大图标
                .setColor(Color.parseColor("#ff0000"))//设置小图标颜色
                .setContentIntent(pendingIntent)//设置通知点击跳转
                .setAutoCancel(true)//设置通知点击消失
                .build();//构建
    }

    public void sendNotification(View view) {
        manager.notify(1,notification);//点击发通知
    }

    public void cacelNotification(View view) {
        manager.cancel(1);//点击取消通知 id和发通知id一致
    }
}

NotificationActivity

package com.example.mynotification;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Nullable;

public class NotificationActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		//onCreate方法用来初始化Activity实例对象
        Log.e("leo", "onCreate: 进入notificationActivity" );
    }
}

需要add到related file xml中

布局

(一)线性布局LinearLayout

线性布局在开发中使用最多,具有垂直方向与水平方向的布局方式,通过设置属性“android:orientation”控制方向,属性值垂直(vertical)和水平(horizontal),默认水平方向。
android:gravity:内部控件对齐方式,常用属性值有center、center_vertical、center_horizontal、top、bottom、left、right等。
这个属性在布局组件RelativeLayout、TableLayout中也有使用,FrameLayout、AbsoluteLayout则没有这个属性。
center:居中显示,这里并不是表示显示在LinearLayout的中心,当LinearLayout线性方向为垂直方向时,center表示水平居中,但是并不能垂直居中,此时等同于center_horizontal的作用;同样当线性方向为水平方向时,center表示垂直居中,等同于center_vertical。
top、bottom、left、right顾名思义为内部控件居顶、低、左、右布局。
这里要与android:layout_gravity区分开,layout_gravity是用来设置自身相对于父元素的布局。
android:layout_weight:权重,用来分配当前控件在剩余空间的大小。
使用权重一般要把分配该权重方向的长度设置为零,比如在水平方向分配权重,就把width设置为零。

二)相对布局RelativeLayout

相对布局可以让子控件相对于兄弟控件或父控件进行布局,可以设置子控件相对于兄弟控件或父控件进行上下左右对齐。
RelativeLayout能替换一些嵌套视图,当我们用LinearLayout来实现一个简单的布局但又使用了过多的嵌套时,就可以考虑使用RelativeLayout重新布局。
相对布局就是一定要加Id才能管理。

RelativeLayout中子控件常用属性:
1、相对于父控件,例如:android:layout_alignParentTop=“true”
android:layout_alignParentTop 控件的顶部与父控件的顶部对齐;
android:layout_alignParentBottom 控件的底部与父控件的底部对齐;
android:layout_alignParentLeft 控件的左部与父控件的左部对齐;
android:layout_alignParentRight 控件的右部与父控件的右部对齐;

2、相对给定Id控件,例如:android:layout_above=“@id/**”
android:layout_above 控件的底部置于给定ID的控件之上;
android:layout_below 控件的底部置于给定ID的控件之下;
android:layout_toLeftOf 控件的右边缘与给定ID的控件左边缘对齐;
android:layout_toRightOf 控件的左边缘与给定ID的控件右边缘对齐;
android:layout_alignBaseline 控件的baseline与给定ID的baseline对齐;
android:layout_alignTop 控件的顶部边缘与给定ID的顶部边缘对齐;
android:layout_alignBottom 控件的底部边缘与给定ID的底部边缘对齐;
android:layout_alignLeft 控件的左边缘与给定ID的左边缘对齐;
android:layout_alignRight 控件的右边缘与给定ID的右边缘对齐;

3、居中,例如:android:layout_centerInParent=“true”
android:layout_centerHorizontal 水平居中;
android:layout_centerVertical 垂直居中;
android:layout_centerInParent 父控件的中央;

(三)层布局FrameLayout

帧布局或叫层布局,从屏幕左上角按照层次堆叠方式布局,后面的控件覆盖前面的控件。
该布局在开发中设计地图经常用到,因为是按层次方式布局,我们需要实现层面显示的样式时就可以
采用这种布局方式,比如我们要实现一个类似百度地图的布局,我们移动的标志是在一个图层的上面。
在普通功能的软件设计中用得也不多。层布局主要应用就是地图方面。

(四)绝对布局AbsoluteLayout

绝对布局中将所有的子元素通过设置android:layout_x 和 android:layout_y属性,将子元素的坐标位置固定下来,即坐标(android:layout_x, android:layout_y) ,layout_x用来表示横坐标,layout_y用来表示纵坐标。屏幕左上角为坐标(0,0),横向往右为正方,纵向往下为正方。实际应用中,这种布局用的比较少,因为Android终端一般机型比较多,各自的屏幕大小。分辨率等可能都不一样,如果用绝对布局,可能导致在有的终端上显示不全等。所有基本不会使用,这里就不多介绍了。

(五)表格布局TableLayout

表格布局,适用于多行多列的布局格式,每个TableLayout是由多个TableRow组成,一个TableRow就表示TableLayout中的每一行,这一行可以由多个子元素组成。实际上TableLayout和TableRow都是LineLayout线性布局的子类。但是TableRow的参数android:orientation属性值固定为horizontal,且android:layout_width=MATCH_PARENT,android:layout_height=WRAP_CONTENT。所以TableRow实际是一个横向的线性布局,且所以子元素宽度和高度一致。
注意:在TableLayout中,单元格可以为空,但是不能跨列,意思是只能不能有相邻的单元格为空。
TableLayout常用属性:
android:shrinkColumns:设置可收缩的列,内容过多就收缩显示到第二行
android:stretchColumns:设置可伸展的列,将空白区域填充满整个列
android:collapseColumns:设置要隐藏的列
列的索引从0开始,shrinkColumns和stretchColumns可以同时设置。
子控件常用属性:
android:layout_column:第几列
android:layout_span:占据列数

(六)网格布局GridLayout

作为android 4.0 后新增的一个布局,与前面介绍过的TableLayout(表格布局)其实有点大同小异;
不过新增了一些东东
①跟LinearLayout(线性布局)一样,他可以设置容器中组件的对齐方式
②容器中的组件可以跨多行也可以跨多列(相比TableLayout直接放组件,占一行相比较)
因为是android 4.0新增的,API Level 14,在这个版本以前的sdk都需要导入项目。这里不解释。

常用属性:
排列对齐:
①设置组件的排列方式: android:orientation=“” vertical(竖直,默认)或者horizontal(水平)
②设置组件的对齐方式: android:layout_gravity=“” center,left,right,buttom

设置布局为几行几列:
①设置有多少行: android:rowCount=“4” //设置网格布局有4行
②设置有多少列: android:columnCount=“4” //设置网格布局有4列

设置某个组件位于几行几列
注:都是从0开始算的哦!
①组件在第几行: android:layout_row = “1” //设置组件位于第二行
②组件在第几列: android:layout_column = “2” //设置该组件位于第三列

设置某个组件横跨几行几列(合并):
横跨几行: android:layout_rowSpan = “2” //纵向横跨2行
横跨几列: android:layout_columnSpan = “3” //横向横跨2列

android:layout_gravity="fill"填充

Menu

选项菜单OptionMenu

MainActivity

package com.example.testmenu;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    //创建OptionMenu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //加载菜单资源
        getMenuInflater().inflate(R.menu.option,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case R.id.save:
                Toast.makeText(this,"保存",Toast.LENGTH_SHORT).show();
                break;
            case R.id.exit:
                Toast.makeText(this,"退出",Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">


    <Button
        android:id="@+id/ctx_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="上下文菜单" />

    <Button
        android:id="@+id/popup_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="弹出式菜单" />

</LinearLayout>

上下文菜单ContextMenu

两种实现方式,一种重写onCreateContextMenu,一种使用上下文操作模式

package com.example.testmenu;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
/*
        //注册contextmenu
        registerForContextMenu(findViewById(R.id.ctx_btn));

*/
    findViewById(R.id.ctx_btn).setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View view) {
            startActionMode(cb);
            return false;
        }
    });

    }
/*
    //创建
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        getMenuInflater().inflate(R.menu.context,menu);
    }

    //菜单选的操做覆盖
    @Override
    public boolean onContextItemSelected(@NonNull MenuItem item) {
        switch(item.getItemId()){
            case R.id.delet:
                Toast.makeText(this,"删除",Toast.LENGTH_SHORT).show();
                break;
            case R.id.opera1:
                Toast.makeText(this,"操作1",Toast.LENGTH_SHORT).show();
                break;
            case R.id.opera2:
                Toast.makeText(this,"操作2",Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onContextItemSelected(item);
    }
*/

    //为按钮设置上下文操作模式
    //实现ActionModeCallback接口
    ActionMode.Callback cb=new ActionMode.Callback() {
        //启动上下文操作模式时调用
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            Log.e("TAG", "创建");
            getMenuInflater().inflate(R.menu.context,menu);
            return true;
        }
        //在创建方法后使用
        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
            return true;
        }

        //菜单被点击
        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
            Log.e("TAG", "点击");
            switch(menuItem.getItemId()){
                case R.id.delet:
                    Toast.makeText(MainActivity.this,"删除",Toast.LENGTH_SHORT).show();
                    break;
                case R.id.opera1:
                    Toast.makeText(MainActivity.this,"操作1",Toast.LENGTH_SHORT).show();
                    break;
                case R.id.opera2:
                    Toast.makeText(MainActivity.this,"操作2",Toast.LENGTH_SHORT).show();
                    break;
            }
            return true;
        }

        //上下文操作结束时调用
        @Override
        public void onDestroyActionMode(ActionMode actionMode) {
            Log.e("TAG", "结束");
        }
    };
    
}

context.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:title="删除" android:id="@+id/delet" />
    <item android:title="重命名" >
        <menu >
            <item android:title="opera1" android:id="@+id/opera1" />
            <item android:title="opera2" android:id="@+id/opera2"/>
        </menu>
    </item>
</menu>

通过java创建optionmenu菜单

  public boolean onCreateOptionsMenu(Menu menu) {
        //加载菜单资源
       // getMenuInflater().inflate(R.menu.option,menu);
        //组id 菜单项id 序号 菜单名称
        menu.add(1,1,1,"设置");
        SubMenu sub = menu.addSubMenu(1, 2, 2, "更多");
        sub.add(2,3,1,"添加");
        sub.add(2,4,1,"删除");
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
/*            case R.id.save:
                Toast.makeText(this,"保存",Toast.LENGTH_SHORT).show();
                break;
            case R.id.exit:
                Toast.makeText(this,"退出",Toast.LENGTH_SHORT).show();
                break;*/
            case 1:
                Toast.makeText(this,"设置",Toast.LENGTH_SHORT).show();
                break;
            case 2:
                Toast.makeText(this,"更多",Toast.LENGTH_SHORT).show();
                break;
            case 3:
                Toast.makeText(this,"添加",Toast.LENGTH_SHORT).show();
                break;
            case 4:
                Toast.makeText(this,"删除",Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

弹出菜单PopupMenu

    Button button=findViewById(R.id.popup_btn);
findViewById(R.id.popup_btn).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        //实例化PopupMenu对象
        PopupMenu menu = new PopupMenu(MainActivity.this,button);//出现在谁的下方
        //加载菜单资源,利用MenuInflater将Menu资源加载到PopupMenu.getMenu()返回的对象中
        menu.getMenuInflater().inflate(R.menu.popup,menu.getMenu());
        //设置点击监听
        menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem menuItem) {
                switch (menuItem.getItemId()){
                    case R.id.copy:
                        Toast.makeText(MainActivity.this,"复制",Toast.LENGTH_SHORT).show();
                    break;
                    case R.id.paste:
                        Toast.makeText(MainActivity.this,"粘贴",Toast.LENGTH_SHORT).show();
                    break;
                }
                return false;
            }
        });
        //
        menu.show();
    }
});
}

popup.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/copy"
        android:title="复制" />
    <item
        android:id="@+id/paste"
        android:title="粘贴" />
</menu>

对话框

AlterDialog提示对话框

package com.example.dialogdemo;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //方法二
    public void  showNormalDialog(){
        AlertDialog builder=new AlertDialog.Builder(MainActivity.this).create();
        builder.setTitle("提示");
        builder.setMessage("您确定退出吗?");
        builder.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                
            }
        });
        builder.show();
    }

    //方法一
    public void myClick(View view) {
        switch (view.getId()){
            case R.id.normal_dialog_btn:

                AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("提示");
                builder.setMessage("您确定退出吗?");
                //设置提示按钮,以及后续操作
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        finish();
                    }
                });
                builder.setNegativeButton("取消",null);
                builder.show();
                /*
                等价
                AlertDialog alertDialog = builder.create();
                alertDialog.show();
                 */

                break;
            case R.id.diy_dialog_btn:

                break;
        }
    }
}

方法二设置按钮时不能传空操作

Dialog

设置按钮布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:background="@drawable/dialog"
    xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
    android:textColor="#e61414"
    android:textStyle="bold|italic"
    android:layout_marginTop="290dp"
    android:textSize="34sp"
    android:text="@string/isexit"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_margin="25dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/yes_btn"
            android:background="@mipmap/yes"
            android:layout_marginRight="20dp"
            android:layout_width="70dp"
            android:layout_height="50dp"/>
          <Button
              android:id="@+id/no_btn"
              android:background="@mipmap/no"
              android:layout_marginLeft="20dp"
            android:layout_width="70dp"
            android:layout_height="50dp"/>
    </LinearLayout>
</LinearLayout>
package com.example.dialogdemo;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //方法二
/*
    public void  showNormalDialog(){
        AlertDialog builder=new AlertDialog.Builder(MainActivity.this).create();
        builder.setTitle("提示");
        builder.setMessage("您确定退出吗?");
        builder.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {

            }
        });
        builder.show();
    }
*/

    //方法一
    public void myClick(View view) {
        switch (view.getId()){
            case R.id.normal_dialog_btn:

                AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("提示");
                builder.setMessage("您确定退出吗?");
                //设置提示按钮,以及后续操作
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        finish();
                    }
                });
                builder.setNegativeButton("取消",null);
                builder.show();
                /*
                等价
                AlertDialog alertDialog = builder.create();
                alertDialog.show();
                 */

                break;
            case R.id.diy_dialog_btn:
                MyDialog myDialog = new MyDialog(this,R.style.mydialog);
                myDialog.show();
                break;
        }
    }
}

activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/normal_dialog_btn"
        android:text="@string/show_alter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="myClick"
        />
    <Button
        android:id="@+id/diy_dialog_btn"
        android:text="@string/show_div_alter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="myClick"
        />
</LinearLayout>

定义一个类继承dialog

public class MyDialog extends Dialog {

    public MyDialog(@NonNull Context context, int themeResId) {
        super(context, themeResId);
        setContentView(R.layout.dialog_layout);

        findViewById(R.id.yes_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                System.exit(0);
            }
        });
        findViewById(R.id.no_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dismiss();
            }
        });
    }
}

为按钮设置样式

<style name="mydialog" parent="android:style/Theme.Dialog">
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@color/white</item>
</style>

在mydialog中应用样式,为对话框添加监听器

如果按钮图片没有加载出来则修改values/themes/themes.xml中

<style name="Theme.Dialogdemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">

四大组件

1.Activity

4种启动模式

1.1SingTop

只执行最上层的,如A,B,C,D,想执行B,则会添加B

1.2singleTask

顶部复用

一个栈里一个activity只能有一个如A,B,C,D启动B,就会弹出C,D

1.3standard默认模式

先进先出,栈的模式

1.4singleInstance

独自占有一个栈,全局只有一个,如当前栈内是A,B,C,D需要启动E,则会新建栈E,如打电话

生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhcyNQIh-1660619680017)(C:\Users\TR\AppData\Roaming\Typora\typora-user-images\image-20220814175616933.png)]

package com.example.activitylife;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart: ");
    }

    //交互                                         
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }

    //暂停
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop: ");
    }

    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }
}

线路一:正常打开到关闭一直往下

线路二:打开另外的activity到onstop()后若返回键,则会到onrestart()再重新创建

线路三:如进行浮窗类事件,则会触发onpause()返回则继续onresume

线路四:终止

补充:当actity销毁之前可以调用

@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
    super.onSaveInstanceState(outState, outPersistentState);
}

把数据存储起来,在下次程序启动的时候,可以拿到数据

当按返回键时触发

@Override
public void onBackPressed() {
    super.onBackPressed();
}

两个Activry之间的传递

可以使用bundle 或则intent自带的常用类型

package com.example.activitylife;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart: ");
    }
    //交互
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }

    //暂停
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop: ");
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }

    public void myclick(View view) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("name","bundle消息");
        bundle.putChar("开头",'A');
        intent.putExtra("name",bundle);
        startActivity(intent);
    }
}

secondactivity

package com.example.activitylife;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_activity);
        if(getIntent()!=null){
            Bundle bundle = getIntent().getBundleExtra("name");
            if(bundle!=null){
                String name = bundle.getString("name");
                Button button = findViewById(R.id.sec);
                button.setText(name);
            }
        }

    }
}

activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/btn_onn"
        android:onClick="myclick"
        android:text="@string/secondactivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

second.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/sec"
        android:textSize="35sp"
        android:text="@string/seconnd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

序列化传输

传输序列化后的对象

public class User implements Serializable{
    private String title;

    public class BaseInfo implements Serializable{}
}

序列化的类的方法也需要序列化

public void myclick(View view) {
    intent.putExtra("",new User());
}

实现关闭SecondActivity或返回时把值传给MainActivity

startActivityForResult(intent,999);

使用Result传入标识符

SecondActivity添加监听

public void seclick(View view) {
    setResult(RESULT_OK);
    finish();
}

MainActivity添加接收判断

public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.i(TAG, "onActivityResult:启动了 ");
    if(requestCode == 999 && requestCode == RESULT_OK){
        Log.i(TAG, "onActivityResult: 成功了!");
        setTitle("前一个页面返回啦!!");
    }else {
        setTitle("返回失败了!!");
    }

也可直接关闭的时候传入Inent 返回的时候在public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) 的data接收

2.Service服务

生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OOK5pLfm-1660619680018)(C:\Users\dofun008\AppData\Roaming\Typora\typora-user-images\image-20220815091236288.png)]

启动服务

MainActivity

package com.example.servicedemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void operate(View view) {
        switch (view.getId()){
            case R.id.start_:
                //启动服务
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stop_:
                //停止服务
                Intent intent1 = new Intent(this,MyService.class);//无论开启几个意图还是操作同一个对象
                stopService(intent1);
                break;

        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <Button
        android:id="@+id/start_"
        android:text="启动服务"
        android:onClick="operate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/stop_"
        android:text="停止服务"
        android:onClick="operate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/bind_"
        android:text="绑定服务"
        android:onClick="operate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/unbind_"
        android:text="解除服务"
        android:onClick="operate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

Myservice

package com.example.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    private static final String TAG = "Ms";
    public MyService() {
    }

    //创建
    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate: ");
        super.onCreate();
    }

    //启动
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand: " );
       return super.onStartCommand(intent, flags, startId);
    }

    //绑定
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.e(TAG, "onBind: " );
        throw new UnsupportedOperationException("Not yet implemented");
    }

    //解绑
    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind: " );
        return super.onUnbind(intent);
    }

    //摧毁
    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy: " );
        super.onDestroy();
    }


}

启动服务,通过onCreate,onStartCommand方法,停止服务经过onDestroy,再次启动则会重新创建

服务对象只会存在一个,若服务已经创建,后续启动的都是同一个服务,除非先销毁它

服务的生命周期和Activity无关,除非手动关闭

绑定服务
package com.example.servicedemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void operate(View view) {
        switch (view.getId()){
            case R.id.start_:
                //启动服务
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stop_:
                //停止服务
                Intent intent1 = new Intent(this,MyService.class);//无论开启几个意图还是操作同一个对象
                stopService(intent1);
                break;
            case R.id.bind_:
                //绑定服务
                Intent intent2 = new Intent(this,MyService.class);
                bindService(intent2, conn,BIND_AUTO_CREATE);
                break;
            case R.id.unbind_:
                //解绑服务

                unbindService(conn);
                break;
        }
    }
}

服务不存在,执行绑定,先创建服务,再绑定服务,执行解绑,先解绑,再销毁。

服务存在,bindService只能被onBind调用,unbindService只能被onUnbind调用

注意:绑定服务不会启动服务,不会在后台启动,只绑定不启动,退出后台后,服务会销毁

进度监控

IBinder:在android中用于远程操作对象的一个基本接口

//绑定
@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    Log.e(TAG, "onBind: " );
    return new Binder();
}
//对于onBind方法而言,要求返回IBinder对象
//实际上,我们会自己定义一个内部类,继承Binder类
//用于绑定客户端和服务
private ServiceConnection conn=new ServiceConnection() {
    //当客户端正常连接着服务时,执行服务的绑定会被调用
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("TAG", "onServiceConnected: " );
    }

    //当客户端和服务的连接丢失了
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.e("TAG", "onServiceDisconnected: " );
    }
};

服务器解绑后无法再次绑定,然而每次重新绑定会执行

onServiceConnected()方法

所以需要IBind来传值

package com.example.servicedemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    //用于绑定客户端和服务
    private ServiceConnection conn=new ServiceConnection() {
        //当客户端正常连接着服务时,执行服务的绑定会被调用
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e("TAG", "onServiceConnected: " );
            MyService.MyBinder mb= (MyService.MyBinder) iBinder;
            int process = mb.getProcess();
            Log.e("TAG", "当前进度是: "+process );
        }

        //当客户端和服务的连接丢失了
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e("TAG", "onServiceDisconnected: " );
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void operate(View view) {
        switch (view.getId()){
            case R.id.start_:
                //启动服务
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stop_:
                //停止服务
                Intent intent1 = new Intent(this,MyService.class);//无论开启几个意图还是操作同一个对象
                stopService(intent1);
                break;
            case R.id.bind_:
                //绑定服务
                Intent intent2 = new Intent(this,MyService.class);
                bindService(intent2, conn,BIND_AUTO_CREATE);
                break;
            case R.id.unbind_:
                //解绑服务

                unbindService(conn);
                break;
        }
    }
}

开启线程,传递进度

package com.example.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import java.util.concurrent.TimeUnit;

public class MyService extends Service {
    private int i;
    private static final String TAG = "Ms";
    public MyService() {
    }

    //创建
    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate: ");
        super.onCreate();
        //开启一个线程。开启耗时任务,(从1到100)
        new Thread(()->{
            for (i = 1; i <=100; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //启动
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand: " );
       return super.onStartCommand(intent, flags, startId);
    }

    //绑定
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.e(TAG, "onBind: " );
        return new MyBinder();
    }
    //对于onBind方法而言,要求返回IBinder对象
    //实际上,我们会自己定义一个内部类,继承Binder类

    class MyBinder extends Binder{
        //定义自己需要的方法(实现进度监控)
        public int getProcess(){
            return i;
        }

    }
    //解绑
    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind: " );
        return super.onUnbind(intent);
    }

    //摧毁
    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy: " );
        super.onDestroy();
    }


}

onBind的类的返回值IBinder刚好可以作为ServiceConnection.onServiceConnected的返回值,但是由于IBinder类需要实现很多方法,不方便,于是使用他的实现类Binder类来做返回值,可以自定义自己需要的业务

2.2AIDL通信

不同进程之间,数据共享,数据通信

创建新的项目aidldemo1

package com.example.aidldemo1;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e("Ms", "onServiceConnected: " );
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e("Ms", "onServiceDisconnected: " );
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void operate(View view) {
        switch (view.getId()){
            case R.id.start_:
                //远程启动服务
               //Intent intent = new Intent("com.liu.myservice");
               Intent intent = new Intent();
               intent.setAction("com.liu.myservice");
                intent.setPackage("com.example.servicedemo");
                startService(intent);
                break;
            case R.id.stop_:
                //远程停止服务
               // Intent intent1 = new Intent("com.liu.myservice");
                Intent intent1 = new Intent();
                intent1.setAction("com.liu.myservice");
                intent1.setPackage("com.example.servicedemo");
                stopService(intent1);
                break;
            case R.id.bind_:
                //远程绑定服务
                //Intent intent2 = new Intent("com.liu.myservice");
                 Intent intent2 = new Intent();
                intent2.setAction("com.liu.myservice");
                intent2.setPackage("com.example.servicedemo");
                bindService(intent2, conn,BIND_AUTO_CREATE);
                break;
            case R.id.unbind_:
                //远程解绑服务
                unbindService(conn);
                break;
        }
    }
}

给原来项目的service起别名

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

注意,这个Intent传值一定要显式声明

远程通信

创建aidl资源

// IMyAidlInterface.aidl
package com.example.serviceaidldemo;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);


        void showProgress();
}

rebuild项目

改写传值方法,因为IBinder只能同一个项目传值

MyService改写onbind

//绑定
@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    Log.e(TAG, "onBind: " );
    return new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void showProgress() throws RemoteException {
            Log.e(TAG, "aidl显示的进度为"+i);
        }
    };
}

MainActivity改写

//用于绑定客户端和服务
private ServiceConnection conn=new ServiceConnection() {
    //当客户端正常连接着服务时,执行服务的绑定会被调用
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("Ms", "onServiceConnected: " );
        IMyAidlInterface imai = IMyAidlInterface.Stub.asInterface(iBinder);
        try {
            imai.showProgress();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

在aidldemo中创建aidl 创建下一级目录需要和主程序的包名一直

,复制aidl文件,并rebuilder

改写传输代码

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

    IMyAidlInterface imit = IMyAidlInterface.Stub.asInterface(iBinder);
    try {
        imit.showProgress();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

AIDL本质是生成Java类,自己写实现,如同搭建了桥梁

3.ContentProvider

创建两个项目dataproviderdemo和dataresoverdemo

在dataproviderdemo创建other ContentPovider方法

package com.example.dataproviderdemo;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

在dataresoverdemo使用dataproviderdemo暴露的方法

在MyContentProvider中onCreate方法中创建数据库

@Override
public boolean onCreate() {
    // TODO: Implement this to initialize your content provider on startup.
    SQLiteOpenHelper sqLiteOpenHelper=new SQLiteOpenHelper(getContext(),"stu.db",null,1) {
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {

            String sql="create table info_tb (_id integer primary key autoincrement,"
                    +"name varchar(20),"
                    +"age integer,"
                    +"gender varchar(2))";
            sqLiteDatabase.execSQL(sql);
        }

        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

        }
    };
    db = sqLiteOpenHelper.getReadableDatabase();

    //返回true
    return true;
}

添加方法

case R.id.add_btn:
    ContentValues values = new ContentValues();
    values.put("name",name);
    values.put("age",age);
    values.put("gender",genderstr);
    //返回值是MyContentProvider中insert传入的值
    Uri uri = resolver.insert(Uri.parse("content://com.liu.myprovider"), values);//参数1:URI对象
    //解析返回的uri的id
    long id = ContentUris.parseId(uri);
    Toast.makeText(MainActivity.this, "添加成功,新学生的学号是"+id, Toast.LENGTH_SHORT).show();
    break;

contentprovider

@Override
public Uri insert(Uri uri, ContentValues values) {
    // TODO: Implement this to handle requests to insert a new row.
    Log.e("TAG", "调用了DataProviderDemo中的insert方法");
    long id = db.insert("info_tb", null, values);
    //将id追加到uri后面
    return  ContentUris.withAppendedId(uri,id);
}

这里resover解析uri然后传入数据,然后接收到contentprovider的ContentUris的方法携带的参数id作为添加成功的依据

查询操作

重写provider的query方法

contentprovider

public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    // TODO: Implement this to handle query requests from clients.
    Cursor c = db.query("info_tb", projection, selection, selectionArgs, null, null, sortOrder);
    return c;
}

resover

case R.id.select_btn:
    //查询、

    Cursor cursor = null;
    if(!idedt.equals("")){
        cursor = resolver.query(uri, null, "_id=?", new String[]{idedt}, null);;
    }else {
        cursor = resolver.query(uri, null, null, null, null);;
    }

    if(cursor!=null){
        SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.item,cursor,
                new String[]{"_id","name","age","gender"},
                new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//页面更新
        stulist.setAdapter(adapter);
    }else {
        Toast.makeText(MainActivity.this, "查无此人", Toast.LENGTH_SHORT).show();
    }
    break;

删除

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    // Implement this to handle requests to delete one or more rows.
    int result = db.delete("info_tb", selection, selectionArgs);
    return result;
}
case R.id.delet_btn:
    //删除、
    int i = resolver.delete(uri, "_id=?", new String[]{idedt});
    if(i>0){
        Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
    }
    break;

修改操作

@Override
public int update(Uri uri, ContentValues values, String selection,
                  String[] selectionArgs) {
    // TODO: Implement this to handle requests to update one or more rows.
    int tb = db.update("info_tb", values, selection, selectionArgs);
    return tb;
}
    case R.id.update_btn:
        //修改、
        ContentValues values1 = new ContentValues();
        values1.put("name",name);
        values1.put("age",age);
        values1.put("gender",genderstr);
        int update = resolver.update(uri, values1, "_id=?", new String[]{idedt});
        if (update>0){
            Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(MainActivity.this, "修改失败", Toast.LENGTH_SHORT).show();
        }

        break;
}

在这里,已经很明显了,contentprovider为resover提供接口,resover请求contentprovider的方法,操作contentprovider的数据,然后收到其返回值

SQLite数据库

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。

下载SQLite Expert

SQL语句

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <EditText
        android:id="@+id/name_edt"
        android:hint="姓名:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <EditText
        android:id="@+id/age_edt"
        android:numeric="integer"
        android:hint="年龄:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <RadioGroup
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
       <TextView
           android:text="性别:"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>
        <RadioButton
            android:id="@+id/man"
            android:text=""
            android:layout_marginLeft="5dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <RadioButton
            android:id="@+id/woman"
            android:text=""
            android:layout_marginLeft="5dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RadioGroup>
    <EditText
        android:id="@+id/id_edt"
        android:hint="编号"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/add_btn"
            android:text="添加"
            android:onClick="opera"
            android:layout_marginLeft="5dp"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:onClick="opera"
            android:id="@+id/select_btn"
            android:text="查询"
            android:layout_marginLeft="5dp"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:onClick="opera"
            android:id="@+id/delet_btn"
            android:text="删除"
            android:layout_marginLeft="5dp"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:onClick="opera"
            android:id="@+id/update_btn"
            android:text="修改"
            android:layout_marginLeft="5dp"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>

编写按钮处理事件

package com.example.sqlitetest;

import androidx.appcompat.app.AppCompatActivity;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText nameEdt,ageEdt,idEdt;
    private RadioGroup genderGp;
    private String genderstr="男";
    private SQLiteDatabase db;
    private String sql;
    private ListView stulist;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        stulist=findViewById(R.id.stu_list);
        nameEdt=findViewById(R.id.name_edt);
        ageEdt=findViewById(R.id.age_edt);
        idEdt=findViewById(R.id.id_edt);
        genderGp=findViewById(R.id.gender_gp);
        genderGp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                if(i==R.id.man){
                    //男
                    genderstr="男";
                }else {
                    genderstr="女";
                }
            }
        });

        //添加、
        //操作对象, 数据库名称
        //如果只有一个数据库名称,那么这个数据库位置会在私有目录中 data/data
        //如果带SD卡路径,那么数据库位置则会在指定的路径下
        //String path = Environment.getExternalStorageDirectory() + "/stu.db";
        SQLiteOpenHelper helper=new SQLiteOpenHelper(this,"test3.db",null,1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                //创建
                Toast.makeText(MainActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();
                String sql="create table test_tb1(_id integer primary key autoincrement,"
                        +"name varchar(20),"+
                        "age integer,"+"gender varchar(6) )"
                        ;
                sqLiteDatabase.execSQL(sql);
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                //升级
                Toast.makeText(MainActivity.this, "数据库升级", Toast.LENGTH_SHORT).show();

            }
        };
        //获取数据库对象
        //数据库存在则打开数据库,数据库不存在则打开数据库
        //数据库存在,但版本号升高了,则调用数据库升级方法
        db = helper.getReadableDatabase();
        //查询 db.rawQuery();
        //增删改创  db.execSQL();
    }


    public void opera(View view) {
        switch (view.getId()){
            case R.id.add_btn:

                String name=nameEdt.getText().toString();
                String age=ageEdt.getText().toString();

               // String sql="insert into test_tb1 (name,age,gender) values('"+name+"',"+age+",'"+genderstr+"') ";
               sql="insert into test_tb1 (name,age,gender) values(?,?,?) ";
                db.execSQL(sql,new String[]{name,age,genderstr});
                Toast.makeText(MainActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.delet_btn:
                //删除、
                sql="delete from test_tb1 where _id=?";
                db.execSQL(sql,new String[]{idEdt.getText().toString()});
                Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.select_btn:
                //查询、
                if(!idEdt.getText().toString().equals("")){
                    sql+="where _id="+idEdt.getText().toString();
                }
                sql="select * from test_tb1";
                //查询结果
                Cursor cursor = db.rawQuery(sql, null);

                //
                SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.item,cursor,
                        new String[]{"_id","name","age","gender"},
                        new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//页面更新
                stulist.setAdapter(adapter);
                break;
            case R.id.update_btn:
                //修改、
                sql="update test_tb1 set name=?,age=?,gender=? where _id=?";
                db.execSQL(sql,new String[]{nameEdt.getText().toString(),ageEdt.getText().toString(),genderstr,idEdt.getText().toString()});
                Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

使用了Viewlist要设置适配器

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/name_item"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/age_item"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/id_item"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/gender_item"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

注意:

如果用的虚拟机,不要把数据库路径写在SD卡中,会闪退

需要借助SQLiteOpenHelper类然后创建数据库或者打开,

使用helper.getReadableDatabase()得到SQLiteDatabase对象,

然后可以对数据库进行增删改查等操作

使用API对数据进行操作

添加insert()

switch (view.getId()){
    case R.id.add_btn:

        ContentValues values = new ContentValues();
        values.put("name",name);
        values.put("age",age);
        values.put("gender",genderstr);
        long id = db.insert("test_tb1", null, values);//表名,可以为空的列,参数
        Toast.makeText(MainActivity2.this, "添加成功,新学员学号是:"+id, Toast.LENGTH_SHORT).show();
        break;

第二个参数依据第三个参数而定是否为null

查询query()

case R.id.select_btn:
    //查询、
    //参数1:表名 参数2:列(查询全部为null或者{"*"})
    // 参数3:条件(列1=? 多值为列1=? and 列2=?)参数4:条件的参数 参数5:分组
    //参数6:分组条件限定 参数7:排序
    Cursor cursor = db.query("test_tb1",null,null,null,null,null,null);

    SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.item,cursor,
            new String[]{"_id","name","age","gender"},
            new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
            CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//页面更新
    stulist.setAdapter(adapter);
    break;

删除delete()

case R.id.select_btn:
    //查询、
    //参数1:表名 参数2:列(查询全部为null或者{"*"})
    // 参数3:条件(列1=? 多值为列1=? and 列2=?)参数4:条件的参数 参数5:分组
    //参数6:分组条件限定 参数7:排序
    Cursor cursor = db.query("test_tb1",null,null,null,null,null,null);

    SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.item,cursor,
            new String[]{"_id","name","age","gender"},
            new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
            CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//页面更新
    stulist.setAdapter(adapter);
    break;

修改

    case R.id.update_btn:
        //修改、
        ContentValues values1 = new ContentValues();
        values1.put("name",name);
        values1.put("age",age);
        values1.put("gender",genderstr);
        int i = db.update("test_tb1", values1, "_id=?", new String[]{idedt});
        if(i>0){
            Toast.makeText(MainActivity2.this, "修改成功", Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(MainActivity2.this, "修改失败啦", Toast.LENGTH_SHORT).show();

        }

        break;
}

条件查询

case R.id.select_btn:
    //查询、
    //参数1:表名 参数2:列(查询全部为null或者{"*"})
    // 参数3:条件(列1=? 多值为列1=? and 列2=?)参数4:条件的参数 参数5:分组
    //参数6:分组条件限定 参数7:排序
    Cursor cursor = null;
    if(!idedt.equals("")){
        cursor = db.query("test_tb1",null,"_id=?",new String[]{idedt},null,null,null);
    }else {
        cursor = db.query("test_tb1",null,null,null,null,null,null);
    }

    if(cursor!=null){
        SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.item,cursor,
                new String[]{"_id","name","age","gender"},
                new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//页面更新
        stulist.setAdapter(adapter);
     }else {
        Toast.makeText(MainActivity2.this, "查无此人", Toast.LENGTH_SHORT).show();
    }
    break;

版权声明:本文为liuwushuang233原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。