« 2010年12月 | メイン | 2011年12月 »

前の10件 1  2  3

2011年01月 記事一覧

2011年01月19日

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が表示された。




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

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 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 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 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」ができ、このように追加画面へと切り換えれる。




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

2011年01月20日

Android SDKでファイル選択ダイアログを作る

test125_01.png
Androidはユーザーにファイルを意識させない設計がされているためなのか、標準でファイル選択ダイアログが用意されていません。ちょっと不便すぎるので今回はファイル選択ダイアログを作成します。

まずはJavaで雛形となるプロジェクトを作り、「menu」ボタンからファイル選択画面を開くSelectFile()を呼び出す流れを作る。この状態ではSelectFile()が実装されていないのでビルドできません。
package com.Test125;

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

public class Test125Act 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,"Open").setIcon(android.R.drawable.ic_menu_set_as);

		return true;
	}


	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch(item.getItemId())
		{
		case 0:
			SelectFile();
			return	true;

		default:
			break;
		}

		return super.onOptionsItemSelected(item);
	}
}

test125_02.png
プロジェクトに「SelectFileDialog.java」ファイルを追加して、ファイル選択ダイアログのメイン処理を実装する。

AlertDialog.Buildeを作り、そこにファイル名のリストメニューを表示。クリックされたアイテムがフォルダならさらに下のフォルダ用のAlertDialog.Builderを作り表示。クリックされたのがファイルなら終了という流れです。
package com.Test125;
 
import java.io.File;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.KeyEvent;

public class SelectFileDialog extends Activity
	implements	DialogInterface.OnClickListener
				,DialogInterface.OnKeyListener
{
	private	File		_fileCurrent;	//現在表示しているフォルダ
	private	File[]		_aFileList;		//現在表示しているフォルダのファイル一覧
	private	String[]	_astrFileName;	//現在表示しているフォルダのメニュー用ファイル名
	private	Context		_context;

	private	Dialog		_dlgThis;

	public SelectFileDialog(Context context)
	{
		_context = context;
	}



	@Override
	public void onPause()
	{
		if(_dlgThis != null && _dlgThis.isShowing())
			_dlgThis.dismiss();
		
		super.onPause();
	}


/*	@Override
	public void onResume()
	{
		Log.d("Test125","--onResume--- ");

		if(_dlgThis != null)
			_dlgThis.show();
		
		super.onResume();
	}

	public	String	GetCurrentPath()
	{
		if(_dlgThis == null || _dlgThis.isShowing() == false || _fileCurrent == null)
			return	"";

		return	_fileCurrent.getAbsolutePath();
	}
*/	

	public	boolean	Show(String strInitPath)
	{
		boolean	ret;

		ret = CreateFileList(strInitPath);
		if(ret == false)
			return	false;

		
		AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(_context);
		dlgBuilder.setCancelable(true);
		dlgBuilder.setOnKeyListener(this);
		dlgBuilder.setTitle(_fileCurrent.getPath());
		dlgBuilder.setItems(_astrFileName,this);

		_dlgThis = dlgBuilder.create();
		_dlgThis.show();

		return	true;
	}


	public	void	Close(DialogInterface dialog,File fileSelected)
	{
		((onSelectFileDialogListener)_context).onFileSelected_by_SelectFileDialog(fileSelected);
		dialog.dismiss();
		_dlgThis = null;
	}


	@Override
	public void onClick(DialogInterface dialog, int which)
	{
		File	file = _aFileList[which];

		if(file.isDirectory())
		{
			//フォルダが選択されたので開く
			Show(file.getAbsolutePath());
			dialog.dismiss();
		}
		else
		{
			//選択されたので終了
			Close(dialog,file);
		}
	}


	@Override
	public boolean  onKey(DialogInterface dialog, int keyCode, KeyEvent event)
	{
		if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN)
		{
			File	fileParent;

			fileParent = _fileCurrent.getParentFile();
			if(fileParent != null)
			{
				Show(fileParent.getAbsolutePath());
				dialog.dismiss();
			}
			else
			{
				//ルートだったので終了
				Close(dialog,null);
			}

			return	true;
		}
		return	false;
	}


	private	boolean	CreateFileList(String strPath)
	{
		File[]	aFiles;
		
		_aFileList = null;
		_astrFileName = null;

		_fileCurrent = new File(strPath);
		if(_fileCurrent == null)
			return	false;
		
		aFiles = _fileCurrent.listFiles();
		if(aFiles == null || aFiles.length == 0)
		{
			_aFileList = new File[0];
			_astrFileName = new String[0];
			return	true;
		}
		

		int			i;
		int			nCount;
		String[]	astrName;

		astrName = new String[aFiles.length];

		nCount = 0;
		for(i = 0; i < aFiles.length; i++)
		{
			if(aFiles[i].isDirectory() && aFiles[i].isHidden() == false)
			{
				//ディレクトリの場合
				astrName[i] = aFiles[i].getName() + "/";
				nCount++;
			}
			else if(aFiles[i].isFile() && aFiles[i].isHidden() == false)
			{
				//通常のファイル
				astrName[i] = aFiles[i].getName();
				nCount++;
			}
			else
			{
				aFiles[i] = null;
			}
		}


		_aFileList = new File[nCount];
		_astrFileName = new String[nCount];

		nCount = 0;
		for(i = 0; i < aFiles.length; i++)
		{
			if(aFiles[i] != null)
			{
				_aFileList[nCount] = aFiles[i];
				_astrFileName[nCount] = astrName[i];
				nCount++;
			}
		}

		//ソートするならここでソート
		
		return	true;
	}


	public interface onSelectFileDialogListener
	{
		public void onFileSelected_by_SelectFileDialog(File file);
	}
}

