前の10件 1  2  3  4  5  6  7  8  9  10  11

記事一覧

2011年01月19日

Android SDKで「menu」ボタンでメニューを表示する

test124_01.png
今回は「menu」ボタンを押したときのメニュー表示を実装する。

と言ってもonCreateOptionsMenu()とonOptionsItemSelected()をオーバーライドするだけで非常に簡単。気をつけることはIDの管理ぐらいだ。
package com.Test124;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class Test124Act extends Activity
{
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}


	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		super.onCreateOptionsMenu(menu);

		int		nId = 0;

		menu.add(0,nId++,Menu.NONE,"AAA").setIcon(android.R.drawable.ic_menu_set_as);
		menu.add(0,nId++,Menu.NONE,"BBB");
		menu.add(0,nId++,Menu.NONE,"CCC").setIcon(R.drawable.icon);

		menu.add(1,nId++,Menu.NONE,R.string.app_name);	//本当はこのようにリソース文字列を使わないとダメ
		menu.add(1,nId++,Menu.NONE,"BBB1");
		menu.add(1,nId++,Menu.NONE,"CCC1");
		menu.add(1,nId++,Menu.NONE,"DDD1");

		menu.add(2,nId++,Menu.NONE,"AAA2");

		return true;
	}


	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		Log.d("Test124","MenuSelected " + item.getItemId() + " " + item.getTitle());

		switch(item.getItemId())
		{
		case 0:
			return	true;

		case 1:
			return	true;

		case 2:
			return	true;

		default:
			break;
		}

		return super.onOptionsItemSelected(item);
	}
}

test124_02.png
実際に実行して、「menu」ボタンを押すとメニューが表示された。

test124_03.png
項目が多い場合は「more」ができ、このように追加画面へと切り換えれる。




プロジェクトファイルをダウンロード

Android SDK/NDKでZIP書庫内の画像ファイルを順々に表示する

test123_01.png
今回はこれまでに実装してきたAndroid NDKでZIP書庫内の画像ファイルを解凍するAndroid SDKでJPG画像を画像に合わせて拡大縮小表示するを組み合わせて、ZIP書庫内の画像を順々に表示させるアプリを作成する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。C++、HelloJNI、STL有効とする。

test123_02.png Eclipseでプロジェクトを読み込み、まずはZIP書庫解凍用のライブラリlibunzipを使うための設定をAndroid.mkに追加する。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test123
LOCAL_SRC_FILES := Test123.cpp
LOCAL_LDLIBS    := -lz -lunzip

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

test123_03.png 次にAndroid NDKでZIP書庫内の画像ファイルを解凍するで作成したZipedImages.hと同じものを追加する。
#ifndef	_ZIPEDIMAGES_H
#define	_ZIPEDIMAGES_H

#include<string>
#include<vector>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <android/log.h>


#include "unzip.h"

#define	MAX_PATH	256

#define	bool	unsigned int
#define	true	(1)
#define	false	(0)




class	CZipedImages
{
protected:

	unzFile		_zipFile;
	std::vector<unz_file_pos>	_vFilePos;


public:

	CZipedImages()
	{
		_zipFile = 0;
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::CZipedImages()");
	}

	~CZipedImages()
	{
		Close();
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::~CZipedImages()");
	}

	bool	IsOpen(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::IsOpen()");
		return	(_zipFile != 0) ? true : false;
	}


	bool	OpenZipFile(const char* pszFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::OpenZipFile()");
		if(IsOpen() == true)
			return	false;

		int		nRet;

		_zipFile = unzOpen(pszFile);

		nRet = unzGoToFirstFile(_zipFile);
		while(nRet == UNZ_OK)
		{
			char	pszFileName[MAX_PATH];
			unz_file_info	info;
			unz_file_pos	pos;
			std::string		strFile;
			std::string		strExt;

			unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
			strFile	= pszFileName;
			strExt	= strFile.substr(strFile.length() - 4,4);		//いい加減な方法で拡張子取得

			if(strExt == ".JPG" || strExt == ".jpg" || strExt == ".PNG" || strExt == ".png")				//いい加減な方法で拡張子チェック
			{
				unzGetFilePos(_zipFile,&pos);
				_vFilePos.push_back(pos);
			}
			else
			{
				//処理されないファイル/フォルダをログ出力
				__android_log_print(ANDROID_LOG_INFO,TAG,"NOT process : (%s)",pszFileName);
			}

			nRet = unzGoToNextFile(_zipFile);
		}

		return	true;
	}


	void	Close(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Close()");

		if(_zipFile != 0)
			unzClose(_zipFile);
		_zipFile = 0;
		_vFilePos.clear();
	}



	int		GetCount(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::GetCount()");

		return	_vFilePos.size();
	}


	bool	Extract(int nIndex,const char* pszFolder,std::string* pstrFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract()");

		if(IsOpen() == false)
			return	false;

		if(nIndex > _vFilePos.size() || nIndex < 0)
			return	false;

		int		nRet;
		char	pszFileName[MAX_PATH];
		unz_file_info	info;

		nRet = unzGoToFilePos(_zipFile,&_vFilePos[nIndex]);
		if(nRet != UNZ_OK)
			return	false;

		nRet = unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
		if(nRet != UNZ_OK)
			return	false;
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() in process %s",pszFileName);


		std::string	strPath;

		//保存先ファイルパス作成
		{
			strPath = pszFolder;
			if(strPath.substr(strPath.length() - 1,1) != "/")
				strPath += "/";

			if(pstrFile != NULL && *pstrFile != "")
			{
				if((*pstrFile)[0] != '/')
					strPath += *pstrFile;
				else
					strPath += pstrFile->substr(1,pstrFile->length() - 1);
			}
			else
			{
				char	pszBuff[256];
				std::string	strExt;

				strExt	= pszFileName;
				strExt	= strExt.substr(strExt.length() - 4,4).c_str();	//いい加減な方法で拡張子取得
				sprintf(pszBuff,"%d%s",nIndex,strExt.c_str());

				if(pstrFile)
					*pstrFile = pszBuff;
				strPath.append(pszBuff);
			}
		}
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() : save to %s",strPath.c_str());

		//解凍
		{
			FILE	*fp;

			fp = fopen(strPath.c_str(),"w");
			if(fp == NULL)
				return	false;

			char	szBuffer[32768];
			int		dwSizeRead;

			nRet = unzOpenCurrentFile(_zipFile);
			if(nRet == UNZ_OK)
			{
				while ((dwSizeRead = unzReadCurrentFile(_zipFile,szBuffer,sizeof(szBuffer))) > 0)
				{
					fwrite(szBuffer,1,dwSizeRead,fp);
				}
				unzCloseCurrentFile(_zipFile);
			}
			fclose(fp);
		}

		return	true;
	}


};

