Android学习笔记《第一行代码》

活动

1.Adroid应用开发特色
1)四大组件:活动(Activity)、服务(Service)、内容提供器(Content Provider)、广播接收器(Broadcast Receiver)
2)丰富的系统控件
3)SQLite数据库
4)强大的多媒体:例如音乐、视频、录音、拍照等
5)地理位置定位

2.活动状态:
1)运行状态:活动位于返回栈的栈顶。
2)暂停状态:活动不在栈顶,但是依旧可见。只有在系统内存极低的情况下,系统才会考虑回收这种活动。
3)停止状态:活动不在栈顶,并且完全不可见。系统依然会为停止状态的活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
4)销毁状态:活动从返回栈中移除后就是销毁状态。系统最倾向于回收处于这种状态的活动,从而保证手机的内存充足。

3.活动的生存周期:
1)onCreate():会在活动第一次被创建的时候调用,应该在这个方法中完成活动的初始化操作,比如加载布局,绑定事件等。
2)onStart():这个方法在活动由不可见变为可见的时候调用。
3)onResume():这个方法在活动准备好和用户进行交互的时候调用,此时的活动一定位于返回栈的栈顶,并且处于运行状态。
4)onPause():这个方法在系统准备去启动或恢复另一个活动的时候调用。通常会在这方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行一定要快,不然会影响到新的栈顶活动的使用。
5)onStop():这个方法在活动完全不可见的时候调用。他和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。
6)onDestroy():这个方法在活动被销毁之前调用,之后的活动的状态将变为销毁状态。
7)onRestart():这个方法在活动由停止状态变为运行状态的时候调用,也就是活动被重新启动了。
**4.onSaveInstanceState(Bundle outState):**此方法可以起到临时保存数据的作用。

5.活动的四种启动模式:
1)standard:系统不在乎返回栈中有这个活动,每次启动都会创建该活动的一个新的实例。
2)singleTop:如果返回栈的栈顶已经是该活动,则直接使用它,不会在创建新的活动实例。
3)singleTask:启动该活动时会在返回栈检查是否存在该活动的实例,有就直接使用,并且把这活动之上的活动统统出栈,如果没有就创建一个新的活动实例。
4)singleInstance:这个活动的会启用一个新的返回栈来管理这个活动,不管哪个应用程序来访问这个活动,都共用一个同一个返回栈,也就解决了共享活动实例的问题

**6.如何知晓当前是哪个活动:**可以写一个继承了AppCompatActivity的BaseActivity类,重写onCreat方法,加上Log.d(“BaseActivity”,getClass().getSimpleName());

7.可以写一个ActivityCollector类作为活动管理器,包含List来存放活动实例,以及add活动,remove活动,removeAll活动,以此实现一键退出程序的功能。

8.启动活动的最佳写法,《第一行代码》

广播

广播机制简介:

每个应用程序都可以对自己感兴趣的广播进行注册,这些广播可能来自系统,也可能来自于其他应用程序

广播类型:
  1. 标准广播:是一种完全异步执行的广播,广播发出之后所有的广播接收器几乎会同时接受到这条广播消息,他们之间没有任何先后顺序,效率较高但是意味着无法截断。
  2. 有序广播:是一种同步执行的广播,同一时刻只有一个广播接收器能接受到这条广播消息,当这个广播接收器中的逻辑执行完之后才会继续传播,优先级高的广播接收器可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播就无法收到广播消息了。
接受系统广播:

Android内置了很多系统级别的广播,比如开机、电池的电量变化、时间或时区发生改变等。

注册广播:
  1. 动态注册:在代码中注册
    1. 使用:

      1. 在IntentFilter中添加自己想要的action
      2. 再创建一个广播接收器
      3. 在活动中通过registerReceiver传入上面两个参数,进行注册
      4. 别忘记动态注册的广播都要在onDestroy()中unregisterReceiver取消注册
    2. 代码:

      public class TestBroadcastActivity extends AppCompatActivity {
          private IntentFilter intentFilter;
          private NetworkChangeReceiver networkChangeReceiver;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_test_broadcast);
              intentFilter = new IntentFilter();
              intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
              networkChangeReceiver = new NetworkChangeReceiver();
              registerReceiver(networkChangeReceiver,intentFilter);
          }
          class NetworkChangeReceiver extends BroadcastReceiver{
              @Override
              public void onReceive(Context context, Intent intent) {
                  ToastUtils.showMessage(getApplicationContext(),"NetworkChangeReceiver--->onReceive--->NetworkChange");
                  Log.d("TestBroadcastActivity","onReceive");
              }
          }
          @Override
          protected void onDestroy() {
              super.onDestroy();
              unregisterReceiver(networkChangeReceiver);
          }
      }
      
  2. 静态注册:
    1. 在AndroidManifest.xml中注册

    2. 可以让程序在未启动的情况下接受广播

    3. 注意:不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,如果onReceive()方法运行了较长时间而没有结束时,程序就会报错

    4. 代码

      /*申请权限*/
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
      /*注册广播*/
      <receiver
                  android:name=".BootCompleteReceiver"
                  android:enabled="true"
                  android:exported="true">
                  <intent-filter>
                      <action android:name="android.intent.action.BOOT_COMPLETED"/>
                  </intent-filter>
              </receiver>
      
