Android从入门到入坟(六)


活动地址:CSDN21天学习挑战赛

跨程序共享数据

1、内容提供器简介

内容提供其(Content Provider)主要 用于在不同引用程序之间实现数据共享功能,他提供了一套 完整的机制,允许一个程序访问另外一个程序中的数据,并且还能保证访问的数据安全性

不同文件存储和SharedPreferences存储中的两种全局读写操作模式

内容提供其可以选择只对哪一部分进行操作

2、运行时权限

权限分成两种权限 危险权限和普通权限

危险权限一共9组24个 其他的全是普通权限

http://developer.android.com/reference.android/Mainfest.permission,html 中可以查看完整的权限列表

3、程序运行时申请权限

首先创建一个RuntimePermissionTest项目

首先申请一个拨打电话的权限 CALL_PHONE

之前学过的ACTION_DIAL打开电话键盘是不需要需要授权的

通过点击按钮进行设置

首先创建一个显示的Intent 并将其action设置成ACTION_CALL则是一个系统内置的打电话的动作,并且该动作需要获取相关权限

public void Apply_callphone(View view) {
        Intent intent=new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }

然后外面前往AndroidManifest.xml文件中设置权限

添加权限即可

添加该权限后 还需要用户进行手动确认权限,因此我们就先要确认用户是否授权

我们只需要向调用ContextCompat.checkSelfPermission()这个方法传入一个 Context 和拨打电话的权限名字 Manifest.permission.CALL_PHONE 他就会返回一个是否授权的只我们就用它和PackageManager.PERMISSION_GRANTED作比较,如果 相等就代表已经授权可以直接进行打电话的动作,

不相等就要进行授权否则程序崩溃,我们就通过 ActivityCompat.requestPermissions()该方法调出一个对话框 让用户进行确认

这个方法接收三个参数

1、Context

2、一个String数组 数组中我们放入打电话的权限名即可

3、requestCode 一个请求码 只要是唯一值即可

调用完这个方法后就会返回一个权限申请的对话框,用户可以对其进行确定和取消

但是不管是那种结果最终都会回到onRequestPermissionsResult()这个方法

因此我们需要重写一下这个方法,利用我们刚刚所传入的请求与码 进行操作

注意 方法在ActivityCompat.requestPermissions()申请权限之后调用 因此申请的结果都会封装到grantResults数组中 我们访问数组中的第一个值如果结果等于PackageManager.PERMISSION_GRANTED就代表授权成功否则就提示授权失败

演示代码如下