#endif

test123_04.png
Javaからネイティブで実装したZIP書庫操作用の関数にアクセスできるようにJNIを実装する。CZipedImagesのメンバー関数を単純にJNIでラッピングした。これでネイティブ側の実装は終りだ。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <string.h>
#include <jni.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif

#define	TAG	"Test123"
#include "ZipedImages.h"

CZipedImages	g_cZip;


extern "C"
jint Java_com_Test123_ZipImageFileView_OpenZipFile(JNIEnv* env,jobject thiz,jstring jstrFile)
{
	bool	ret;

	const	char*	pszFile = env->GetStringUTFChars(jstrFile,NULL);
	ret = g_cZip.OpenZipFile(pszFile);
	env->ReleaseStringUTFChars(jstrFile,pszFile);

	return	(ret == true) ? 1 : 0;
}

extern "C"
jint Java_com_Test123_ZipImageFileView_GetInZipCount(JNIEnv* env,jobject thiz)
{
	return	g_cZip.GetCount();
}


extern "C"
jstring Java_com_Test123_ZipImageFileView_Extract(JNIEnv* env,jobject thiz,jint jnIndex,jstring jstrFolder)
{
	std::string		strFile;

	int		nIndex = jnIndex;
	bool	ret;

	const	char*	pszFolder = env->GetStringUTFChars(jstrFolder,NULL);
	ret = g_cZip.Extract(nIndex,pszFolder,&strFile);
	env->ReleaseStringUTFChars(jstrFolder,pszFolder);

	if(ret)
		return env->NewStringUTF(strFile.c_str());
	else
		return env->NewStringUTF("");
}


extern "C"
void Java_com_Test123_ZipImageFileView_Close(JNIEnv* env,jobject thiz)
{
	g_cZip.Close();
}

test123_05.png
次にImageFileView.javaファイルを追加して、画像ファイルを読み込みビュー表示するクラスを実装する。

基本的にAndroid SDKでJPG画像を画像に合わせて拡大縮小表示すると同じだが、画像ファイルが大きくなるとメモリが足りなくなって落ちる問題の修正などをしている。nScaleの計算が間違えているのはご愛嬌。
package com.Test123;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;


public class ImageFileView extends View
{
	protected	Bitmap	_bmpImageFile;		//画像ファイル
	protected	Bitmap	_bmpResized;		//リサイズ画像

	protected	int		_nWidthDisp;
	protected	int		_nHeightDisp;
	
	public ImageFileView(Context context)
	{
		super(context);

		//画面サイズ取得
		WindowManager	wmWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		Display			display = wmWindowManager.getDefaultDisplay();
		_nWidthDisp		= display.getWidth();
		_nHeightDisp	= display.getHeight();
	}

	//設定の保存
	public void OnSaveState(Bundle outState)
	{
		if(outState == null)
			return;
	}

	//設定の読込
	public void OnLoadState(Bundle savedInstanceState)
	{
		if(savedInstanceState == null)
			return;
	}
	


	//画像を大まかに縮小して_bmpImageFileへ読み込み、
	//表示用にリサイズして_bmpResizedへ保存する
	public boolean	LoadImageFile(String strFile,boolean bInvalidate)
	{
		//画像の解析(大きい画像が開けない対策)
		BitmapFactory.Options	options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		_bmpImageFile = BitmapFactory.decodeFile(strFile,options);
		
		int		nWidth = options.outWidth;
		int		nHeight = options.outHeight;


		int		nScale;

		//nScaleは2の乗数にする
		{
			int	nScaleX = (int)(float)(nWidth / _nWidthDisp + 0.5f);
			int	nScaleY = (int)(float)(nHeight / _nHeightDisp + 0.5f);

			nScale = (nScaleX < nScaleY) ? nScaleX : nScaleY;
			
			int		i = 1;

			while(nScale > i)
			{
				i *= 2;
			}
			nScale = i - 1;
			if(nScale <= 0)
				nScale = 1;
		}
		
		//画像の本読み込み
		options.inJustDecodeBounds = false;
		options.inSampleSize = nScale;
		_bmpImageFile = BitmapFactory.decodeFile(strFile,options);


		/////////////////////////////////
		//画像のリサイズ
		
		nWidth = _bmpImageFile.getWidth();
		nHeight = _bmpImageFile.getHeight();

		float	fScale;

		//拡大縮小率取得
		if(true)
		{
			//画像に合わせる
			if((long)nWidth * _nHeightDisp > (long)_nWidthDisp * nHeight)
				fScale = (float)_nWidthDisp / nWidth;
			else
				fScale = (float)_nHeightDisp / nHeight;
		}
		else if(false)
		{
			//高さに合わせる
			fScale = (float)_nHeightDisp / nHeight;
		}
		else
		{
			//幅に合わせる
			fScale = (float)_nWidthDisp / nWidth;
		}

		
		//リサイズ
		Matrix	matrix = new Matrix();
		matrix.postScale(fScale,fScale,0,0);
		_bmpResized = Bitmap.createBitmap(_bmpImageFile,0,0,nWidth,nHeight,matrix,true);

		if(bInvalidate)
			invalidate();		//表示更新

		return	(_bmpImageFile != null) ? true : false;
	}


	@Override
	protected void onDraw(Canvas canvas)
	{
		if(_bmpImageFile == null)
		{
			super.onDraw(canvas);
			return;
		}

		
		//描画
        Rect	rect = new Rect(0,0,_bmpResized.getWidth(),_bmpResized.getHeight());
        canvas.drawBitmap(_bmpResized,rect,rect,null);
	}

}


test123_06.png
次にImageFileView派生でZipImageFileViewクラスを作る。

ZIPファイルから画像ファイルを解凍、ImageFileViewに読み込ませ、画像ファイルを削除という単純な流れにした。
package com.Test123;

import java.io.File;

import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;


public class ZipImageFileView extends ImageFileView
{
	private	int		_nImageIndex = -1;	//現在読み込まれている画像のzip内の画像インデックス
	private	int		_nImageCount;		//zip内の画像数
	
	public ZipImageFileView(Context context)
	{
		super(context);
		
		//zipファイル読み込み
		OpenZipFile("/sdcard/desire_img.zip");
		_nImageCount = GetInZipCount();
	}

	//設定の保存
	@Override
	public void OnSaveState(Bundle outState)
	{
		super.OnSaveState(outState);
		if(outState == null)
			return;

		outState.putInt("_nImageIndex",_nImageIndex);
	}

	
	//設定の読込
	@Override
	public void OnLoadState(Bundle savedInstanceState)
	{
		super.OnLoadState(savedInstanceState);
		if(savedInstanceState == null)
			return;

		int		nIndex;
		
		nIndex = savedInstanceState.getInt("_nImageIndex");
		LoadImageFromZip(nIndex,true);
	}

	
	
