카메라 사진 위에 그림이나 글자를 넣어 찍고 싶다면??
의외로 간단하다... 아래를 보시라~
사진을 찍을려면 먼저 카메라를 연동해야 한다.
이부분은 비록 복잡해 보이지만 어차피 안드로이드 샘플 예제에 다 나오는 내용이다.
그런데 룰리팝 이후로 카메라 연동방법이 바뀌는 바람에 다시 삽질좀 하며 작성했다ㅎㅎ
이글의 주제는 카메라 연동이 아니니 참고만... 설명도 생략...
private CameraManager mCameraManager;
private CameraDevice mCameraDevice;
private Camera mCamera;
private Size mPreviewSize;
private CaptureRequest.Builder mPreviewBuilder, mPictureBuilder;
private CameraCaptureSession mCaptureSession;
private ImageReader mImageReader;
private void createTextureView() {
TextureView view = findViewById(R.id.TextureView);
mCameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
view.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
closeCamera();
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
private void openCamera() {
try {
String[] ids = mCameraManager.getCameraIdList();
int length = ids.length;
for (int i = 0; i < length; i++) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(ids[i]);
if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[i];
Size size = map.getOutputSizes(ImageFormat.JPEG)[i];
ImageReader reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), ImageFormat.JPEG, 1);
reader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
ByteBuffer buffer = reader.acquireLatestImage().getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
//여기서 사진데이터를 받아
이미지를만듬
makeImage(bytes);
}
}, mPictureHandler);
mImageReader = reader;
mCameraManager.openCamera(ids[i], mCameraStateCallback, null);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private CameraDevice.StateCallback mCameraStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface surface = new Surface(texture);
CaptureRequest.Builder builder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(surface);
mPreviewBuilder = builder;
camera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
mCaptureSession = session;
setPreview(session);
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onDisconnected(CameraDevice camera) {
}
@Override
public void onError(CameraDevice camera, int error) {
}
};
private void closeCamera() {
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
if (mImageReader != null) {
mImageReader.close();
mImageReader = null;
}
}
private void setPreview(CameraCaptureSession session) {
mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
HandlerThread thread = new HandlerThread("CameraPreview");
thread.start();
Handler backgroundHandler = new Handler(thread.getLooper());
try {
session.setRepeatingRequest(mPreviewBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
찍은 사진데이터를 비트맵으로 변경하면서 그 위에 스킨을 넣는 부분이다.
그냥 스킨이 되는 레이아웃뷰들의 캡쳐이미지를 가져와 사진이미지에 덮어주기만 하면된다.
이 기본원리로 다양한 응용이 가능할 것이다...
private Bitmap makeImage(byte[] datas) {
//카메라 사진데이터를 이용해 비트맵이미지를 만든다
Bitmap cameraimage;
try {
cameraimage = BitmapFactory.decodeByteArray(datas, 0, datas.length);
} catch (OutOfMemoryError e) {
//보통 사진데이터가 크기 때문에 메모리오류가 나면 크기를 줄인다
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
cameraimage = BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
}
if (cameraimage != null) {
//이미지나 글자가 포함된 레이아웃의 갭쳐이미지를 불려온다
View view = findViewById(R.id.SkinLayout);
view.setDrawingCacheEnabled(true);
Bitmap skinimage = view.getDrawingCache();
//세로모드일때는 카메라 사진을 회전시켜준다(기종에 따라 다를수 있다)
Matrix matrix = new Matrix();
matrix.setRotate(90);
cameraimage = Bitmap.createBitmap(cameraimage, 0, 0, cameraimage.getWidth(), cameraimage.getHeight(), matrix, false);
//카메라 사진위에 레이아웃 갭쳐이미지를 덮어 그린다
Canvas canvas = new Canvas(cameraimage);
canvas.drawBitmap(skinimage, 0, 0, null);
skinimage.recycle();
return cameraimage;
}
return null;
}
XML은 카메라뷰 위에 스킨레이아웃을 겹친다
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<TextureView android:id="@+id/TextureView"
android:layout_width="match_parent" android:layout_height="match_parent"/>
<LinearLayout android:id="@+id/SkinLayout"
android:layout_width="match_parent" android:layout_height="match_parent">
<!-- 여기에 이미지나 글자를 -->
</LinearLayout>
</RelativeLayout>
도움이 되셨다면~ 정성으로 빚은 저희 앱! 많은 이용 바래요:)
https://meorimal.com/index.html?tab=spaceship
https://meorimal.com/subway.html
'개발 > android' 카테고리의 다른 글
재귀 함수의 완벽한 이해 (0) | 2016.09.26 |
---|---|
TimePicker, DatePicker, NumberPicker의 폰트 바꾸기 (0) | 2016.07.18 |
초간단 꺽은선 그래프 만들기 (4) | 2016.04.25 |
버튼의 Selector 이미지를 간절히 가져오고 싶다면? (0) | 2016.03.28 |
일반뷰를 맵뷰처럼 밀어서 스크롤 시키기 (2) | 2016.02.29 |