自定义广播:
  1. 发送标准广播:

    将action传入Intent中,然后sendBroadcast即可发出标准广播。广播是使用Intent传递的,因此还可以在Intent中携带一些数据传递给广播接收器。

  2. 发送有序广播:
    1. 和标准广播一样,只是将sendBroadcast变为sendOrderedBroadcast
    2. 在设置的priority属性,设置优先级
    3. 可以在onReceive中调用abortBroadcast截断广播
本地广播:
  1. 概念
    1. 之前的广播都属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,我们也可以接受来自于其他任何应用程序的广播,这样很容易引起安全性的问题。
    2. 本地广播机制:广播只能够在应用程序的内部进行传递,广播接收器也只能接受来自本应用程序发出的广播。
  2. 实践
    1. 获得LocalBroadcastManager:

      localBroadcastManager = LocalBroadcastManager.getInstance(MyLocalBroadcastReceiverActivity.this);
      
    2. 别的和动态注册一样,唯一不同是在本地广播管理器中注册

      localBroadcastManager.registerReceiver(myLocalBroadcastReceiver,intentFilter);
      
    3. 发送广播也要在本地广播管理器中发送

      localBroadcastManager.sendBroadcast(intent);
      
    4. 最后别忘记在onDestroy方法中取消注册

      protected void onDestroy() {
              super.onDestroy();
              localBroadcastManager.unregisterReceiver(myLocalBroadcastReceiver);
          }
      
  3. 优点:
    1. 可以明确正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露
    2. 其他程序无法将广播发送到我们程序内部,因此不必担心有安全漏洞的隐患
    3. 发送本地广播比发送系统广播更加高效
通过广播来强制下线:
  1. base类(所有类都继承这个BaseActivity类):

    public class BaseActivity extends AppCompatActivity {
    
        private ForceOfflineReceiver receiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityCollector.addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
        @Override
        protected void onResume() {
            super.onResume();
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction("com.nick.example.FORCE_OFFLINE");
            receiver = new ForceOfflineReceiver();
            registerReceiver(receiver,intentFilter);
        }
        @Override
        protected void onPause() {
            super.onPause();
            if(receiver!=null){
                unregisterReceiver(receiver);
            }
            receiver = null;
        }
        class ForceOfflineReceiver extends BroadcastReceiver{
            @Override
            public void onReceive(final Context context, Intent intent) {
                Log.d("ForceOfflineReceiver","onReceive...");
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                //构建者模式
                builder.setTitle("Warning").setMessage("You are forced to be offline. Please" +
                        " try to login again...").setCancelable(false).setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCollector.removeAll();
                        context.startActivity(new Intent(context,LoginActivity.class));
                    }
                });
                builder.show();
            }
        }
    }
    
    
  2. 销毁活动管理器(自定义的)可以用来关闭所有活动

    public class ActivityCollector {
        public static List<Activity> activities = new ArrayList<>();
        public static void addActivity(Activity activity){
            activities.add(activity);
        }
        public static void removeActivity(Activity activity){
            activities.remove(activity);
        }
        public static void removeAll(){
            for(Activity activity:activities){
                if(!activity.isFinishing()){
                    activity.finish();
                }
                activities.clear();
            }
        }
    }
    
  3. 最后再Main活动中发送一个广播,由于都是继承BaseActivity的,所以在BaseActivity中接受到广播消息之后弹出一个AlertDialog,并关闭所有活动,实现强制下线功能

持久化技术

持久化技术简介:
  1. 数据持久化就是将内存中的瞬时数据保存到存储设备中
  2. Android系统主要提供了三种方式用于简单的实现数据持久化功能
    1. 文件存储:
    2. SharedPreferences存储
    3. 数据库存储