	public boolean	LoadImageFromZip(int nIndex,boolean bInvalidate)
	{
		if(nIndex >= _nImageCount || _nImageCount == 0 || nIndex < 0)
			return	false;
		if(nIndex == _nImageIndex)
			return	true;				//すでに読み込まれている

		String	strFile;

		//画像解凍
		strFile = Extract(nIndex,"/sdcard/");
		if(strFile == "")
			return	false;
		strFile = "/sdcard/" + strFile;

		//画像読み込み
		LoadImageFile(strFile,bInvalidate);
		_nImageIndex = nIndex;

		//ファイル削除
		File	file = new File(strFile);
		file.delete();

		return	true;
	}

	
	
	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		//タッチイベント取得
		if(event.getAction() == MotionEvent.ACTION_DOWN)
		{
			int		nIndex;

			nIndex = _nImageIndex + 1;
			if(nIndex >= _nImageCount)
				nIndex = 0;

			LoadImageFromZip(nIndex,true);
		}
		return true;
	}	

	
	@Override
	protected void onDraw(Canvas canvas)
	{
		if(_bmpImageFile == null)
		{
			LoadImageFromZip(0,true);
			return;
		}

		super.onDraw(canvas);
	}
	
	

	private native int  OpenZipFile(String strFile);
	private native int  GetInZipCount();
	private native String  Extract(int nIndex,String strFolder);
	private native void  Close();

	static
	{
		System.loadLibrary("Test123");
	}
}

test123_07.png 最後にアクティビティに作成したビューを割り当てる。
package com.Test123;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class Test123Act extends Activity
{
	private	ImageFileView	_view;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		_view = new ZipImageFileView(this);
		setContentView(_view);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);

		if(_view != null)
			_view.OnSaveState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		
		if(_view != null)
			_view.OnLoadState(savedInstanceState);
	}
}

test123_08.png
これで実行するとZIP書庫内の画像ファイルが表示され、画面をタッチすると、、、

test123_09.png
書庫内の次の画像が表示された。

先読み処理や別スレッドでの解凍処理などをしていないため、全体的にモッサリ。実機ではまぁまぁ使えるものの、VirtualPC上のAndroidエミュレーターでは論外なほどの遅さでした。




ファイルをダウンロード

Android NDKでZIP書庫内の画像ファイルを解凍する

test122_01.png
今回はAndroid NDKにより、ZIP書庫内に含まれる画像ファイルを解凍するためのクラスを作成する。ZIP書庫の操作には「Android NDKの標準ライブラリにlibunzipを追加して利用する」で用意したlibunzipを利用する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。C++、HelloJNIでSTLを有効にした。


test122_02.png
自動生成されたプロジェクトをEclipseに取り込み、「ZipedImages.h」ファイルを新規作成してZIP書庫操作用のクラスを実装する。

指定されたZIP書庫を開き、書庫内にあるすべてのファイル名をチェックして拡張子が画像ならstd::vectorにファイル位置を保存。そしてそのstd::vector上のインデックス指定によりファイルを指定フォルダへと解凍するという流れにした。かなり中途半端かつ汎用性のない実装になっている。汎用的に実装しようと思うと、ファイル名に日本語が使われていた場合やら何やら色々考えないといけないのでやめました。
#ifndef	_ZIPEDIMAGES_H
#define	_ZIPEDIMAGES_H

#include<string>
#include<vector>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <android/log.h>


#include "unzip.h"

#define	MAX_PATH	256

#define	bool	unsigned int
#define	true	(1)
#define	false	(0)




class	CZipedImages
{
protected:

	unzFile		_zipFile;
	std::vector<unz_file_pos>	_vFilePos;


public:

	CZipedImages()
	{
		_zipFile = 0;
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::CZipedImages()");
	}

	~CZipedImages()
	{
		Close();
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::~CZipedImages()");
	}

	bool	IsOpen(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::IsOpen()");
		return	(_zipFile != 0) ? true : false;
	}


	bool	OpenZipFile(const char* pszFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::OpenZipFile()");
		if(IsOpen() == true)
			return	false;

		int		nRet;

		_zipFile = unzOpen(pszFile);

		nRet = unzGoToFirstFile(_zipFile);
		while(nRet == UNZ_OK)
		{
			char	pszFileName[MAX_PATH];
			unz_file_info	info;
			unz_file_pos	pos;
			std::string		strFile;
			std::string		strExt;

			unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
			strFile	= pszFileName;
			strExt	= strFile.substr(strFile.length() - 4,4);		//いい加減な方法で拡張子取得

			if(strExt == ".JPG" || strExt == ".jpg" || strExt == ".PNG" || strExt == ".png")				//いい加減な方法で拡張子チェック
			{
				unzGetFilePos(_zipFile,&pos);
				_vFilePos.push_back(pos);
			}
			else
			{
				//処理されないファイル/フォルダをログ出力
				__android_log_print(ANDROID_LOG_INFO,TAG,"NOT process : (%s)",pszFileName);
			}

			nRet = unzGoToNextFile(_zipFile);
		}

		return	true;
	}


	void	Close(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Close()");

		if(_zipFile != 0)
			unzClose(_zipFile);
		_zipFile = 0;
		_vFilePos.clear();
	}



	int		GetCount(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::GetCount()");

		return	_vFilePos.size();
	}


	bool	Extract(int nIndex,const char* pszFolder,std::string* pstrFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract()");

		if(IsOpen() == false)
			return	false;

		if(nIndex > _vFilePos.size() || nIndex < 0)
			return	false;

		int		nRet;
		char	pszFileName[MAX_PATH];
		unz_file_info	info;

		nRet = unzGoToFilePos(_zipFile,&_vFilePos[nIndex]);
		if(nRet != UNZ_OK)
			return	false;

		nRet = unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
		if(nRet != UNZ_OK)
			return	false;
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() in process %s",pszFileName);


		std::string	strPath;

		//保存先ファイルパス作成
		{
			strPath = pszFolder;
			if(strPath.substr(strPath.length() - 1,1) != "/")
				strPath += "/";

			if(pstrFile != NULL && *pstrFile != "")
			{
				if((*pstrFile)[0] != '/')
					strPath += *pstrFile;
				else
					strPath += pstrFile->substr(1,pstrFile->length() - 1);
			}
			else
			{
				char	pszBuff[256];
				std::string	strExt;

				strExt	= pszFileName;
				strExt	= strExt.substr(strExt.length() - 4,4).c_str();	//いい加減な方法で拡張子取得
				sprintf(pszBuff,"%d%s",nIndex,strExt.c_str());

				if(pstrFile)
					*pstrFile = pszBuff;
				strPath.append(pszBuff);
			}
		}
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() : save to %s",strPath.c_str());

		//解凍
		{
			FILE	*fp;

			fp = fopen(strPath.c_str(),"w");
			if(fp == NULL)
				return	false;

			char	szBuffer[32768];
			int		dwSizeRead;

			nRet = unzOpenCurrentFile(_zipFile);
			if(nRet == UNZ_OK)
			{
				while ((dwSizeRead = unzReadCurrentFile(_zipFile,szBuffer,sizeof(szBuffer))) > 0)
				{
					fwrite(szBuffer,1,dwSizeRead,fp);
				}
				unzCloseCurrentFile(_zipFile);
			}
			fclose(fp);
		}

		return	true;
	}
};

