生活在继续 生活会越来越好 努力过后必将会有回报 在这个过程中 最重要的保持着永远激昂的心 在枯燥 在困难时 仍然能保持 那份学习的心 不断进步 不断努力
关于 android 相机的 功能
首先 拍照 的流程 时
点击拍照按钮 ————跳转到拍照的activity————点击拍摄按钮 ———— 跳转到显示照片的页面 大概是这个流程
先看下布局文件 这个是camera1 camera2 参考网址 :https://www.jianshu.com/p/73fed068a795
camera1 适用 android5.0以下的版本 camera2 适用于 androidsdk 5.0及以上的
代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" //使用相对布局 来进行排布
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.left_activity.LandForm.CameraLandFormActivity">
<FrameLayout 帧布局
https://www.jianshu.com/p/d0b49d00dc7f,https://www.jianshu.com/p/465f645adac0
android:id="@+id/mSurfaceViewLayout"
android:layout_width="match_parent" //
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="?actionBarSize" />
<!-- layout_alignparentright参数值为true或false,true表示采用贴紧父元素的右边缘的方式布局,false表示无效,不采用贴紧父元素的右边缘的方式。-->
<!-- android:layout_centerInparent 相对于父元素完全居中-->
<!-- android:layout_alignParentBottom 贴紧父元素的下边缘-->
<!-- android:layout_alignParentLeft 贴紧父元素的左边缘-->
<!-- android:layout_alignParentRight 贴紧父元素的右边缘-->
<!-- android:layout_alignParentTop 贴紧父元素的上边缘-->
//Android中所有的控件都具有这两个属性,可选值有3种:match_parent、fill_parent、wrap_content.
//其中match_parent和fill_parent的意义相同,但官方更推荐match_parent.
//match_parent表示让当前控件的大小和父布局的大小一样,也就是由父布局来决定当前控件的大小
//wrap_content表示让当前的控件大小能够刚好包含里面的内容,也就是由控件内容决定当前控件的大小
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/takePictureFrameWidth"
android:layout_alignParentBottom="true"
android:layout_centerVertical="true"
android:background="@android:color/black">
<ImageButton //拍照 按钮
android:id="@+id/ibTakePhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:src="@drawable/camera_capture" />
</FrameLayout>
<ImageButton
android:id="@+id/ibBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="?android:listPreferredItemPaddingStart"
android:background="@android:color/transparent"
android:src="@drawable/ib_knight_return" />
<com.zsch.androidlib.view.LVCircularRing //固定的視覺特效插件 就是 等待的 圆圈效果
android:id="@+id/ivLoading"
android:layout_width="@dimen/circle_ring_width"
android:layout_height="@dimen/circle_ring_width"
android:layout_centerInParent="true"
android:visibility="gone" />
<ImageView
android:id="@+id/mCompassView" //我們公司軟件的特定的 方向罗盘的
android:layout_width="@dimen/compass_size"
android:layout_height="@dimen/compass_size"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="@color/transparent"
android:scaleType="fitCenter"
android:src="@mipmap/bg_compasspointer" />
</RelativeLayout>
activity代码
package com.zsch.forestinventory.activity.left_activity.LandForm;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
import com.zsch.androidlib.activity.BaseActivity;
import com.zsch.androidlib.engine.CameraManager;
import com.zsch.androidlib.ui.MySurfaceView;
import com.zsch.androidlib.utils.AppConstants;
import com.zsch.androidlib.view.LVCircularRing;
import com.zsch.forestinventory.MyApplication;
import com.zsch.forestinventory.R;
import com.zsch.forestinventory.activity.MainActivity;
import com.zsch.forestinventory.db.gen.DaoSession;
import com.zsch.forestinventory.db.gen.FieldValidationDao;
import com.zsch.forestinventory.entity.landforms.FieldValidation;
import com.zsch.forestinventory.entity.landforms.LandForm;
import com.zsch.forestinventory.entity.outdoor_scene.Image;
import com.zsch.forestinventory.entity.outdoor_scene.OutdoorScene;
import com.zsch.forestinventory.entity.outdoor_scene.SceneGeoPoint;
import com.zsch.forestinventory.entity.outdoor_scene.ScenePoint;
import com.zsch.forestinventory.utils.DateUtils;
import org.osmdroid.util.GeoPoint;
import java.io.File;
import java.util.Calendar;
import java.util.List;
/**
* Created by TinySun on 2017/10/17.
* 拍照用的Activity
*/
public class CameraFieldValidationActivity extends BaseActivity implements View.OnClickListener, CameraManager.IPhotoSaveStateListener, SensorEventListener {
//BaseActivity 其实 为了 修改原本 activity的 进程顺序 自己定义一个 方便 理解和使用
//举例 BaseActivity中的 顺序为
//initVariables();
//initView(savedInstanceState);
//loadData()
//所以 activity中按照这个顺序
public static final String PROJECT_NAME_ID = "projectNameId"; //这里是intent的显示传递 将数据 进行activity间的传递
public static final String IMAGE_TYPE_ID= "imagetype_id";
public static final String LAND_FROM_ID = "landFromId"; //新增地质点id 将场景点去除 这里开始接受 mainactivity 传来的 项目名 和 地质点id
public static final String LOCATION_MAP ="location_map"; //新增拍摄平面图
public static final String REMOTE_SENSING_IMAGE ="remote_sensing_image"; //新增拍摄遥感照片
public static final String ROR_PHOTO ="for_photo"; //新增拍摄举例照片
public static final String FIELDVALIDATION_ID = "fieldValidationId"; //新增验证点id
public static final String IMG_ID = "imgId";
private MySurfaceView mSurfaceView; //非常重要的android控件 用于相机预览
private FrameLayout mSurfaceViewLayout;
private ImageButton ibTakePhoto;//拍照按钮
private ImageButton ibBack;//返回按钮
private LVCircularRing ivLoading;
private ImageView mCompassView; //指南针视图
private CameraManager mCameraManager;
private Camera mCamera;
//private OrientManager mOrientManager;
private float azimuth;
private float targetAzimuth;
private boolean isCompassActive = false;
private DaoSession mSession;
private ScenePoint scenePoint;
private LandForm LandformsImages; //新增 实体LanfForm的
private FieldValidation FieldValidationsImages; //新增 实体对像FieldValidation的
private String projectName;
private String imagetype_id;
private String savePath;
private String location_map;//新增拍摄平面图
private String remote_sensing_image;//新增拍摄遥感照片
private String for_photo;//新增拍摄遥感照片
private String fieldValidationId;//新增验证点id
SensorManager mSensorManager;
private Sensor accelerometer;//加速度传感器
private Sensor magnetic;//地磁传感器
float currentDegree = 0f;
private float[] accelerometerValues = new float[3];
private float[] magneticFieldValues = new float[3];
@Override
protected void initVariables() { //初始化变量
long landFromId = getIntent().getLongExtra(LAND_FROM_ID, -1L); // 这里开始接受 mainactivity 传来的地质点id
projectName = getIntent().getStringExtra(PROJECT_NAME_ID); //获取传过来的项目名称 这个 必须得有 以后再写 目前 先做别的
imagetype_id=getIntent().getStringExtra(IMAGE_TYPE_ID);//获取传过来的imagetype_id
location_map=getIntent().getStringExtra(LOCATION_MAP);//获取传过来的平面图id
//解析传送过来的 数据 根据不同的 数据类型 去接收 根据上面 解析了 String和long
remote_sensing_image=getIntent().getStringExtra(REMOTE_SENSING_IMAGE);//获取传过来的拍摄遥感照片
for_photo=getIntent().getStringExtra(ROR_PHOTO);//获取传过来的举例照片
long fieldValidationId= getIntent().getLongExtra(FIELDVALIDATION_ID, -1L);//获取传过来的验证点id
if (fieldValidationId < 0)
onBackPressed();
mSession = ((MyApplication) getApplication()).getSession(); //实例化 在MyApplication中 将 数据库 连接
//关于MyApplication的理解和使用
//https://blog.csdn.net/ShanYu1198124123/article/details/52443576?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-13&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-13
//SQLiteOpenHelper与SQLiteDatabase的使用
//https://www.cnblogs.com/joahyau/p/6680012.html
//例子 获取User对象时立即执行查询操作获取Friend对象 获取更深层的对象 1对1 表连接 通过这个对象获取 另一个表的对象
// LandformsImages = mSession.getLandFormDao().loadDeep(landFromId); //1.通过传过来的id查两个表这两表一对一连接 一起的数据 loadDeep()仅限于1对1表的数据 一起查询出来
//参考文献 https://www.jianshu.com/p/6a70e57a9e8c
FieldValidationsImages=mSession.getFieldValidationDao().queryBuilder().where(FieldValidationDao.Properties.Id.eq(fieldValidationId)).unique();
//查询 数据 greendao的 查询 以上根据id去查询
savePath = AppConstants.APP_DIR + projectName + AppConstants.IMAGE_FILE; //照片的保存路径 APP_DIR为程序路径 //照片的保存路径 APP_DIR为程序路径
mCameraManager = CameraManager.getInstance(); //CameraManager实例化
mCamera = mCameraManager.getCamera(); //获得照相机对象
if (mCamera != null) { //判断有无照相机
Camera.Parameters parameters = mCamera.getParameters(); //获取照相参数
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
if (sizes.get(0).width > sizes.get(sizes.size() - 1).width) {
parameters.setPictureSize(sizes.get(0).width, sizes.get(0).height); //这里通过判断有无照相机镜头 去判断
} else {
parameters.setPictureSize(sizes.get(sizes.size() - 1).width, sizes.get(sizes.size() - 1).height);
}
CameraManager.setCameraDisplayOrientation(this, 0, mCamera);
mCamera.setParameters(parameters);
}
}
@Override
protected void initView(Bundle savedInstanceState) {
setContentView(R.layout.activity_camera_land_form);
mSurfaceViewLayout = (FrameLayout) findViewById(R.id.mSurfaceViewLayout);
ibTakePhoto = (ImageButton) findViewById(R.id.ibTakePhoto);
ibBack = (ImageButton) findViewById(R.id.ibBack);
ivLoading = (LVCircularRing) findViewById(R.id.ivLoading);
mCompassView = (ImageView) findViewById(R.id.mCompassView);
ibTakePhoto.setOnClickListener(this);
ibBack.setOnClickListener(this);
mCompassView.setOnClickListener(this);
ivLoading.setViewColor(getResources().getColor(R.color.colorProgressBarDefaultWheel));
ivLoading.setBarColor(getResources().getColor(R.color.colorProgressBarWheel));
// 实例化管理者
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
//实例化加速度传感器
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//实例化地磁传感器
magnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
calculateOrientation();//通过上面的参数 计算位置
}
@Override
protected void loadData() { //加载一段HTML代码片段
if (mCamera != null) {
mSurfaceView = new MySurfaceView(this, mCamera);
mSurfaceViewLayout.addView(mSurfaceView);
}
isCompassActive = true;
}
@Override
protected void onResume() { //暂停后继续
if (mSurfaceView != null) {
mCamera.startPreview();
}
// 注册监听事件
mSensorManager.registerListener(this,accelerometer,SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this,magnetic,SensorManager.SENSOR_DELAY_GAME);
super.onResume();
}
@Override
protected void onPause() { //暂停后
super.onPause();
mSensorManager.unregisterListener(this); //注销监听器
if (mSurfaceView != null) {
mCamera.stopPreview();
}
}
@Override
protected void onDestroy() { //销毁
isCompassActive = false;
mSensorManager.unregisterListener(this);
if (mCamera != null) {
mCameraManager.releaseCamera();
mCamera = null;
mSurfaceView = null;
}
super.onDestroy();
}
boolean isRotate = true;//是否旋转
@Override
public void onClick(View view) { //点击触发事件
switch (view.getId()) {
case R.id.ibTakePhoto://拍照按钮
this.targetAzimuth = this.azimuth;
isRotate = false;
takePhoto(); //执行拍照事件
break;
case R.id.mCompassView://罗盘方位角按钮
if(isRotate){
isRotate = false;
this.targetAzimuth = this.azimuth;
}else{
isRotate = true;
}
break;
case R.id.ibBack://返回按钮
onBackPressed();
break;
}
}
// 需要改需要改需要改需要改需要改需要改需要改需要改需要改需要改需要改需要改需要改
@Override
public void onBackPressed() {//返回按键
Intent data = new Intent();
if (FieldValidationsImages != null) {
data.putExtra(FIELDVALIDATION_ID, FieldValidationsImages.getId().longValue()); //scenePoint不为空 则把地质点id传回去
setResult(RESULT_OK, data);
} else {
setResult(RESULT_CANCELED); //RESULT_CANCELED = 0;传回去
}
finish();
}
/**
*
*/
private void takePhoto() { //拍照事件
isCompassActive = false; //指南针 活动
showLoading(true); //表示 执行 showloading
mCameraManager.takePicture(savePath, generatePictureName(), this); //保存照片到sd卡 调用了CameraaMangager的takePicture()方法
}
@Override
public void hasSavePhoto(String path) { //CameraManager的方法并进行重写
Image image = null;
File file = new File(path);
image = new Image();
image.setName(file.getName());
image.setAzimuth(this.targetAzimuth);
image.setProjectName(projectName);
image.setMScenePointId(FieldValidationsImages.getId()); //存储scene id mScenePointId 这里存储地质点id
//判断 remote_sensing_image和 for_photo 遥感照片或举例照片不为空的 进行插入
if(remote_sensing_image!=null){ //说明传过来的是遥感照片
image.setImage_type(remote_sensing_image);
}else{ //这里填 for_photo
image.setImage_type(for_photo);}//说明传过来的是举例照片
mSession.insert(image); //这步照片的id以及名字 存入数据库 将id类的相关名字
notifySystemToScan(file); //其实就是通知系统创建或者删除了某个文件,系统需要扫描sd卡,进行更新
Intent data = new Intent(); //intent方法 将数据 putExtra 上 用 setResult返回 因为 之前activity使用 stratartforresult传过来的
data.putExtra(FIELDVALIDATION_ID, image.getMScenePointId().longValue()); //将地质点id返回到主页面里
data.putExtra(IMG_ID, image.getId().longValue());//将照片id返回
data.putExtra(IMAGE_TYPE_ID, image.getImage_type());//将照片类型id返回 可以是平面图 也可以是照片
data.putExtra(PROJECT_NAME_ID,projectName);//将项目名称返回 表中无项目名 利用id就行 需要项目名称 实景里面可以 这里 不可以 会出现问题
setResult(RESULT_OK, data); //这里是将图片id 和实景id 返回 之前 应该是 mainactivity 返回后 进行判断 onActivityResult()方法里有
//y由于我不是打开新的网页 所以只需要imagetype就行了 返回ok就证明招牌存储成功
isCompassActive = true;
runOnUiThread(new Runnable() {
@Override
public void run() { //非主线程中更新ui的问题
CameraFieldValidationActivity.this.showLoading(false);
CameraFieldValidationActivity.this.finish();
}
});
}
private void showLoading(boolean show) { //拍照后 showLoading 这个代表了 拍照后的示意图效果
if (show) {
if (ivLoading.getVisibility() == View.VISIBLE) return;
ivLoading.setVisibility(View.VISIBLE);
ivLoading.startAnim(1000);
} else {
if (ivLoading.getVisibility() == View.INVISIBLE) return;
ivLoading.setVisibility(View.GONE);
ivLoading.stopAnim();
}
}
private String generatePictureName() { //takePicture()里面获取照片名称 的修饰方法 生成 利用 uuid去生成
Calendar calendar = Calendar.getInstance(); //日历类 这里初始化日历对象
StringBuffer buffer = new StringBuffer(); //字符串存储类
buffer.append("P");
String year = calendar.get(Calendar.YEAR) + "";
buffer.append(year.substring(2,4));
int month = calendar.get(Calendar.MONTH) + 1;
if (month < 10) {
buffer.append("0");
}
buffer.append(month);
int day = calendar.get(Calendar.DAY_OF_MONTH);
if (day < 10) {
buffer.append("0");
}
buffer.append(day);
buffer.append(randomString());//添加随机字母
buffer.append(AppConstants.IMAGE_SUFFIX);//添加后缀
return buffer.toString();
}
/**
* 生成随机字符串,使用UUID 使用 uuid 的范例
* @return
*/
public String randomString() {
String str = java.util.UUID.randomUUID().toString();
String text = str.substring(0,8) + str.substring(str.length() - 8,str.length());
return text;
}
/**
* 场景点初始化
* @param mSession
* @param scene
* @param point
* @return
*/ //初始化存储点 并将点的属性存储
public static ScenePoint generateScencePoint(DaoSession mSession, OutdoorScene scene, GeoPoint point) {
SceneGeoPoint sceneGeoPoint = new SceneGeoPoint(point);//坐标点
mSession.insert(sceneGeoPoint);
ScenePoint scenePoint = new ScenePoint();//场景点
scenePoint.setOutdoorSceneId(scene.getId());
scenePoint.setPoint(sceneGeoPoint);
scenePoint.setTime(DateUtils.getTime());
scenePoint.setInvestigator(MainActivity.currentProject.getInvestigator());
scenePoint.setDeviceId(MainActivity.currentProject.getDeviceId());
mSession.insert(scenePoint);
return scenePoint;
}
private float calculateOrientation(){ // 通过上面的参数 计算位置
float[] values = new float[3];
float[] R = new float[9];
SensorManager.getRotationMatrix(R, null, accelerometerValues,
magneticFieldValues);
SensorManager.getOrientation(R, values);
values[0] = (float) Math.toDegrees(values[0]);
return values[0];
}
@Override
public void onSensorChanged(SensorEvent event){
if (!isCompassActive) return;
if(!isRotate){//如果停止旋转,
return;
}
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
accelerometerValues = event.values;
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticFieldValues = event.values;
}
float degree = calculateOrientation();
/* RotateAnimation类:旋转变化动画类
fromDegrees:旋转的开始角度。
toDegrees:旋转的结束角度。
pivotXType:X轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotXValue:X坐标的伸缩值。
pivotYType:Y轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotYValue:Y坐标的伸缩值
*/
RotateAnimation ra = new RotateAnimation(currentDegree,-degree, Animation.RELATIVE_TO_SELF,0.5f,
Animation.RELATIVE_TO_SELF,0.5f);
//ra.setDuration(200); //旋转过程持续时间
ra.setFillAfter(true);//动画执行完后是否停留在执行完的状态
mCompassView.startAnimation(ra);
currentDegree=-degree;
if(degree < 0){
this.azimuth = degree + 360 + 90;
if(this.azimuth > 360){
this.azimuth = this.azimuth - 360;
}
}else{
this.azimuth = degree + 90;
}
}
@Override
public void onAccuracyChanged(Sensor sensor,int accuracy){
}
public void notifySystemToScan(File file) { //其实就是通知系统创建或者删除了某个文件,系统需要扫描sd卡,进行更新
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(file);
intent.setData(uri);
sendBroadcast(intent);
}
}