文件存储:
  1. 概念:是Adroid中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理。比较适用于存储一些简单的文本数据或二进制数据。
  2. 实现:
    1. Context类中提供了openFileOutput()方法,可以用于将数据存储到指定的文件中,接受两个参数第一个参数名,**注意:**这个参数名不能包含路径。第二个参数是文件的操作模式:MODE_PRIVATE和MODE_APPEND。
    2. MODE_PRIVATE:所写入的内容将会覆盖源文件中的内容
    3. MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件。
    4. openFileOutput()方法返回一个FileOutputStream对象
    5. openFileInput()方法返回一个FileInputStream对象
SharedPreferences存储:
  1. 概念

    1. SharedPreferences是使用键值对的方式来存储数据的
    2. SharedPreferences还支持多种不同的数据类型存储
  2. 获取SharedPreferences对象三种方法

    1. Context类中的getSharedPreferences():第一个参数文件名,第二个参数操作模式
    2. Activity类中的getPreferences():只要一个操作模式参数,他会将当前类名作为SharedPreferences的文件名
    3. PreferenceManager类中的getDefaultSharedPreferences()方法:这是一个静态方法,接受一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件
      1. 调用SharedPreferences对象的edit()方法获取SharedPreferences.Editor对象
      2. 向Editor对象中添加数据,例如putInt、putString等
      3. 调用apply()方法将添加的数据提交
  3. 代码:

    //写入
    SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
                    editor.putString("username",username);
                    editor.putString("password",password);
                    editor.apply();
    //读取
    SharedPreferences sharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
            sharedPreferences.getString("username","");
            sharedPreferences.getString("password","");
    
数据库存储:
  1. SQLite数据库存储:

    详见《第一行代码》P221-229,比较麻烦就直接用LitePal操作数据库了

  2. LitePal:
    1. 简介:

      1. LitePal是一款开源的Android数据库框架,采用对象映射(ORM)的模式
      2. 它将开发常用的一些数据库功能进行了封装
      3. 可以不用写SQL语句完成CRUD操作
      4. 将面向对象的语言和面向关系的数据库之间建立一种映射关系,就是对象关系映射了。
    2. 配置LitePal:

      1. 导入依赖

        dependencies {
        	......
            implementation 'org.litepal.android:java:3.0.0'
        }
        
      2. 在main下创建assets目录,然后创建litepal.xml文件

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

      3. 编辑litepal.xml

        <?xml version="1.0" encoding="utf-8"?>
        <litepal>
            <dbname value="BookStore"></dbname>
            <version value="1"></version>
            <list>
                <mapping class="com.nick.testlitepal.Book"></mapping>
            </list>
        </litepal>
        
      4. 在AdroidManifest.xml中配置LitePalApplication

        <application
                android:name="org.litepal.LitePalApplication"
                     ....
        
    3. 使用LitePal

      1. 配置完毕后,任意使用一次操作数据库,数据库都能创建出来
      2. 如果属性中有另外的javabean,需要在litepal.xml文件的list中配置mapping
      3. 记得要升级版本号
    4. LitePal的CRUD操作:此操作需要Javabean继承LiteSupport类

      public class Book extends LitePalSupport {
          ...
      
      1. 增加数据:

        Book book = new Book();
                       ...
                        book.save();
        
      2. 更新数据:

        1. model.isSaved()在两种情况下会返回true,一种情况是已经调用save方法去添加数据了,一种是model对象通过LitePal查询出来的数据。

        2. 直接调用如果是保存过的数据,直接调用save方法就是更新了

        3. 先写需要更新的数据到一个Book中,然后在updateAll中写类似sql的语句完成更新

          Book updateBook = new Book();
                          updateBook.setPrice(10000);
                          updateBook.updateAll("price < ?","1000");
          
        4. 想要将数据更新成默认值可以用setToDefault方法,传入相应的列名就可以了

          Book updateBook = new Book();
                          updateBook.setPrice(10000);
          			   updateBook.setToDefault("price");
                          updateBook.updateAll("price < ?","1000");
          
      3. 删除数据:

        1. isSaved()为true的数据可以直接调用delete()方法,

        2. 调用LitePal.deleteAll() 方法

          LitePal.deleteAll(Book.class,"price > ?","100");
          
        3. 如果不指定约束条件,就表明你要删除表中的所有数据

      4. 查询数据:

        1. 查询所有数据:

          List<Book> list = LitePal.findAll(Book.class);
          
        2. 定制查询功能

          List<Book> list = LitePal.select("name","author").find(Book.class);
                     list = LitePal.where("price > ?","100").find(Book.class);
          
        3. 定制查询功能都对应sql语句

          1. select:指定查询哪几列的数据
          2. where:指定查询的约束条件
          3. order:用于指定结果排序方式—例子:LitePal.order(”price desc“).find(Book.class);指定按照书价从高到低
          4. limit:用于指定查询结果的数量
          5. offset:用于指定查询结果的偏移量

运行时权限

简介:

Android 6.0系统中开始引用运行时权限,从而更好的保护了用户的隐私和安全。

没有权限直接操作的话程序会崩溃。

权限分类:
  1. 普通权限:普通权限指的是那些不会直接威胁到用户的安全和隐私的权限,这些权限系统会帮我们进行授权,不需要用户手动去操作
  2. 危险权限:可能会触及用户隐私或者对设备安全性造成影响的权限,如获取设备联系人、定位等等。危险权限必须用户手动点击授权才可以。(危险权限表见《第一行代码》P248 )
拨打电话为例子:
  1. 在AndroidManifest中申请权限

    <uses-permission android:name="android.permission.CALL_PHONE"/>
    
  2. 用ContextCompat.checkSelfPermission判断是否授权,没授权ActivityCompat.requestPermissions申请权限

                    if(ContextCompat.checkSelfPermission(MainActivity.this,
                            Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.CALL_PHONE},1);
                    }else{
                        call();
                    }
    
  3. requestPermissions申请完权限后,无论同意否都会回调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){
                        call();
                    }else{
                        Toast.makeText(getApplicationContext(),"You denied the permission",Toast.LENGTH_LONG).show();
                    }
                    break;
            }
        }
    