#endif

test122_03.png そしてメイン処理内でCZipedImaesクラスのテスト用に2つのファイルを解凍する処理を追加する。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <string.h>
#include <jni.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif


#define	TAG	"Test122"
#include "ZipedImages.h"



extern "C"
jstring Java_com_Test122_Test122Act_stringFromJNI(JNIEnv* env,jobject thiz)
{
	CZipedImages	cZip;

	cZip.OpenZipFile("/sdcard/desire_img.zip");

	bool		ret;
	int			nSize;
	std::string	strFile;

	nSize = cZip.GetCount();

	//最初のファイルを解凍
	strFile = "";
	ret = cZip.Extract(0,"/sdcard",&strFile);
	if(ret)
		__android_log_print(ANDROID_LOG_DEBUG,"Test122","save to %s",strFile.c_str());

	//5番目のファイルを解凍
	strFile = "";
	ret = cZip.Extract(5,"/sdcard",&strFile);
	if(ret)
		__android_log_print(ANDROID_LOG_DEBUG,"Test122","save to %s",strFile.c_str());

	cZip.Close();

	return env->NewStringUTF("Test122text");
}

test122_04.png 最後にAndroid.mkにlibunzipとlibzipのライブラリを追加する。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test122
LOCAL_SRC_FILES := Test122.cpp
LOCAL_LDLIBS += -lz -lunzip

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

test122_05.png
これで実行するとSDカード上のZIP書庫を読み込み、その中にあるファイルのうち2つが解凍された。




プロジェクトファイルをダウンロード

Android NDKの標準ライブラリにlibunzipを追加して利用する

libunzip01.png
今回はAndroid NDKから簡単にzip書庫ファイルにアクセスするためのライブラリ「libunzip.a」をビルド。Android NDKの標準ライブりへ登録。そして実際に利用してみる。

まずは以前作成したAndroid NDKでZIP書庫内のファイルリストを取得するのときのプロジェクトファイル「Test117.zip」をダウンロードする。

libunzip02.png
次にEasyProjectGenerator for Androidで雛形プロジェクトを作成する。今回はC++、HelloJNIを利用した。

libunzip03.png
そして作成したプロジェクトのjniフォルダの中に、先ほどダウンロードしたTest117.zipに含まれる「ioapi.h」「ioapi.c」「unzip.h」「unzip.c」の4ファイルを解凍する。

libunzip04.png
今回はライブラリをビルドするだけなので不要な「Test120.c」を削除する。

libunzip05.png
Eclipseでプロジェクトを読み込み、「Android.mk」を編集する。

今回は.aファイルを作成するのでBUILD_STATIC_LIBRARYを指定するのがポイントだ。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libunzip
LOCAL_SRC_FILES := ioapi.c unzip.c

include $(BUILD_STATIC_LIBRARY)
libunzip06.png BUILD_STATIC_LIBRARYを指定しただけでは.aファイルはビルドされないので、「Application.mk」を編集してAPP_MODULE指定をする。
APP_MODULES := libunzip

libunzip07.png
これでビルドすると「libunzip.a」ファイルが生成された。

libunzip08.png
次に「libunzip.a」をAndroid NDKの標準ライブラリとして登録してしまう。

NDKのフォルダ「C:\android-ndk\platforms\android-xxx\arch-arm\usr\lib\」を開き、その中へ「libunzip.a」をコピーする。

libunzip09.png
忘れずにヘッダーファイルの「ioapi.h」「unzip.h」の2ファイルをコピーする。これは「C:\android-ndk\platforms\android-xxx\arch-arm\usr\include\」へコピーする。これで標準ライブラリへの登録は終りだ。






libunzip10.png
最後に実際にlibunzipを利用してみる。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作成。C++のHelloJNIとした。

libunzip11.png
Eclipseでプロジェクトを読み込み、Android.mkを編集する。

libunzipを利用するため、LOCAL_LDLIBSに「-lz」と「-lunzip」を追加する。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test121
LOCAL_SRC_FILES := Test121.cpp
LOCAL_LDLIBS += -lz -lunzip 

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

libunzip12.png そしてテストするためのメイン処理を実装する。普通にunzip.hをincludeして利用するだけだ。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <string.h>
#include <jni.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif


#include <stdio.h>
#include <string.h>

#include <unzip.h>

#define	MAX_PATH	256