test125_03.png
そして再び戻ってSelectFileDialogを呼び出す処理を実装する。

本当はSelectFileDialogをメンバー変数ではなく、ローカルに宣言して利用できるようにしたかったのですが、、、ファイル選択ダイアログを開いているときに端末の向きを変えてオリエンテーションが変わるとOnPause、OnResumeが呼ばれずにメモリリークが起きるので、苦肉の策としてこのような実装にしました。
package com.Test125;

import java.io.File;

import com.Test125.SelectFileDialog.onSelectFileDialogListener;

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

public class Test125Act extends Activity
		implements onSelectFileDialogListener
{
	@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,"Open").setIcon(android.R.drawable.ic_menu_set_as);

		return true;
	}


	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch(item.getItemId())
		{
		case 0:
			SelectFile();
			return	true;

		default:
			break;
		}

		return super.onOptionsItemSelected(item);
	}


	protected	SelectFileDialog	_dlgSelectFile;

	
	private	void	SelectFile()
	{
		//ここで画面回転を固定すべき(画面が固定されていないなら)

		_dlgSelectFile = new SelectFileDialog(this);
		_dlgSelectFile.Show("/sdcard/");
	}

	
	@Override
	public void onFileSelected_by_SelectFileDialog(File file)
	{
		//ここで画面回転の固定を解除すべき(SelectFileDialog利用前に画面が固定されていなかったなら)

		if(file != null)
			Log.d("Test125","selected : " + file.getName());
		else
			Log.d("Test125","not selected");

		_dlgSelectFile = null;
	}

	
	@Override
	public void onPause()
	{
		if(_dlgSelectFile != null)
			_dlgSelectFile.onPause();
		
		super.onPause();
	}
}

test125_04.png
これで実行して、「memu」ボタンを押すとメニューが表示され、、、

test125_05.png
メニューの「Open」をクリックすると、、、

test125_06.png
ファイル選択ダイアログが開きました。フォルダを選択するとその中が表示され、「戻る」ボタンを押すとひとつ上のフォルダに移ります。