内容提供器

简介:
  1. 内容提供器主要用于在不同的应用程序之间实现数据共享的功能。
  2. 他提供了一套完整的机制,允许一个程序访问另一个程序的数据,同时还能保证被访数据的安全性
  3. 不同于文件存储和SharedPreferences存储的两种全局可读写操作模式,内容只对一部分数据进行共享,从而保证隐私数据不会有泄露的风险。
读取系统中联系人信息例子:
  1. 在配置文件中添加一个ListView

  2. 利用Cursor查询数据:CONTENT_URI是用来Uri.parse()方法解析出来的结果,然后对Cursor进行遍历

    cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null,null,null,null);
    
  3. 还有记得申请权限:

    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    
  4. 主要代码:

    public class MainActivity extends AppCompatActivity {
        private ArrayAdapter<String> adapter;
        private List<String> contacts = new ArrayList<>();
        private ListView lv_Contacts;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            lv_Contacts = findViewById(R.id.lv_contacts);
            adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,contacts);
            lv_Contacts.setAdapter(adapter);
    
            if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
            }else{
                readContacts();
            }
        }
    
        private void readContacts(){
            Cursor cursor = null;
            try{
                Log.d("readContacts","Try...");
                //核心
                cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null,null,null,null);
                if(cursor != null){
                    while (cursor.moveToNext()){
                        String displayName = cursor.getString(cursor.getColumnIndex
                                (ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                        String number = cursor.getString(cursor.getColumnIndex
                                (ContactsContract.CommonDataKinds.Phone.NUMBER));
                        contacts.add(displayName + ":" + number);
                    }
                    adapter.notifyDataSetChanged();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(cursor != null){
                    cursor.close();
                }
            }
        }
    
        @Override
        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(getApplicationContext(),"读取联系人信息失败...",Toast.LENGTH_LONG);
                    }
                    break;
            }
        }
    }
    
创建自己的内容提供器
  1. 写一个MyProvider继承ContentProvider,重写它的六个方法
    1. onCreate:通常会在这里完成对数据库的创建和升级,返回true表示初始化成功
    2. query:使用uri参数来确定查询哪张表,projection参数用于确定查询哪列,以及selection、selectionArgs参数用于约束查询哪些行
    3. insert:使用uri参数确定要添加到的表,待添加的数据存放在values。添加完成后返回一个表示这条新纪录的URI
    4. uodate:通过uri参数确定需要更新的数据,返回受影响的行数
    5. delete:使用uri参数确定删除哪一张表中的数据
    6. getType:根据传入的uri来返回相应的MIME类型
  2. 标准的URI写法:content://com.example.app.provider/table1
    1. 标志着期望访问com.example.app这个应用的table1表中的数据,还可以继续在后面加一个id
    2. 通配符:*:表示任意长度的任意字符 #:表示匹配任意长度的数字
    3. 再借助UriMatcher这个类可以轻松地实现匹配内容URI的功能
  3. 暂停…