int		Test(const char* pszZipFile)
{
	int		nRet;
	unzFile	zipFile = unzOpen(pszZipFile);

	nRet = unzGoToFirstFile(zipFile);
	while(nRet == UNZ_OK)
	{
		unz_file_info	info;
		char	pszFileName[MAX_PATH];

		nRet = unzGetCurrentFileInfo(zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
		if(nRet == UNZ_OK)
		{
			__android_log_print(ANDROID_LOG_INFO,"Test121","Found File in zip : %s",pszFileName);
		}

		nRet = unzGoToNextFile(zipFile);
	}
	unzClose(zipFile);

	return	1;
}


extern "C"
jstring Java_com_Test121_Test121Act_stringFromJNI(JNIEnv* env,jobject thiz)
{
	Test("/sdcard/desire_img.zip");

	return env->NewStringUTF("Test121text");
}

libunzip13.png
これで実行すると、SDカード上に配置したzip書庫内のファイル一覧がログに出力された。




プロジェクトファイルをダウンロード

Android SDKでJPG画像を画像に合わせて拡大縮小表示する

test119_01.png
今回はAndroid SDKでjpgファイルを読み込み、画面サイズに合わせて拡大縮小表示する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。JavaのSimpleプロジェクトを利用した。

test119_02.png
そしてEclipseにプロジェクトを取り込み、「ImageFileView.java」ファイルを新しく作成してImageViewクラスを実装する。

jpg画像の読み込みはBitmapFactory.decodeFile()で行い、Bitmap.createBitmap()にMatrixを渡すことで拡大縮小、Canvas::drawBitmap()で範囲を指定して描画する。

ImageViewにBitmapやBitmapDrawableを渡す方法は、ImageViewが勝手に拡大縮小をするために利用できない。
package com.Test119;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;


public class ImageFileView extends View
{
	private	Bitmap	_bmpImageFile;		//画像ファイル
	private	Bitmap	_bmpResized;		//リサイズ画像
	private	int		_nWidthDisp;		//画面幅
	private	int		_nHeightDisp;		//画面縦


	public ImageFileView(Context context)
	{
		super(context);

		//画面サイズ取得
		WindowManager	wmWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		Display			display = wmWindowManager.getDefaultDisplay();
		_nWidthDisp		= display.getWidth();
		_nHeightDisp	= display.getHeight();
		Log.d("Test119","DisplaySize " + _nWidthDisp + "x" + _nHeightDisp);

		//画像ファイル読み込み
		LoadImageFile("/sdcard/test.jpg",false);	//描画更新しない
	}


	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		//タッチイベント取得
		if(event.getAction() == MotionEvent.ACTION_DOWN)
		{
			LoadImageFile("/sdcard/test2.jpg",true);
		}
		return true;
	}	


	public boolean	LoadImageFile(String strFile,boolean bInvalidate)
	{
		//画像読み込み
		_bmpImageFile = BitmapFactory.decodeFile(strFile);
		
		int		nWidth = _bmpImageFile.getWidth();
		int		nHeight = _bmpImageFile.getHeight();
		Log.d("Test119","ImageSize " + nWidth + "x" + nHeight);

		float	fScale;

		//拡大縮小率取得
		if(true)
		{
			//画像に合わせる
			if((long)nWidth * _nHeightDisp > (long)_nWidthDisp * nHeight)
				fScale = (float)_nWidthDisp / nWidth;
			else
				fScale = (float)_nHeightDisp / nHeight;
		}
		else if(false)
		{
			//高さに合わせる
			fScale = (float)_nHeightDisp / nHeight;
		}
		else
		{
			//幅に合わせる
			fScale = (float)_nWidthDisp / nWidth;
		}

		//リサイズ
		Matrix	matrix = new Matrix();
		matrix.postScale(fScale,fScale,0,0);
		_bmpResized = Bitmap.createBitmap(_bmpImageFile,0,0,nWidth,nHeight,matrix,true);

		if(bInvalidate)
			invalidate();		//表示更新

		return	(_bmpImageFile != null) ? true : false;
	}


	@Override
	protected void onDraw(Canvas canvas)
	{
		if(_bmpImageFile == null)
		{
			super.onDraw(canvas);
			return;
		}


		//描画
        Rect	rect = new Rect(0,0,_bmpResized.getWidth(),_bmpResized.getHeight());
        canvas.drawBitmap(_bmpResized,rect,rect,null);
	}
}

test119_03.png
上で実装したImageFileViewをアクティビティに結びつける。

今回はさらにタイトル消去とフルスクリーン化も行った。
package com.Test119;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class Test119Act extends Activity
{
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setContentView(new ImageFileView(this));
	}
}

test119_04.png
これでSDカード上にjpgファイルを2つ配置してから実行すると、起動時は/sdcard/test.jpgが読み込まれ表示、画面をタッチすると、、、

test119_05.png
/sdcard/test2.jpgが表示された。




プロジェクトファイルをダウンロード

2011年01月18日

Android NDKでlibjpegをビルドして利用する

test118_01.png
今回はIJGのJPEGライブラリ(libjpeg、jpeglib)をAndroid NDKから利用してJpgファイルを表示する。

まずIndependent JPEG GroupからJPEGライブラリのソースファイルをダウンロードする。「jpegsr8c.zip」というファイルだった。

test118_02.png
EasyProjectGenerator for Androidで雛形となるAndroidプロジェクトを作成する。今回はC++でNativeViewを利用する。

test118_03.png
ダウンロードしたJPEGライブラリのソースファイル一式をAndroidプロジェクトのjniフォルダへ解凍する。

test118_04.png
さらにjniフォルダにあったソースファイルやmakefileを「Test118」というフォルダを作成してその中へ移動する。

test118_05.png
プロジェクトをEclipseへ取り込む。

jniフォルダ直下に「Android.mk」ファイルを作成する。そしてjniフォルダの下のフォルダをビルド対象に指定する。
include $(call all-subdir-makefiles)

test118_06.png 次にlibjpegの「jpeg-8c」フォルダの中に「Android.mk」ファイルを作成して、JPEGライブラリのビルド指定をする。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libjpeg

LOCAL_SRC_FILES := jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c jdarith.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemname.c

include $(BUILD_STATIC_LIBRARY)

test118_07.png
次にlibjpegの「jpeg-8c」フォルダの中にある「jconfig.txt」ファイルを選択した状態で[F2]キーを押して、ファイル名を「jconfig.h」に変更する。これでJPEGライブラリのビルド準備が整った。

test118_08.png 次にcppファイルを開き、実際にlibjpegを利用してjpgファイルを読み込み表示する処理を実装する。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <jni.h>
#include <android/bitmap.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif


/* this function is from Android NDK bitmap-plasma , and modify for green color bug */
static uint16_t  make565(int red, int green, int blue)
{
	return (uint16_t)( ((red   << 8) & 0xf800) |
					   ((green << 3) & 0x07e0) |
					   ((blue  >> 3) & 0x001f) );
}


#include <stdio.h>

extern "C"
{
#define XMD_H
#include "../jpeg-8c/jpeglib.h"
#include "../jpeg-8c/jerror.h"
}


static	void	_JpegError(j_common_ptr cinfo)
{
	char	pszMessage[JMSG_LENGTH_MAX];

	(*cinfo->err->format_message)(cinfo,pszMessage);

	__android_log_print(ANDROID_LOG_INFO,"Test118","error!  %s",pszMessage);
}

