构建自己的Camera应用程序

上一篇文章,我们学习了调用系统内置的Camera应用程序,并对图片做了一些处理,但是没有太多的灵活性。比如:我们希望延迟拍摄,简单的调用系统照相机,则不能实现。

下面我们来探讨如何利用底层的Camera类来构建一个照相应用程序,并学习如何利用所提供的功能。

Camera类的使用

1.首先在配置文件中添加Camera权限

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

在开启摄像头之前,我们先获得取景器预览头像Surface。Surface是Android中的一个抽象类,表示绘制图形或图像的位置。绘图Surface主要用到SurfaceView这个类;

2.在布局中引入该控件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <SurfaceView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

3.在代码中找到 SurfaceView控件,为他添加一个SurfaceHolder类,SurfaceHolder是Surface上的一个监控器,并给我们提供回调接口(接口中方法创建、销毁、更改等),我们可以在接口中处理我们的业务逻辑

<pre name="code" class="java"><span style="white-space:pre">	</span>SurfaceView <span style="font-family: Arial, Helvetica, sans-serif;">surfaceView=(SurfaceView) findViewById(R.id.camera_view);</span>
<span style="white-space:pre">	</span>SurfaceHolder <span style="font-family: Arial, Helvetica, sans-serif;">holder=surfaceView.getHolder();</span>

 
<span style="white-space:pre">	</span>/*
 <span style="white-space:pre">	</span>* 设置Surface是一个推送类型的Surface,意味着在Surface本身的外部维持绘图缓冲区,
 <span style="white-space:pre">	</span>* 该缓冲区由Camera管理,推送类型的surface是camera预览所需的surface
 <span style="white-space:pre">	</span>*/
<span style="white-space:pre">	</span>holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
<span style="white-space:pre">	</span>//实现SurfaceHolder.CallBack接口,从而在surface发生变化时获得通知
<span style="white-space:pre">	</span>holder.addCallback(this);
<span style="white-space:pre">	</span>/**
	 * 实现SurfaceHolder.CallBack接口回调方法
	 */
	@Override
	public void surfaceCreated(SurfaceHolder holder) {}
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {}

活动和surface已经建立了,下面我们开始使用Camera对象

当活动创建的时候我们应该获得Camera对象,那么我们怎样知道获得已经创建了呢?

上面我们已经为活动添加了一个监听,并实现了回调surfaceCreated()执行时代表活动以创建

@Override
	public void surfaceCreated(SurfaceHolder holder) {
		//活动创建后,获得camera对象
		camera=Camera.open();
<span style="white-space:pre">	</span>}
接下来我们要将预览显示设置为正在使用的 SurfaceHolder 

<span style="white-space:pre">		</span>try {
<span style="white-space:pre">			</span>//给相机设置预览器holder
<span style="white-space:pre">			</span>camera.setPreviewDisplay(holder);
<span style="white-space:pre">		</span>} catch (IOException e) {
<span style="white-space:pre">			</span>//程序出现异常时我们应该释放Camera对象
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">			</span>camera.release();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>//最后启动摄像头预览
<span style="white-space:pre">		</span>camera.startPreview();


在Surface销毁的时候我们也需要释放camera对象

@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		camera.stopPreview();
		camera.release();
	}
通过以上代码,我们已经可以预览照相界面,但你会发现预览界面没放正,向左倾斜了90度;产生这种情况的原因是因为Camera假设方向是水平或横向模式,我们需要对他做一个处理,处理方法是将活动窗体设置成横屏

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

这样虽然能正确显示预览窗体,但是手机只是横屏显示,这样显然满足不了我们的需求。

下面我们来学习一种方法,当我们手机旋转的时候预览窗体依然能正确显示