// 该方法为点击事件的方法
public void Apply_callphone(View view) {
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
            // 授权结果不等于 PackManager.PERMISSION.GRANTED 则进行调用
            // 弹出授权对话框
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},1);
        }else{
            // 否则就代表授权成功了
            try {
                Intent intent=new Intent(Intent.ACTION_CALL);
                intent.setData(Uri.parse("tel:15313396550"));
                startActivity(intent);
            }catch (Exception e){
                Toast.makeText(this, "授权失败了,亲", Toast.LENGTH_SHORT).show();
            }

        }
    }
}
// 重写的onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    	// 利用请求码进行操作
        switch (requestCode){
            case 1:
                if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
                   try {
                       Intent intent=new Intent(Intent.ACTION_CALL);
                       intent.setData(Uri.parse("tel:15313396550"));
                       startActivity(intent);
                   }catch (Exception e){
                       Toast.makeText(this, "异常错误,亲", Toast.LENGTH_SHORT).show();
                   }
                }else {
                    // 取消授权或者授权失败了进行调用
                    Toast.makeText(this, "授权失败了,亲", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }

4、Content Resolver 的i基本用法

对于一个程序来说,想要访问内容提供其中的共享数据就一定要借助Content Resolver类,Content中的getContentResolver()方法就可以得到该类的实例,Content中也是提供了CRUD方法对数据进行增删改查的,

insert() 查询数据

update() 更新数据

delete() 删除数据

query() 查询数据

ContentResolver中的增删改查不同于Sqllite他无需查询表明 而是使用了URI参数进行代替,内容URI给内容提供其中提供数据建立的唯一标识符,它由两个部分组成

1、authority 该参数用于区分不同的应用程序,一般采用程序包名的方式命名

2、path 用于对统一程序中不同的表做区分的通常加到authority后面

然后我们还需要在组成的字符串前面加上协议声明

例如一个程序包名为 com.example.app.这个程序包下有两个表table1和table2

那URI标准组成格式如下

content://com.example.app.provider/table2

content://com.example.app.provide/table1

这样可以非常清楚的表述出来外面想要访问的数据了

得到URI字符串后外面还需要将其解析成Uri对象

Uri uri=Uri.Parse(“content://com.example.app.provider/table2”)

然后就可以利用改产线查询数据了

// 查询数据
Cousor cousor=getContentResolver().query{
	uri,
	projection,
	selection,
	selectionArgs,
	sortOrder
}
query()方法参数对应SQL部分描述
urifrom table name指定查询的表
projectionselect column1,column2指定查询的列名
selectionwhere column=value指定where的约束条件
selectionArgs为where中的占位符提供具体值
orderByorder by column1,column2指定查询结果排序方式

最后返回的对象依旧是一个 Cousor对象

查询数据:

我们通过移动游标的位置来遍历Cursor的所有行,然后再取出每一行中相对应的数据

if(cursor!=null){
	where(cursor.moveTotext()){
	 	String column1=cursor.getString(cursor.getColumnIndex("colunm1"));
		int colunm2=cursor.getInt(cursor.getColumnText("column2"))
	}
}
插入数据:

依旧利用ContentValues来传递与组装数据

ContentValues Values=new ContentValues();
Values.put("column1","text");
Values.put("column2",1)
getCotentResolver().insert(uri,values)
更新数据:

如果我们想要将column1的值清空,可以借助update()方法实现

我们利用selection 和 selectionArgs参数便可以实现

ContentValues values=new ContentValues();
values.put("column1","");
getContenResolver().update(uri,values,"conlumn1=? and column2=?",new String[]{"text","1"})
删除数据

我们也可以调用 delete()房啊删除这条数据

getContentResolver(),delete(uri,"column2=?",new String[]{"1"});

5、读取系统联系人

本次实践将会联系前几章节的类容

首先我们创建Contacts Text项目

我们本次使用ListView将获取到的信息展现出来

因此我们需要准备一个 Furit类用于存储获取到信息类容

然后再准备一个ListViewAdapter的一个适配器 和准备一个ListView子元素的布局

Furit类代码如下

public class Furit {
    String Name;
    String Phone;
    public Furit(String name ,String phone){
        this.Name=name;
        this.Phone=phone;
    }

    public String getName() {
        return Name;
    }

    public String getPhone() {
        return Phone;
    }
}

ListView适配器代码如下

public class ListViweAdapter extends ArrayAdapter<Furit> {
    int itemID=0;
    public ListViweAdapter(@NonNull Context context, int resource, @NonNull List<Furit> objects) {
        super(context, resource, objects);
        itemID=resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        TextView Name=null;
        TextView Phone=null;
        View view;
        Furit furit=getItem(position);
        if (convertView==null){
            view=LayoutInflater.from(getContext()).inflate(itemID,parent,false);
            Name=view.findViewById(R.id.main_listview_item_name);
            Phone=view.findViewById(R.id.main_listview_item_phone);
        }else {
            view=convertView;
        }
        Name.setText(furit.getName());
        Phone.setText(furit.getPhone());
        return view;
    }
}

ListView子元素布局如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:orientation="vertical"
    >
    <TextView
        android:id="@+id/main_listview_item_name"
        android:layout_marginLeft="30dp"
        android:textSize="30dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="name"/>
    <TextView
        android:id="@+id/main_listview_item_phone"
        android:textSize="30dp"
        android:layout_marginLeft="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Phone"></TextView>
</LinearLayout>

然后我们在MainActivity的布局项目下添加布局

代码如下

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/main_activity_listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>

然后再将准备的适配器传递给ListView

代码如下

public List<Furit> list=new ArrayList<>();
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	ListView listView=findViewById(R.id.main_activity_listview);
	 adapter=new ListViweAdapter(this,R.layout.listview_item,list);
	listView.setAdapter(adapter);
}

然后我们正式需要的功能是 启动获取电话本中的信息、点击拨打对应人电话

因此我们需要两个权限一个是电话本的获取权限、一个是拨打电话的权限

前往AndroidManifest.xml文件进行权限声明首先,不然程序会报错

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>

然后前往MainActivity中的初始方法中对电话本的获取权限进行一个检测

代码如下

// 查询权限是否获取
        if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
	// 检测到权限未被授予 请求用户授予权限
	ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
	}else {
         // 用于查询将查询到的信息 添加给适配器
    	readContacts();
	}