static int DrawBitmap(AndroidBitmapInfo* pBitmapInfo, void* pPixels,const char* pszJpegFile)
{
	int			yy;
	int			nJpegLineBytes;			//JPEG1ラインのバイト数

	char*		lpbtBits;
	JSAMPLE*	pSample;
	FILE*		fp;
	JSAMPROW	buffer[1];
	JSAMPLE		tmp;

	jpeg_decompress_struct		cInfo;
	jpeg_error_mgr				jError;

	fp = fopen(pszJpegFile,"rb");
	if(fp == NULL)
		return	0;

	cInfo.err = jpeg_std_error(&jError);			//エラーハンドラ設定
	jError.error_exit = _JpegError;					//エラーハンドラ設定

	jpeg_create_decompress(&cInfo);					//
	jpeg_stdio_src(&cInfo,fp);						//読込ファイル設定
	jpeg_read_header(&cInfo,TRUE);					//ヘッダー読込
	jpeg_start_decompress(&cInfo);					//デコードスタート

	nJpegLineBytes = cInfo.output_width * cInfo.output_components;		//JPEG 1ラインのバイト数

	pSample = new JSAMPLE[nJpegLineBytes + 10];		// ラインバッファ確保
	buffer[0] = pSample;

	yy = 0;
	while(cInfo.output_scanline < cInfo.output_height)
	{
		if(yy >= pBitmapInfo->height)
			break;

		jpeg_read_scanlines(&cInfo,buffer,1);		//Jpegを1ライン読み込む

		int		xx;
		int		x3;

		uint16_t*  pLine = (uint16_t*)pPixels;
		for(xx = 0, x3 = 0; xx < pBitmapInfo->width && x3 < nJpegLineBytes; xx++, x3 += 3)
		{
			//JPEGはBGRの順
			pLine[xx] = make565(buffer[0][x3 + 0],buffer[0][x3 + 1],buffer[0][x3 + 2]);
		}

		// go to next line
		pPixels = (char*)pPixels + pBitmapInfo->stride;
		yy++;
	}
	delete	pSample;

	jpeg_finish_decompress(&cInfo);		// 読み込み終了処理
	jpeg_destroy_decompress(&cInfo);
	fclose(fp);

	return	1;
}




extern "C"
JNIEXPORT void JNICALL Java_com_Test118_NativeView_RenderBitmap(JNIEnv * env, jobject  obj, jobject bitmap)
{
	AndroidBitmapInfo  info;
	void*			  pixels;
	int				ret;

	if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
		return;

	if (info.format != ANDROID_BITMAP_FORMAT_RGB_565)
		return;

	if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0)
		return;

	DrawBitmap(&info,pixels,"/sdcard/test.jpg");

	AndroidBitmap_unlockPixels(env, bitmap);
}

test118_09.png 最後にcppファイルをビルドするための「Android.mk」ファイルにビルドしたlibjpegを利用する指定をする。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test118
LOCAL_SRC_FILES := Test118.cpp
LOCAL_LDLIBS    := -lz -ljnigraphics

LOCAL_STATIC_LIBRARIES += libjpeg

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

test118_10.png
これでビルドをかけると、libjpeg.aとそれを利用したバイナリが生成される。

test118_11.png
テスト表示用のjpgファイル「test.jpg」をAndroidエミュレーターのSDカードへと転送する。

test118_12.png
そしてプロジェクトを実行すると、jpgファイルが表示された。




プロジェクトファイルをダウンロード

Android NDKでZIP書庫内のファイルリストを取得する

test117_01.png
今回はデータ圧縮/解凍用のzlibを利用してzip書庫ファイル内のファイル一覧を取得する。

zlib自体はAndroid NDKに元々含まれているが、zlibの頒布ファイルにはzipファイルにアクセスするためのライブラリが用意されている。今回はそれを利用する。

http://www.zlib.net/からzlibのソースコードをダウンロードする。ダウンロードバージョンはZlib 1.2.5。

test117_02.png
AndroidのプロジェクトをEasyProjectGenerator for Androidで生成する。今回はC++用のNativeViewプロジェクトにした。

test117_03.png
ダウンロードしたzlibのソースコードの「zlib-1.2.5/ contrib/ minizip」フォルダに含まれる「ioapi.h」「ioapi.c」「unzip.h」「unzip.c」ファイルを、プロジェクトのjniフォルダ内に解凍する。

test117_04.png
Eclipseでプロジェクトを開き、まずはAndroid.mkを編集して、「ioapi.c」「unzip.c」をビルド対象に入れる。

このとき、zlibを利用するので、LOCAL_LDLIBSに「-lz」オプションが指定されているのを確認しておく。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test117
LOCAL_SRC_FILES := Test117.cpp ioapi.c unzip.c
LOCAL_LDLIBS    := -lz -ljnigraphics

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

test117_05.png 次にメイン処理を実装する。"unzip.h"に含まれる、unzOpen()でzipファイルを開き、unzGetCurrentFileInfo()で書庫内のファイル情報を取得、unzGoToNextFile()で次のファイルに進む、最後はunzClose()でクローズして終了という簡単な流れだ。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <jni.h>
#include <android/bitmap.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif


/* this function is from Android NDK bitmap-plasma , and modify for green color bug */
static uint16_t  make565(int red, int green, int blue)
{
	return (uint16_t)( ((red   << 8) & 0xf800) |
					   ((green << 3) & 0x07e0) |
					   ((blue  >> 3) & 0x001f) );
}


static void DrawBitmap(AndroidBitmapInfo* pBitmapInfo, void* pPixels)
{
	int		yy;

	for(yy = 0; yy < pBitmapInfo->height; yy++)
	{
		int		xx;
		uint16_t*  pLine = (uint16_t*)pPixels;

		for(xx = 0; xx < pBitmapInfo->width; xx++)
		{
			pLine[xx] = make565(xx % 256,yy % 256,0);
		}

		// go to next line
		pPixels = (char*)pPixels + pBitmapInfo->stride;
	}
}


#include <stdio.h>
#include <string.h>

#include "unzip.h"

#define	MAX_PATH	256

int		Test(const char* pszZipFile)
{
	int		nRet;
	unzFile	zipFile = unzOpen(pszZipFile);

	nRet = unzGoToFirstFile(zipFile);
	while(nRet == UNZ_OK)
	{
		unz_file_info	info;
		char	pszFileName[MAX_PATH];

		nRet = unzGetCurrentFileInfo(zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
		if(nRet == UNZ_OK)
		{
			__android_log_print(ANDROID_LOG_INFO,"Test117","Found File in zip : %s",pszFileName);
		}

		nRet = unzGoToNextFile(zipFile);
	}
	unzClose(zipFile);

	return	1;
}

extern "C"
JNIEXPORT void JNICALL Java_com_Test117_NativeView_RenderBitmap(JNIEnv * env, jobject  obj, jobject bitmap)
{
	Test("/sdcard/desire_img.zip");

	AndroidBitmapInfo  info;
	void*			  pixels;
	int				ret;

	if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
		return;

	if (info.format != ANDROID_BITMAP_FORMAT_RGB_565)
		return;

	if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0)
		return;

	DrawBitmap(&info,pixels);

	AndroidBitmap_unlockPixels(env, bitmap);
}

test117_06.png このままではビルドできないので、「ioapi.h」の50行目ぐらいにUSE_FILE32APIのdefineを追加して、fopen64ではなくfopenが使われるようにする。
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"


#define	USE_FILE32API