这里需要用到camera类中的一个嵌套类Camera.Parameters类。这个类有一系列重要的属性和设置,可以用来改变Camera对象的运行的方式,那么这个类中的某些参数来帮助我们解决预览窗体问题,应该在创建Camera对象后设置Parameters(surfaceCreated方法中

主要思路:我们首先获得当前屏幕的方向,然后通过屏幕方向设置Camera.Parameters类的“orientation”值

//首先判断当前屏幕的方向
if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE){//横向
	//设置Camera的参数值
	parameters.set("orientation", "landscape");
	//图像应该旋转的角度(有效度数0,90,180,270)
	camera.setDisplayOrientation(0);
	/*
	* 对于2.2以上版本可用,实际上并不执行旋转,它会告知Camera对象在EXIF数据中指定该图像需要旋转的角度,
	 * 如没有设置该属性,在其他应用程序中查看图像时,可能会侧面显示
	*/
	parameters.setRotation(0);
}else if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT){//纵向
	parameters.set("orientation", "portrait");
	camera.setDisplayOrientation(90);
	parameters.setRotation(90);
}
		
这里我们实现一个照相机界面颜色效果,对应的获取器和设置器的方法是getColorEffect和setColorEffect,同时还存在一个getSupportedColorEffects方法

1.首先通过getSupportedColorEffects方法获取支持的颜色效果

2.然后遍历效果,找到我们需要的颜色效果

3.调用setColorEffect方法设置相机颜色效果

代码如下:

//获取支持的颜色效果
List<String> colorEffects=parameters.getSupportedColorEffects();
//获取集合迭代器
Iterator<String> iterator=colorEffects.iterator();
//遍历集合
while(iterator.hasNext()){
	String colorEffect=iterator.next();
	//找到我们需要的颜色效果
	if(colorEffect.equals(Camera.Parameters.EFFECT_SOLARIZE)){
	//设置颜色效果
		parameters.setColorEffect(colorEffect);
		break;
	}
}
camera.setParameters(parameters);

下面我们要改变摄像头预览的大小

和上面更改相机颜色效果一样

1.首先通过getSupportedPreviewSizes方法获取支持大小,返回Camera.Size对象

2.然后遍历支持大小,找到我们需要的最大值

3.调用setPreviewSize方法设置相机预览大小

4.告知surfaceview预览对象,设置显示大小(此步骤必须做,否则不能改变大小,还会影响预览效果)

<span style="white-space:pre">		</span>private static final int LARGEST_WIDTH=200;//我们定义显示的预览宽度
		private static final int LARGEST_HEIGHT=200;//我们定义显示的预览高度
		int bestWidth=0;
		int bestHeight=0;
		//获取设备支持的所有大小,返回Camera.Size对象
		List<Camera.Size> sizes=parameters.getSupportedPreviewSizes();
		if(sizes.size()>1){
			Iterator<Camera.Size> iterator=sizes.iterator();
			while(iterator.hasNext()){
				Camera.Size size=iterator.next();
				//获取小于我们给定指定最大值为我们应该改变的大小
				if(size.width>bestWidth&&size.width<=LARGEST_WIDTH&&
						size.height>bestHeight&&size.height<=LARGEST_HEIGHT){
					bestWidth=size.width;
					bestHeight=size.height;
				}
			}
			//设置预览显示大小
			if(bestWidth!=0&&bestHeight!=0){
				parameters.setPreviewSize(bestWidth, bestHeight);
				//告知摄像头预览对象surfaceView以该大小进行显示,如果不设置,将不会改变预览大小,预览会扭曲低质量
				surfaceView.setLayoutParams(new LinearLayout.LayoutParams(bestWidth, bestHeight));
			}
		}
		camera.setParameters(parameters);

上面我们对相机的一些参数进行了配置,下面我们就来拍一拍,并获取拍摄的照片

这里我们要用Camera类来获取拍摄的照片,调用它的takePicture方法;该方法接收3||4个参数,所有这些参数都是回调方法,takepicture方法的最简单方式是将所有的参数都设置为null。尽管能够捕获照片,但是不能获得它的引用。因此至少应该实现一种回调方法,一种最安全的回调方法是Camera.PictureCallback.onPictureTaken,它确保被调用,并且在压缩图像时被调用。我们将实现Camera.PictureCallback接口,并重写onPictureTaken方法

关于camera的其他回调方法

1.Camera.PreviewCallBack接口

它的回调方法onPreviewFrame(byte[] data,Camera camera)方法,当存在预览帧时调用该方法,可以传入保存当前图像像素的字节数组