そして端末の向きを変えると、、、メニューが終了します。本当は向きを変えてメニューを再表示させるべきですが複雑になるのでやめました。




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

2011年01月21日

Android SDKで画面が533x320ドットになる問題の解決策

test126_01.png
Androidで画像表示をしているうちに、画面サイズが533×320ドットとして認識される問題にぶつかりました。

以下のようなソースコードで画面のサイズや解像度などの情報を取得する。実行にはHTC Desireを利用した。
package com.Test126;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

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


		WindowManager	wmWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
		Display			display = wmWindowManager.getDefaultDisplay();

		Log.d("Test126","Display.getWidth() " + display.getWidth());
		Log.d("Test126","Display.getWidth() " + display.getHeight());
		
		DisplayMetrics	displayMetrics = new DisplayMetrics();
		display.getMetrics(displayMetrics);
		Log.d("Test126","DisplayMetrics.widthPixels   " + displayMetrics.widthPixels);
		Log.d("Test126","DisplayMetrics.heightPixels  " + displayMetrics.heightPixels);
		Log.d("Test126","DisplayMetrics.density       " + displayMetrics.density);
		Log.d("Test126","DisplayMetrics.densityDpi    " + displayMetrics.densityDpi);
		Log.d("Test126","DisplayMetrics.scaledDensity " + displayMetrics.scaledDensity);
		Log.d("Test126","DisplayMetrics.xdpi          " + displayMetrics.xdpi);
		Log.d("Test126","DisplayMetrics.ydpi          " + displayMetrics.ydpi);
	}
}

test126_02.png
するとこのような結果になった。HTC Desireの物理的な画面サイズは800×480ドットだが、取得できたサイズは533×320ドット。

マニフェスト設定によってはこのような結果になるらしい。もちろんこの状態だと、アプリ内での描画サイズも533×320ドットになり、800×480ドットの画面サイズを活かした緻密な描画ができない。
01-21 00:22:41.873: DEBUG/Test126(8149): Display.getWidth() 533
01-21 00:22:41.873: DEBUG/Test126(8149): Display.getWidth() 320
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.widthPixels   533
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.heightPixels  320
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.density       1.5
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.densityDpi    240
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.scaledDensity 1.5
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.xdpi          254.0
01-21 00:22:41.873: DEBUG/Test126(8149): DisplayMetrics.ydpi          254.0

test126_03.png 533×320ドットになる問題の解決策のひとつはAndroidManifest.xmlの中でuses-sdk android:minSdkVersionを指定する方法。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.Test126"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test126Act"
                  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-sdk android:minSdkVersion="4"/>

</manifest>

test126_04.png これできちんと800×480ドットと認識できた。
01-21 00:23:46.933: DEBUG/Test126(8221): Display.getWidth() 800
01-21 00:23:46.933: DEBUG/Test126(8221): Display.getWidth() 480
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.widthPixels   800
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.heightPixels  480
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.density       1.5
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.densityDpi    240
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.scaledDensity 1.5
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.xdpi          254.0
01-21 00:23:46.933: DEBUG/Test126(8221): DisplayMetrics.ydpi          254.0

test126_05.png もう一つの解決策はsupports-screensでandroid:largeScreensなどを有効にする方法。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.Test126"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test126Act"
                  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>


<supports-screens
	android:smallScreens="true"
	android:normalScreens="true"
	android:largeScreens="true"
	android:anyDensity="true" />
</manifest>

test126_06.png こちらでも800×480ドットと認識された。
01-21 00:24:52.513: DEBUG/Test126(8294): Display.getWidth() 800
01-21 00:24:52.513: DEBUG/Test126(8294): Display.getWidth() 480
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.widthPixels   800
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.heightPixels  480
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.density       1.5
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.densityDpi    240
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.scaledDensity 1.5
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.xdpi          254.0
01-21 00:24:52.513: DEBUG/Test126(8294): DisplayMetrics.ydpi          254.0

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