#if defined(USE_FILE32API)
#define fopen64 fopen
#define ftello64 ftell
#define fseeko64 fseek
#else
#ifdef _MSC_VER
 #define fopen64 fopen
 #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
  #define ftello64 _ftelli64
  #define fseeko64 _fseeki64
 #else // old MSC
  #define ftello64 ftell
  #define fseeko64 fseek
 #endif
#endif
#endif

test117_07.png


これでSDカードにZIPファイルを配置してから実行すると、zip書庫内のファイルリストをログに出力できた。


プロジェクトファイルをダウンロード

Android端末へドラッグアンドドロップでファイルを転送する

adb_push01.png
WindowsからAndroidエミュレーターや実機へファイルを送り込むのに、毎回、コマンドプロンプトでadb pushをしたりDDMSするのは面倒。そのため今回はドラッグアンドドロップで簡単に転送できるようにする。

まずメモ帳を管理者権限で開く。「スタート」メニューに「notepad」と入力して、現れた実行ファイルを右クリックして現れるメニューから「管理者として実行」を選択する。

adb_push02.png そしてメモ帳にadb pushコマンドを入力する。
adb push %1 /sdcard/
pause

adb_push03.png
コマンドを入力したら「ファイル」メニューの「名前を付けて保存」で保存する。

adb_push04.png
保存する場所はAndroid SDKのフォルダ内にある「platform-tools」フォルダだ。adb.exeなどと同じフォルダへ保存する。今回はファイル名を「adb_push_to_sdcard.bat」とした。バッチファイル(.bat)として保存するのがポイント。

adb_push05.png
保存したらもう一度「名前を付けて保存」を開く。

adb_push06.png
そして保存したフォルダを再び開き、「すべてのファイル」を選択する。

adb_push07.png
先ほど保存したバッチファイルがある。このファイルを右クリックして現れるメニューから「送る」にある「デスクトップ(ショートカットを作成)」を選択する。

adb_push08.png
これでデスクトップにショートカットが作成された。保存はしなくていいので「キャンセル」ボタンを押してメモ帳を閉じる。






adb_push09.png
実際にファイルを転送する。

実機を接続するかAndroidエミュレーターを起動した状態で、転送したいファイルをデスクトップ上のショートカットへドラッグアンドドロップする。

adb_push10.png
するとコマンドプロンプトが開き、AndroidのSDカード上へ選択したファイルが転送される。

adb_push11.png
DDMSでも転送したファイルが確認できた。


2011年01月17日

Androidのカメラアプリをオートフォーカス対応にする

test116af_01.png
前回はシンプルなAndroidカメラアプリを作成した。今回は前回のカメラアプリをオートフォーカス対応にする。

まずはAndroidManifest.xmlにオートフォーカスの利用宣言を追加する。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.Test116"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test116Act"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

</manifest>

test116af_02.png
「オートフォーカス」というと実装が難しいように感じるが、ものすごく簡単にできる。

今まで撮影用にCamera.takePicture()を呼んでいた所で、代わりにCamera.autoFocus()を使ってオートフォーカスコールバックを指定する。するとオートフォーカスが終了したときにコールバックが実行されるので、そこで改めてCamera.takePicture()を呼んで撮影するだけだ。

	private Camera.AutoFocusCallback	_pfnAutoFocusCallback = new AutoFocusCallback();

	private final class AutoFocusCallback implements Camera.AutoFocusCallback
	{
		public void onAutoFocus(boolean success, Camera camera)
		{
			Log.d(TAG, "CameraView::onAutoFocus()");

			camera.autoFocus(null);
			camera.takePicture(_pfnShutterCallback, _pfnRawPictureCallback,new JpgPictureCallback());
		};
	}


	public void TakePicture()
	{
		Log.d(TAG, "CameraView::takePicture()");
		
		if(_nReady != 0)
		{
			_nReady = 0;
			if(true)
			{
				//オートフォーカス撮影
				_Camera.autoFocus(_pfnAutoFocusCallback);
			}
			else
			{
				//即撮影
				_Camera.takePicture(_pfnShutterCallback, _pfnRawPictureCallback,new JpgPictureCallback());
			}
		}
	}

test116af_03.png
実際に撮影してギャラリーで確認。きちんとピントが合っている。

test116af_04.jpg
これが実際に撮影した画像を縮小したもの。前回のピンぼけ写真とは比べ物にならないぐらいよくなった。




プロジェクトファイルをダウンロード

Androidで簡単なカメラアプリを作る

test116_01.png
今回はシンプルなカメラアプリを作成する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。今回はJavaのSimpleプロジェクトを利用した。

test116_02.png 自動生成されたプロジェクトをEclipseに取り込み、AndroidManifest.xmlにカメラ利用のパーミッション宣言を追加する。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.Test116"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test116Act"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

</manifest>

test116_03.png
次にメインソースコードを編集する

。今回はメインの背景が緑色のレイアウトに1×1ドットのCameraViewを追加する形にした。CameraViewのサイズはCameraView内で変更することにする。

また総ショット数(画像保存時の連番に利用)を保存/利用するためにonSaveInstanceState()やonRestoreInstanceState()を作り、CameraViewへと渡すようにした。
package com.Test116;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.graphics.Color;

public class Test116Act extends Activity
{
	private	CameraView	_view = null;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		LinearLayout layout = new LinearLayout(this);
		layout.setBackgroundColor(Color.GREEN);

		_view = new CameraView(this,savedInstanceState);

		layout.addView(_view,new LinearLayout.LayoutParams(1,1));
		setContentView(layout);
	}


	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);

		if(_view != null)
			_view.OnSaveState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		
		if(_view != null)
			_view.OnLoadState(savedInstanceState);
	}
}

test116_04.png
そして「CameraView.java」ファイルを作成。CameraViewクラスを実装する。

プレビューはカメラがサポートする最小サイズで表示。プレビューを画面タッチすると撮影。撮影時はサポートする最大サイズでjpg保存。ファイル名は総ショット数を利用して決定している。


package com.Test116;

import android.hardware.Camera;
import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.MotionEvent;
import java.io.FileOutputStream;
import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import java.util.List;
import android.view.ViewGroup.LayoutParams;
import android.content.ContentValues;
import android.content.ContentResolver;