添加回调三种方法:

setPreviewCallback(Camera.PreviewCallback)

setOneShotPreviewCallback(Camera.PreviewCallback)

setPreviewCallbackWithBuffer(Camera.PreviewCallback)

2.Camera.AutoFocusCallback接口

定义了onAutoFocus方法,方完成一个自动聚焦活动是调用它。通过传入此回调接口的一个实例,在调用Camera对象上的autoFocus方法时会触发自动聚焦

3.Camera.ErrorCallback接口

定义了onError方法,当发生一个camera错误时调用它

4.Camera.OnZoomChangeListener

定义了onZoomChange方法,当正在进行或完成平滑缩放时调用它

5.Camera.ShutterCallback

定义了onShutter方法,当捕获图像时立刻调用它



拍摄图片后并保存到图库的完整代码如下

package com.qq.mycamera;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;

import android.app.Activity;
import android.content.ContentValues;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity implements SurfaceHolder.Callback,Camera.PictureCallback,OnClickListener{

	private SurfaceView surfaceView;
	private ImageView imageView;
	private SurfaceHolder holder;
	private Camera camera;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		surfaceView=(SurfaceView) findViewById(R.id.camera_view);
		holder=surfaceView.getHolder();
		/*
		 * 设置Surface是一个推送类型的Surface,意味着在Surface本身的外部维持绘图缓冲区,
		 * 该缓冲区由Camera管理,推送类型的surface是camera预览所需的surface
		 */
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		//实现SurfaceHolder.CallBack接口,从而在surface发生变化时获得通知
		holder.addCallback(this);
		
		surfaceView.setFocusable(true);//可聚焦,默认情况下surface不可聚焦
		surfaceView.setFocusableInTouchMode(true);//设置为不禁用,触摸模式下,通常会禁用焦点
		surfaceView.setClickable(true);//可单击
		//为预览视图添加监听
		surfaceView.setOnClickListener(this);
		imageView=(ImageView) findViewById(R.id.photoIv);
	}
	/**
	 * 实现SurfaceHolder.CallBack接口回调方法
	 */
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		//活动创建后,获得camera对象
		camera=Camera.open();
		Camera.Parameters parameters=camera.getParameters();
		//首先判断当前屏幕的方向
		if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE){//横向
			//设置Camera的参数值
			parameters.set("orientation", "landscape");
			//图像应该旋转的角度(有效度数0,90,180,270)
			camera.setDisplayOrientation(0);
			/*
			 * 对于2.2以上版本可用,实际上并不执行旋转,它会告知Camera对象在EXIF数据中指定该图像需要旋转的角度,
			 * 如没有设置该属性,在其他应用程序中查看图像时,可能会侧面显示
			 */
			parameters.setRotation(0);
		}else if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT){//纵向
			parameters.set("orientation", "portrait");
			camera.setDisplayOrientation(90);
			parameters.setRotation(90);
		}
		try {
			//给相机设置预览器holder
			camera.setPreviewDisplay(holder);
		} catch (IOException e) {
			//程序出现异常时我们应该释放Camera对象
			e.printStackTrace();
			camera.release();
		}
		//最后启动摄像头预览
		camera.startPreview();
	}
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		camera.stopPreview();
		camera.release();
	}
	/**
	 * 该回调方法第一个参数:实际图像的数据的字节数组
	 * 第二个参数:捕获该图像的Camera对象的引用
	 */
	@Override
	public void onPictureTaken(byte[] data, Camera camera) {
		//获取图片的宽高
		BitmapFactory.Options options=new BitmapFactory.Options();
		options.inSampleSize=8;
		Bitmap bitmap=BitmapFactory.decodeByteArray(data, 0, data.length,options);
		imageView.setImageBitmap(bitmap);
		//将图片保存到mediaStore中
		Uri uri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues());
		try {
			//获取图片文件的流
			OutputStream os=getContentResolver().openOutputStream(uri);
			//将数据写到文件中
			os.write(data);
			os.flush();
			os.close();
			Toast.makeText(this,"照片已保存", 0).show();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			Toast.makeText(this, e.getMessage(), 0).show();
		} catch (IOException e) {
			e.printStackTrace();
			Toast.makeText(this, e.getMessage(), 0).show();
		}
		//调用takePicture方法后,调用startPreview方法可以安全的重新启动它
		camera.startPreview();
	}
	@Override
	public void onClick(View v) {
		camera.takePicture(null, null,this);
	}

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <SurfaceView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="200dp" />

    <ImageView
        android:id="@+id/photoIv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_launcher" />