Android SDKでキーロック状態を取得する

test127.png
Androidアプリの実行中に今現在画面がロックされているかどうかのロック状態を調べるにはKeyguardManager:: inKeyguardRestrictedInputMode()を使う。trueならロック中、falseならロックされていない。

問題はロック/アンロックの検出。HTC Desireでは下のソースで検出できましたが、Androidエミュレーターでは検出できませんでした。また下のソースではアンロックの検出にフォーカス状態を利用しているので、フォーカスが一定しないアプリの場合は使えないはずです。 電源ボタンをハンドルすれば手動でのロック/アンロックは検出できるはずですが、そうすると今度はソフトウエア的なロックなどは検出できないはずですし、、、ということでロック/アンロックの検出方法は不明です。
package com.Test127;

import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

public class Test127Act extends Activity
{
	private	KeyguardManager	_KeyguardManager;
	private	boolean			_bScreenLocked = false;
	
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		_KeyguardManager = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
		_bScreenLocked = _KeyguardManager.inKeyguardRestrictedInputMode();
	}

	
	@Override
	public void	onPause()
	{
		super.onPause();

		_bScreenLocked = _KeyguardManager.inKeyguardRestrictedInputMode();
		if(_bScreenLocked)
		{
			//ロックされた
			//呼ばれないことあり
			Log.d("Test127","onPause() Locked!!! Do something ....");
		}
	}

	
	@Override
	public void	onWindowFocusChanged(boolean hasFocus)
	{

		super.onWindowFocusChanged(hasFocus);

		if(_bScreenLocked == true && hasFocus)
		{
			//アンロックされた
			//呼ばれないことあり
			Log.d("Test127","onWindowFocusChanged() Unlocked!!! Do something ....");
		}
		_bScreenLocked = _KeyguardManager.inKeyguardRestrictedInputMode();
	}
}


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

Android SDKでメニュー項目を動的に操作する

test128_01.png
以前にAndroid SDKで「menu」ボタンでメニューを表示するでメニューを表示した。onCreateOptionsMenu()でメニュー項目を作り表示、onOptionsItemSelected()で選択されたメニュー項目を実行できる。

しかしonCreateOptionsMenu()は初回に1度だけ呼ばれるのみなので、動的にメニュー項目を増やしたり減らしたり、表示するメニューの文字を変えることはできなかった。動的にメニュー項目を変更するにはonPrepareOptionsMenu()をオーバーライドして、そこでメニュー項目を操作する。
package com.Test128;

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


public class Test128Act 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);


		return true;
	}


	@Override
	public boolean onPrepareOptionsMenu(Menu menu)
	{
		String	str;

		str = Calendar.getInstance().getTime().toString();
		menu.findItem(1).setTitle(str);

		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);
	}
}

test128_02.png
これでメニューを動的に操作して、表示するたびにそのときの日時をメニューとして表示することができた。




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

実行時クラスを取得/比較する

test129_01.png JavaではgetClass()を使えば実行時クラスを取得、比較できる。派生クラスの判別に利用可能。
package com.Test129;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

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

		BASE	test = new AAA();

		if(test.getClass() == BASE.class)
			Log.d("Test129","test == BASE");
		else
			Log.d("Test129","test != BASE");

		if(test.getClass() == AAA.class)
			Log.d("Test129","test == AAA");
		else
			Log.d("Test129","test != AAA");

		if(test.getClass() == BBB.class)
			Log.d("Test129","test == BBB");
		else
			Log.d("Test129","test != BBB");
	}

	private	class	BASE
	{
	};
	
	private	class	AAA extends BASE
	{
	};
	
	private	class	BBB extends BASE
	{
	};
}

test129_02.png
きちんと比較できた。




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

前の10件 1  2  3





usefullcode@gmail.com

About 2011年01月

2011年01月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2010年12月です。

次の記事は2011年12月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。