public class CameraView extends SurfaceView
	implements SurfaceHolder.Callback
{
	private final String	TAG = "Test116";
	
	private	SurfaceHolder	_SurfaceHolder;
	private	Camera			_Camera;
	private	int				_nReady;
	private	long			_nShotNo;


	public CameraView(Context context,Bundle savedInstanceState)
	{
		super(context);

		Log.d(TAG, "CameraView::CameraView()");

		_nReady = 0;
		_nShotNo = 0;
		OnLoadState(savedInstanceState);
	
		_SurfaceHolder = getHolder();
		_SurfaceHolder.addCallback(this);
		_SurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}

	//設定の保存
	public void OnSaveState(Bundle outState)
	{
		Log.d(TAG, "CameraView::OnSaveState()");

		if(outState != null)
		{
			outState.putLong("_nShotNo",_nShotNo);
		}
	}

	//設定の読込
	public void OnLoadState(Bundle savedInstanceState)
	{
		Log.d(TAG, "CameraView::OnLoadState()");

		if(savedInstanceState != null)
		{
			_nShotNo = savedInstanceState.getLong("_nShotNo");
		}
	}
	

	public void surfaceCreated(SurfaceHolder holder)
	{
		Log.d(TAG, "CameraView::surfaceCreated()");

		try
		{
			_Camera = Camera.open();
			_Camera.setPreviewDisplay(holder);
		}
		catch (Exception e)
		{
			Log.e(TAG, "CameraView::surfaceCreated() : Fail to setPreviewDisplay.");
		}
	}

	
	public void surfaceChanged(SurfaceHolder holder,int format,int w,int h)
	{
		Log.d(TAG, "CameraView::surfaceChanged()");

		Camera.Parameters	paramCamera = _Camera.getParameters();
		List<Size>			asizeSupport;
		Size		size;

		//カメラがサポートする撮影サイズ一覧を取得
		asizeSupport = paramCamera.getSupportedPictureSizes();
		if(asizeSupport.size() != 0)
		{
			//一番大きい撮影サイズを利用
			size = asizeSupport.get(0);
			paramCamera.setPictureSize(size.width,size.height);
		}
		
		//カメラがサポートするプレビューサイズ一覧を取得
		asizeSupport = paramCamera.getSupportedPreviewSizes();
		if(asizeSupport.size() != 0)
		{
			//一番小さいプレビューサイズを利用
			size = asizeSupport.get(asizeSupport.size() - 1);
			paramCamera.setPreviewSize(size.width,size.height);


			LayoutParams	paramLayout;

			//ビューサイズをプレビューサイズに合わせる
			paramLayout = getLayoutParams();
			paramLayout.width = size.width;
			paramLayout.height = size.height;
			setLayoutParams(paramLayout);
		}

		//カメラパラメーター設定
		_Camera.setParameters(paramCamera);
		
		_Camera.startPreview();		//プレビューの開始
		_nReady = 1;
	}

	
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		Log.d(TAG, "CameraView::surfaceDestroyed()");

		//カメラの破棄
		_nReady = 0;
		_Camera.setPreviewCallback(null);
		_Camera.stopPreview();
		_Camera.release();
		_Camera = null;
	}


	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		//タッチイベント取得
		if(event.getAction() == MotionEvent.ACTION_DOWN)
		{
			Log.d(TAG, "CameraView::onTouchEvent()");

			TakePicture();			//撮影
		}
		return true;
	}



	private ShutterCallback		_pfnShutterCallback = new ShutterCallback();

	private final class ShutterCallback implements android.hardware.Camera.ShutterCallback
	{
		public void onShutter()
		{
			Log.d(TAG, "CameraView::onShutter()");
		}
	};



	private RawPictureCallback	_pfnRawPictureCallback = new RawPictureCallback();
	
	private final class RawPictureCallback implements PictureCallback
	{
		public void onPictureTaken(byte [] rawData, android.hardware.Camera camera)
		{
			Log.d(TAG, "CameraView::onRawPictureTaken()");
		};
	}

	
	private final class JpgPictureCallback implements PictureCallback
	{
		public void onPictureTaken(byte [] data, android.hardware.Camera camera)
		{
			Log.d(TAG, "CameraView::onJpgPictureTaken()");

			String	strFolder;
			String	strFile;

			strFolder = Environment.getExternalStorageDirectory()+"/DCIM/Camera/";

			//保存ファイル名
			//重複チェックをしていないので、もしもすでにファイルがあったら上書きされる!
			strFile = strFolder + "test" + _nShotNo + ".jpg";
			
			//if(_nShotNo == 0)
			//{
			//	//フォルダ作成
			//	File	newFolder = new File(strFolder);
			//	newFolder.mkdir();
			//}

			try
			{
				//撮影画像保存(dataがすでにjpg画像になっているので、これをそのままファイルに落とすだけ)
				FileOutputStream	cFile = new FileOutputStream(strFile);
				cFile.write(data);
				cFile.close();

				//コンテンツ登録(androidギャラリーへの登録)
				long	nDate;
				ContentValues values = new ContentValues();

				nDate = System.currentTimeMillis();
				values.put(Images.Media.MIME_TYPE,"image/jpeg");			//必須
				values.put(Images.Media.DATA,strFile);						//必須:ファイルパス(uriからストリーム作るなら不要)
				values.put(Images.Media.SIZE,new File(strFile).length()); 	//必須:ファイルサイズ(同上)
//				values.put(Images.Media.TITLE,strFile);
//				values.put(Images.Media.DISPLAY_NAME,strFile);
				values.put(Images.Media.DATE_ADDED,nDate);
				values.put(Images.Media.DATE_TAKEN,nDate);
				values.put(Images.Media.DATE_MODIFIED,nDate);
//				values.put(Images.Media.DESCRIPTION,"");
//				values.put(Images.Media.LATITUDE,0.0);
//				values.put(Images.Media.LONGITUDE,0.0);
//				values.put(Images.Media.ORIENTATION,"");
				
				ContentResolver	contentResolver = getContext().getContentResolver();
				contentResolver.insert(Media.EXTERNAL_CONTENT_URI, values);
			}
			catch(Exception e)
			{
				Log.e(TAG, "CameraView::onJpgPictureTaken() : Fail to save image file.");
			}
			_nShotNo++;

			camera.startPreview();		//プレビュー再開
			_nReady = 1;
		};
	};


	
	public void TakePicture()
	{
		Log.d(TAG, "CameraView::takePicture()");
		
		if(_nReady != 0)
		{
			_nReady = 0;
			_Camera.takePicture(_pfnShutterCallback, _pfnRawPictureCallback,new JpgPictureCallback());
		}
	}
}

test116_05.png
実装が終わったので実行してみる。

test116_06.png
デバイスの選択画面で「Choose a running Android device」から実機を選択する。

test116_07.png
これで実機でアプリが実行されてプレビューが表示された。プレビュー画面を指でタッチすると撮影される。

test116_08.png
撮影した画像はAndroid標準のギャラリーアプリで確認できる。

test1.jpg
これが実際に撮影した画像を縮小したもの。保存サイズは2592×1952ドットでした。オートフォーカスを実装していないのでピンぼけすぎです。




プロジェクトファイルをダウンロード

前の10件 1  2  3  4  5  6  7  8  9  10  11