在Android上,可以通过使用意图来使用设备上的某些软件。所有带有合适硬件(摄像头)的原版Android都会带有照相功能的应用程序。Camera应用程序中包含了一个意图过滤器,它使得我们可以通过意图调用系统应用,而不需要我们自己编写。意图过滤器是程序员用于指定程序能够提供某个特定功能的一种方法。在应用程序的AndroidManifest.xml文件中制定一个意图过滤器,将会告诉Android,这个应用程序将根据这个指令执行特定的任务。
Camera应用中指定了一下意图过滤器
所以我们可以通过意图调用系统照相应用程序
Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");或
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTRUE);(推荐使用)
startActivity(intent);
以上两种任选一种
虽然我们现在可以调用系统拍照程序,但是这样毫无意义,我们还应该得到它的返回数据(图片)
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTRUE);(推荐使用)
startActivityForResult(intent);
这样开启拍照程序我们能通过重写onActivityResult()方法获取返回的数据(bitmap对象....)
完整代码如下所示
package com.example.camerademo;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity implements OnClickListener{
public static final int REQUEST_CODE=0;
private Button paizhaoBt;
private ImageView photoIv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initListener() {
paizhaoBt.setOnClickListener(this);
}
private void initView() {
paizhaoBt=(Button) findViewById(R.id.paizhao);
photoIv=(ImageView) findViewById(R.id.photo_iv);
}
@Override
public void onClick(View v) {
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==RESULT_OK){
//Camera返回给我们的附加数据
Bundle bundle=data.getExtras();
//从附加值中获取bitmap对象
Bitmap bitmap=(Bitmap) bundle.get("data");
//使用setImageBitmap将照片显示在界面上
photoIv.setImageBitmap(bitmap);
}
}
}
1.图片保存
从Android1.5开始,在大多数设备上可以将一个附加值传递给触发Camera应用程序的意图,MediaStore类中的EXTRA_OUTPUT常量,这个附加值常量将以URI的方式封装图片的保存路径,将图片以文件的方式存储起来。
例如:
//获取根路径
File root=Environment.getExternalStorageDirectory();
//保存的图片文件
imageFile=new File(root, "test.jpg");
Uri uri=Uri.fromFile(imageFile);
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, REQUEST_CODE);
通过以上代码如果照相完成,图片将保存到imageFile文件中
2.图片显示
因为原始图片较大,加载到程序会耗内存,我们必须先将其压缩,Android中提供了BitmapFactory这个类对bitmap对象进行处理
例如: BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize=8;
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
photoIv.setImageBitmap(bitmap);
上诉代码是将图片大小压缩到原始图片的1/8,这种没有考虑图片的原始大小和屏幕大小,为了适应屏幕我们计算出一个压缩比率,再进行压缩
完整代码如下
package com.example.camerademo;
import java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity implements OnClickListener{
public static final int REQUEST_CODE=0;
private Button paizhaoBt;
private ImageView photoIv;
private File imageFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initListener() {
paizhaoBt.setOnClickListener(this);
}
private void initView() {
paizhaoBt=(Button) findViewById(R.id.paizhao);
photoIv=(ImageView) findViewById(R.id.photo_iv);
}
@Override
public void onClick(View v) {
//获取根路径
File root=Environment.getExternalStorageDirectory();
//保存的图片文件
imageFile=new File(root, "test.jpg");
Uri uri=Uri.fromFile(imageFile);
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==RESULT_OK){
//获取屏幕宽高
int screenWidth=getWindowManager().getDefaultDisplay().getWidth();
int screenHeight=getWindowManager().getDefaultDisplay().getHeight();
//获取图片的宽高
BitmapFactory.Options options=new BitmapFactory.Options();
//只返回图片的范围大小,不解码图片本身
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
int imgWidth=options.outWidth;
int imgHeight=options.outHeight;
//求压缩比率,取整
int widthRadio=(int) Math.ceil(imgWidth/screenWidth);
int heightRadio=(int) Math.ceil(imgHeight/screenHeight);
//那个比率大的,作为压缩比率
options.inSampleSize=widthRadio>heightRadio?widthRadio:heightRadio;
//解码图片
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
photoIv.setImageBitmap(bitmap);
}
}
}
上面我们将拍照后的图片存储到sdcard中的文件中,下面我们学习将图片存储到MediaStore中(获取图片标准路径并存储)
Android有一种在应用程序之间共享数据的方法,负责这个功能的类为内容提供者ContentProvider,内容提供者为不同类型数据的存储和检索提供了一个标准接口。
图像、音频、视频的标准内容提供者为MediaStore。MediaStore在设备上的一个标准位置存放文件,为存储和检索该文件的元数据提供便利 。什么是元数据呢?元数据是关于数据的数据,包括文件本身的数据信息,如大小名称等
和上面一样,我们要先获取图片的uri(图片存储位置)
为了获取存储图片的标准位置,首先获得MediaStore(内容提供者)的引用。使用内容解析者(ContentResolver)访问内容提供者(MediaStore)
这里我们需要传递一个特定的URI,使用的URI包含在android.provider.MediaStore.Images.Media类中的常量
1.EXTERNAL_CONTENT_URI将图片存储到外部存储器中
2.INTERNAL_CONTENT_URI将图片存储到内存中
存储图片,视频,音频等内容比较大,推荐使用第一种方式存储
由于是插入一张新图片到内容提供者所以要用insert方法
Uri uri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,new ContentValues());
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTRUE);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,uri);
在上述代码中创建了一个ContentValues对象,该对象用于保存我们在创建图片时相关联的元数据,这里我们传入了一个空对象
下面我们给ContentValues对象中添加数据,ContentValues对象存储着名值对数据
名:主要是android.provider.MediaStore.Images.Media类中的常量(这些常量位于MediaStore.MediaColumns中,由Media类实现该接口)
ContentValues values=new ContentValues(3);
values.put(Media.DIAPLAY_NAME,"TEXT");//图片名字
values.put(Media.DESCRIPTION,"TEXT");//图片描述
values.put(Media.MIME_TYPE,"image/jpeg");//文件类型
//添加一条新纪录。insert方法返回新纪录的uri
Uri uri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,values);
返回的URI,将通过意图传递给Camera应用程序,以指定该图像应该保存的位置
URI格式:content://media/external/images/media/16
下面我们怎样来找到刚刚存储到MediaStore中的图片呢?
同保存到sdcard中的图片一样,我们同样要使用到BitmapFactory这个类,来解析图片
如下:
Bitmap bitmap=BitmapFactory.decodeStream(getContentResolver().openInputStream(uri),null,options);
这里我们是通过内容解析者为图片打开一个Inputstream,然后通过BitmapFactory对流解析
那么以后我们要对图片数据进行修改怎么办呢?
内容解析者为我们提供了update方法,可以对数据进行修改更新
ContentValues values=new ContentValues(3);
values.put(Media.DIAPLAY_NAME,"hehe");//图片名字
values.put(Media.DESCRIPTION,"hehe");//图片描述
getContentResolver().update(uri,values,null,null);
完整代码如下
package com.example.camerademo;
import java.io.File;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity implements OnClickListener{
public static final int REQUEST_CODE=0;
private Button paizhaoBt;
private ImageView photoIv;
private Uri uri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initListener() {
paizhaoBt.setOnClickListener(this);
}
private void initView() {
paizhaoBt=(Button) findViewById(R.id.paizhao);
photoIv=(ImageView) findViewById(R.id.photo_iv);
}
@Override
public void onClick(View v) {
//创建ContentValues对象用于存储图片信息
ContentValues values=new ContentValues(3);
values.put(Media.DISPLAY_NAME, "test");//图片名称
values.put(Media.DESCRIPTION, "test");//图片描述
values.put(Media.MIME_TYPE, "image/jpeg");//文件类型
//获取内容解析者将图片文件存储到MediaStore中
uri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,new ContentValues());
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==RESULT_OK){
try {
//获取屏幕宽高
int screenWidth=getWindowManager().getDefaultDisplay().getWidth();
int screenHeight=getWindowManager().getDefaultDisplay().getHeight();
//获取图片的宽高
BitmapFactory.Options options=new BitmapFactory.Options();
//只返回图片的范围大小,不解码图片本身
options.inJustDecodeBounds=true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
int imgWidth=options.outWidth;
int imgHeight=options.outHeight;
//求压缩比率,取整
int widthRadio=(int) Math.ceil(imgWidth/screenWidth);
int heightRadio=(int) Math.ceil(imgHeight/screenHeight);
//那个比率大的,作为压缩比率
options.inSampleSize=widthRadio>heightRadio?widthRadio:heightRadio;
//解码图片
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
photoIv.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
上面获取图片是通过调用摄像头后获取的单张图片,当我们不需要摄像头,只需要查看我们MediaStore库中的图片,我们到底该怎么做呢?
其实内容提供者MediaStore是以数据库的形式来存储数据的
为了执行查询,我们需要用到managedQuery方法
参数说明:第一个参数,uri
第二个参数,列名称的数组
第三个参数,where子句
第四个参数,where子句参数
第五个参数,order by子句
首先我们要创建一个需要返回的列的字符串数组
String[]columns={Media.DATA,Media._ID,Media.TITLE,Media.DISPLAY_NAME};
//返回整个图库的记录,也可条件查找
Cursor cursor=managedQuery(Media.EXTERNAL_CONTENT_URI,columns,null,null,null);
返回的游标cursor中有我们当前选择的每个列的索引
String colunmIndex=cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
为了从列表中选择字段我们需要使用索引查找
if(moveToFirst()){//确保游标有效并包含数据
String imgName=cursor.getString(colunmIndex);
}
怎样获取图片内部元数据EXIF呢?什么是EXIF?
EXIF表示可交换的图像文件格式,它是在图像文件中保存元数据的一种标准方式。EXIF数据实际上是图像文件的一部分,因此当图像位置发生变化时,应保存好EXIF
Exif是一种图像文件格式,它的数据存储于JPEG格式是完全相同的,实际上Exif格式就是JPEG格式头插入了数码照片的信息,包括拍摄的光圈、快门、平衡白、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码以及GPS等。简单来说,Exif=拍摄参数+JPED。因此,可以利用任何可以查看JPEG文件的看图软件浏览Exif信息,但是并不是所有图形程序都能处理Exif信息,而自Android2.0之后,加入了对图片Exif数据的支持。
ExifInterface
在Android下,通过ExifInterface类操作图片的Exif信息,虽然这个类的名字包含Interface,但它不是一个接口,它是一个类,处于"android.media.ExifInterface"包下,是媒体库的一部分功能的实现。ExifInterface有一个构造函数,接受一个String类型的数据,此为读取图片文件的地址。
Exif数据在图片中可以理解为Key-value键值对的方式存储,一般通过如下几个方法操作:
可以看到,上面大部分方法操作了一个String类型的tag参数,此为Exif的属性,在ExifInterface中定义了一些字符串的静态常量表示这些tag值,常用如下:
ExifInterface exifInterface = new ExifInterface("/sdcard/a.jpg");
String FFNumber = exifInterface.getAttribute(ExifInterface.TAG_APERTURE);
String FDateTime = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
String FExposureTime = exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
String FFlash = exifInterface.getAttribute(ExifInterface.TAG_FLASH);
String FFocalLength = exifInterface.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
String FImageLength = exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
String FImageWidth = exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
String FISOSpeedRatings = exifInterface.getAttribute(ExifInterface.TAG_ISO);
String FMake = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
String FModel = exifInterface.getAttribute(ExifInterface.TAG_MODEL);
String FOrientation = exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
String FWhiteBalance = exifInterface.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
Exif信息在图片中以二进制的形式存储,每个字段存储的数据位数是固定的,并且tag的数量也是固定,所以我们只能操作图片Exif信息中已经存在的tag的值,并且保存的数据要依照它存储位数的限制,如果存储的数据类型错误,将会导致存储的数据可能无法正确的取出,超出位数将被截取。如无法将TAG_ORIENTATION中存储一个字符串的数据,它必须存储int类型的值,多出来的将被截取
saveAttributes()方法主要用于把内存中所有当前Exif信息保存到目标图片中,依照官方文档的解释,它是一个低效率的,它会把图片的所有Exif信息,重新依次保存到目标图片,所以推荐使用setAttribute()方法进行设置Exif信息。但是在实际应用中发现,如果仅使用setAttribute()设置Exif信息,将不会写入到目标图片中,只有在改变Exif信息后,调用saveAttribute()才可以把新的Exif写入到目标图片中。这个过程效率比较低,模拟器上会卡顿一下,但是真机测试没有这样的情况,反应很快。
用法:
// 获取图片Exif
ExifInterface exif = new ExifInterface("/sdcard/a.jpg");
// 保存指定tag的值
exif.setAttribute(strAttr,strValue);
// 把Exif信息写入目标图片
exif.saveAttributes();
需要添加以下权限
下面是一个读取手机全部图片,并修改图片的EXIF信息,并读取信息的完整代码
activity中的代码
package com.example.photoalbumdemo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.database.Cursor;
import android.media.ExifInterface;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Gallery;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
//存储手机中的所有图片地址
private List imgUrls=new ArrayList<>();
//显示图片总数
private TextView imgNumTv;
//用于展示图片
private Gallery gallery;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgNumTv=(TextView) findViewById(R.id.photoNum);
gallery=(Gallery) findViewById(R.id.gallery1);
//需要查询的图片属性数组
String[]columns={Media.DATA,Media._ID,Media.TITLE,Media.DISPLAY_NAME};
//查询出来后,返回的Cursor对象,存储着图片信息
Cursor cursor=managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
//获取图片地址在数据库中的列索引
int fileColumn=cursor.getColumnIndexOrThrow(Media.DATA);
//获取图片标题在数据库中的列索引
int titleColumn=cursor.getColumnIndexOrThrow(Media.TITLE);
//获取图片名字在数据库中的列索引
int displayColumn=cursor.getColumnIndexOrThrow(Media.DISPLAY_NAME);
//遍历游标,获取图片信息
while(cursor.moveToNext()){
String imgUrl=cursor.getString(fileColumn);//图片地址
String imgTitle=cursor.getString(titleColumn);//图片标题
String imgName=cursor.getString(displayColumn);//图片名称
Log.i("图片", imgTitle+"/n"+imgName+"/n"+imgUrl);
imgUrls.add(imgUrl);//将图片地址存储到集合
}
imgNumTv.setText("你有"+imgUrls.size()+"张图片");
//设置适配器
gallery.setAdapter(new ImageAdapter(imgUrls,this));
//设置项点击事件
gallery.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view,
int position, long id) {
try {
//获取图片EXIF信息
ExifInterface ei=new ExifInterface(imgUrls.get(position));
String description=ei.getAttribute(ExifInterface.TAG_MODEL);
Toast.makeText(MainActivity.this, description, 0).show();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
画廊适配器代码package com.example.photoalbumdemo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
public class ImageAdapter extends BaseAdapter{
//图片地址集合
private List imgUrls=new ArrayList<>();
private Context context;
private int screenWidth;//屏幕宽
private int screenHeight;//屏幕高
public ImageAdapter(List imgUrls,Context context) {
this.imgUrls=imgUrls;
this.context=context;
//获取屏幕宽高
WindowManager manager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
screenWidth=manager.getDefaultDisplay().getWidth();
screenHeight=manager.getDefaultDisplay().getHeight();
}
@Override
public int getCount() {
return imgUrls.size();
}
@Override
public Object getItem(int position) {
return imgUrls.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
//修改图片EXIF数据信息
ExifInterface ei=new ExifInterface(imgUrls.get(position));
ei.setAttribute(ExifInterface.TAG_MODEL, "2015");
ei.saveAttributes();
} catch (IOException e) {
e.printStackTrace();
}
ViewHolder holder=null;
if(convertView==null){
ImageView imageView=new ImageView(context);
convertView=imageView;
holder=new ViewHolder();
holder.imageView=imageView;
convertView.setTag(holder);
}else{
holder=(ViewHolder) convertView.getTag();
}
//压缩图片
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(imgUrls.get(position), options);
//获取图片宽高
int imgWidth=options.outWidth;
int imgHeight=options.outHeight;
//求出采样比率
int radio=imgWidth/screenWidth>imgHeight/screenHeight?imgWidth/screenWidth:imgHeight/screenHeight;
options.inJustDecodeBounds=false;
options.inSampleSize=radio;
Bitmap bitmap=BitmapFactory.decodeFile(imgUrls.get(position), options);
//图片显示
holder.imageView.setImageBitmap(bitmap);
return convertView;
}
class ViewHolder{
ImageView imageView;
}
}
注意要在配置文件中添加写权限,不然修改图片EXIF信息不成功