</LinearLayout>























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 编写 Android 应用程序实现无线局域网络 IP 摄像头与 TP-Link 网络摄像头之间的连接是可行的。接下来,我将概括性地介绍如何实现这一过程。 首先,你需要使用 Android Studio 这样的集成开发环境来创建一个新的 Android 项目。然后,下载 TP-Link 网络摄像头官方提供的开发文档以了解其 API 和相关功能。根据文档所提供的信息,你可以开始编写和实现摄像头连接的相关代码。 在 Android 应用程序中,你需要导入一些必要的库和依赖项,以便于处理与网络摄像头通信的各种任务。例如,你可以使用 Java 的网络套接字库与摄像头建立连接,并通过 HTTP 请求来获取图像和视频流。另外,你还可以使用图像处理库来处理和展示从摄像头获取到的图像。 接下来,你可以创建一个适当的用户界面来展示摄像头实时图像和提供用户交互功能。你可以使用 Android 的标准视图控件,如 ImageView 和 Button,来实现这些功能。你还可以根据需要添加一些额外的功能,如图像捕捉、录像等。 在与 TP-Link 网络摄像头通信的过程中,你需要确保应用程序能够处理网络连接异常、超时等情况。为此,你可以使用 Java 中的异常处理机制,并在适当的地方添加错误处理代码。 最后,你需要构建并部署你的应用程序到 Android 设备上进行测试。在测试过程中,你可以检查是否成功连接并获取到摄像头返回的图像或视频流。如果发现问题,你可以根据错误日志和调试信息进行排查和修复。 总而言之,通过使用 Android Studio 和相关开发文档,你可以编写一个能够连接 TP-Link 网络摄像头的 Android 应用程序。这个应用程序可以让用户实时监视摄像头的图像,并提供一些额外的功能。 ### 回答2: 在Android平台上编写无线局域网络IP摄像头程序IPC TP-Link Camera可以通过以下步骤实现: 首先,需在Android项目中添加TP-Link官方提供的SDK库。这个库提供了访问IP摄像头的一些基本功能,如实时视频流的接收和解码、图像截取等。通过引入这个库,可以很方便地进行相关开发。 然后,需要在Android应用程序中设置相关的权限和配置。在AndroidManifest.xml文件中,可以添加一些必要的权限,例如:访问网络权限、WIFI状态权限等。另外,还可以通过代码设置TP-Link摄像头的IP地址、端口号等配置信息,以便与相应的设备进行通信。 接着,需要编写相关的代码来实现与TP-Link IP摄像头的交互。可以创建一个类来封装与摄像头的网络连接和通信逻辑。可以使用Android提供的网络API,如HttpURLConnection或OkHttp,与摄像头建立TCP或HTTP连接,并发送指令获取视频流或进行其他操作。 在接收视频流方面,可以使用SurfaceView或TextureView来展示实时视频。通过解码接收到的视频数据,并将解码后的帧渲染到界面上,可以实现实时的视频展示效果。 此外,还可以添加一些用户交互的功能,如图像截取、录像、移动镜头控制等。这些功能可以通过发送相应的指令给摄像头,并接收对应的响应来实现。 最后,在完成开发后,可以使用Android Studio等工具进行调试和打包,最终将应用程序安装到Android设备上。在设备上运行应用程序时,可以通过调用封装好的API接口,与TP-Link IP摄像头进行交互,实现所需的功能。 总之,通过添加TP-Link SDK库并编写相关代码,可以在Android上开发出一个实现无线局域网络IP摄像头功能的应用程序。以上是大致的步骤和思路,具体的实现可能会因具体情况和需求而有所差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值