然后重写onRequestPermissionsResult这个方法 授权后不管最结果都会执行这个方法

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode){
        case 1:
            if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
	    readContacts();
	}else {
        Toast.makeText(this,"授权失败,请授权后再查询!",Toast.LENGTH_SHORT).show();
    }
        break;
    }
}

完成后 就可以撰写readContacts方 用于获取联系人信息

private void readContacts(){
    // 构建一个cursor对象 
    Cursor cursor=null;
    // 电话本已经提供了一个用于获取信息的uri 对象
    Uri uri= ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
    try {
        // 该对象用于执行 Content Resolver中的查询操作 传入五个值
        cursor=getContentResolver().query(uri,null,null,null,null);
        if (cursor!=null){
            // 移动游标 没查询完一行向下移动一次
            while(cursor.moveToNext()){
               @SuppressLint("Range") String                    UserName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));// 获取名字
               @SuppressLint("Range") String UserPhone=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));// 获取信息
                // 传入list中
                Furit furit=new Furit(UserName,UserPhone);
                list.add(furit);
            }
            // 刷新ListView
            adapter.notifyDataSetChanged();
        }
    }catch (Exception e){
        Toast.makeText(this,"信息获取异常",Toast.LENGTH_SHORT).show();
    }finally {
        if (cursor!=null){
            // 关闭 Cursor
            cursor.close();
        }
    }
}

完成之后我们再写一给 点击拨号的功能、

回到 MainActivity中的初始方法onCreate中

// 创建ListView 子元素点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        // Phone 为全局变量
        phone=list.get(position).getPhone();
        Toast.makeText(MainActivity.this, phone, Toast.LENGTH_SHORT).show();
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
            // 检测到权限未被授予 请求用户授予权限
            ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},2);
        }else {
            Intent intent=new Intent(Intent.ACTION_CALL);

            intent.setData(Uri.parse("tel:"+phone));

            startActivity(intent);
        }
    }
});

然后回到 onRequestPermissionsResult方法中的 Switch 添加一个请求码即可

case 2:
	if (grantResults.length>0&&grantResults[1]==PackageManager.PERMISSION_GRANTED){
	    try {
	        Toast.makeText(this,grantResults[0]+"",Toast.LENGTH_SHORT).show();
	        Log.d(TAG,grantResults[1]+"" );
	        Log.d(TAG,grantResults[0]+"" );
	        Intent intent=new Intent(Intent.ACTION_CALL);

	        intent.setData(Uri.parse("tel:"+phone));

	        startActivity(intent);

	    }catch (Exception e){
	        Toast.makeText(this,"回调执行失败!",Toast.LENGTH_SHORT).show();
	    }

	}else {
	    Toast.makeText(this,"授权失败,请授权后再查询!",Toast.LENGTH_SHORT).show();
	}
	break;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值