通知(Notification)

简介:
  1. 当某个应用程序希望给客户发送一些提示信息,而又不在前台运行时,就可以借助通知来实现。
  2. 在手机最上方的状态栏中会显示一个通知的图标,下拉状态栏就可以看到通知的详细内容。
通知的基本使用:一定要记得应用程序要 开启通知 !!!
  1. 需要NotificationManager来对通知进行管理,可以调用getSystemService方法获取到。

    NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    
  2. 需要使用Builder构造器来创建Notification对象:

    Notification notification = new NotificationCompat.Builder(this);
    
  3. 上面只是创建了一个空的Notification对象,我们可以在build()方法之前连缀任意多的设置方法来创建一个丰富的通知

    	notification.setContentTitle("This is title")
                    .setContentText("This is text")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                            R.mipmap.ic_launcher))
                    .setPriority(100)
                    .build();
    
  4. 最后只需调用NotificationManager的notify()方法就可以让通知显示出来了

    manager.notify(1,notification);
    

    第一个参数是id,需要保证通知的每个id都是不同的,第二个参数就是Notification对象了。

  5. 设置点击之后自动取消:.setAutoCancel(true),或者用NotificationManager的cansel方法,传入通知的id即可。

  6. PedingIntent:

    1. 他和Intent很像,Intent更趋向于立即执行某个动作,而PedingIntent更倾向于在某个合适的时机去执行某个动作,所以可以简单地理解为延迟执行的Intent

    2. 根据需求来选择三种不同获得PedingIntent的实例:getActivity、getBroadcast、getService,所接收的参数都是相同的。

    3. 第一个参数是Context,第二个一般传入0,第三个是Intent对象,第四个用于确定PendingIntent的行为,通常0。

      Intent intent = new Intent(MainActivity.this,ShowActivity.class);PendingIntent pi = PendingIntent.getActivity(MainActivity.this,0,intent,0);
      
      notification.setContentIntent(pi);
      
通知进阶技巧:

通知可以设置音效,震动,显示灯的颜色以及闪烁时间,或者用系统默认的操作,注意申请权限。

通知的高级功能:注意手机需要对程序通知设置高优先级才能看到效果,不然只显示Title。

setStyle允许我们构建出丰富的文本内容,长文本、图片等

  1. 长文本:

    .setStyle(new NotificationCompat.BigTextStyle().bigText("TTTTT" +
                                "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT" +
                                "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT"))
    
  2. 图片:

    .setStyle(new NotificationCompat.BigPictureStyle()
              .bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.worktime)))
    
  3. 通知优先级:setPriority方法有五个选择

    NotificationCompat.PRIORITY_MAX、MIN、HIGH、LOW、DEFAULT

运用手机多媒体

调用摄像头拍照:
  1. XML中放一个Button和ImageView

  2. 按钮事件:

    		   //用于存放摄像头拍下的图片
    		   File outputImage = new File(getExternalCacheDir(),"output_image.jpg");
                try {
                    if(outputImage.exists()){
                        outputImage.delete();
                    }
                    outputImage.createNewFile();
                }catch (IOException e){
                    e.printStackTrace();
                }
    			//判断安卓系统版本
    			//高于Android7.0(Android7.0开始认为直接使用本地真实路径的Uri是不安全的)
                if(Build.VERSION.SDK_INT >= 24){
                    			//FileProvideer是一种特殊的内容提供器
                    //第一个参数Context对象,第二个参数是任意唯一的字符串,第三个参数是刚刚创建的File
                    image_Uri = FileProvider.getUriForFile(MainActivity.this,
                            "com.nick.operatingcamera.provide",outputImage);
                }else{
                    image_Uri = Uri.fromFile(outputImage);
                }
    			//启动相机
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    			//指定照片存储位置
                intent.putExtra(MediaStore.EXTRA_OUTPUT,image_Uri);
                startActivityForResult(intent,TAKE_PHOTO);
    
  3. 在onActivitResult()中取出Bitmap对象,放到ImageView中。

    case TAKE_PHOTO:
                        if(resultCode == RESULT_OK){
                            try {
                                //将照片显示出来
                                Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(image_Uri));
                                iv_Photo.setImageBitmap(bitmap);
                            }catch (FileNotFoundException e){
                                e.printStackTrace();
                            }
                        }
                    break;
    
  4. 记得声明权限—为了兼容低版本的Adriod系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值