DXライブラリ Android版を使用した Androidアプリで Java のコードでクリップボードを使用する
( Visual Studio編 )


 すべての処理を C++ で実行できれば良いのですが、Android の基本言語が Java である関係で、
今のところ Java を使用しないとクリップボードの機能を使用することができません。

 なので、ここでは『DXライブラリ Android版』には無いクリップボードの機能を Java を使用して
行うための手順を記します。


 1.『AndroidManifest.xml』の内容を Java のコードを実行できるように変更する

 2.プロジェクトに Java のソースファイルを追加する

 3.Java のコードを入力する

 4.Java の情報を取得する C++ のコードを入力する



1.『AndroidManifest.xml』の内容を Java のコードを実行できるように変更する

  Visual Studio『DXライブラリ Android版』を使用する Androidアプリのプロジェクトを開き
 『ソリューションエクスプローラー』と書かれているリストに表示されている『AndroidManifest.xml』
 クリックして、内容を表示します。

  『AndroidManifest.xml』の内容は、使い方ページに沿って編集した場合は以下のようになっていると思いますが、
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="false" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

  この中の以下のように変更します。(色が緑や赤の部分が変更箇所や追加箇所で、2箇所です)
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="true" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="com.(プロジェクト名).Packaging.(プロジェクト名)" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



2.プロジェクトに Java のソースファイルを追加する

  まずに Java のソースファイルを以下の手順でプロジェクトに追加します。

  ① 『ソリューションエクスプローラー』と書かれているリストに表示されている『(プロジェクト名).Packaging』
    を右クリックして、表示されるプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、名前を『src』としてください。

  ② 次に追加した『src』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『com』としてください。

  ③ 次に追加した『com』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『(プロジェクト名)』( (プロジェクト名) はお手元のプロジェクトの名前に置き換えてください )としてください。

  ④ 次に追加した『(プロジェクト名)』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『Packaging』としてください。

    と、要は『(プロジェクト名).Packaging』以下に『src』→『com』→『(プロジェクト名)』→『Packaging』というフォルダ
    を作ります。次に漸く Java ファイルの追加です。

  ⑤ 追加した『Packaging』フォルダーを右クリックして、今度は『新しい項目(W)...』を選択して『新しい項目の追加』ダイアログを表示します。

  ⑥ ダイアログ左側のリストから『インストール済み』→『Cross Platform』→『Android』を選び、
    右側のリストから『Java』を選びます。

  ⑦ ダイアログ下側の『名前(N):』の項目に『(プロジェクト名).java』と入力して『追加(A)』ボタンを押します。

  ⑧ 次に追加された『(プロジェクト名).java』を開いた状態でメニューから『ファイル(F)』→『名前を付けて(プロジェクト名).javaを保存(A)...』を
    選択して、『名前を付けてファイルを保存』ダイアログを表示します。
    ( VisualStudio 2015 の場合はメニューから『ファイル(F)』→『保存オプションの詳細設定(V)...』を選択して、『保存オプションの詳細設定』ダイアログを表示します )

  ⑨ 『名前を付けてファイルを保存』ダイアログの右下にある『上書き保存(S)』の項目を『エンコード付きで保存(V)...』
    に変更します。すると変更した瞬間に『名前を付けて保存の確認』ダイアログが表示されますので『はい(Y)』を選択してください。
    ( VisualStudio 2015 の場合はこの工程が無いので、手順⑩に進んでください )

  ⑩ 次に『保存オプションの詳細設定』ダイアログの『エンコード(E):』の項目を『日本語 (シフト JIS) - コードページ 932』から
    『Unicode (UTF-8 シグネチャなし) - コードページ 65001』に変更して『OK』ボタンを押します。
    ( 『Unicode (UTF-8 シグネチャ付き) - コードページ 65001』と間違えないようにしてください、
     『Unicode (UTF-8 シグネチャなし) - コードページ 65001』はリストのかなり下の方にあります )


 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )


  これで java ファイルの追加は完了です。



3.Java のコードを入力する

  追加した『(プロジェクト名).java』には最初から数行書かれていますが、それを全部消して、代わりに
 今回の『クリップボードの処理』を行うための以下のコードを入力します。
package com.(プロジェクト名).Packaging; import android.app.NativeActivity ; import android.content.ClipboardManager ; import android.content.ClipData ; public class com.(プロジェクト名) extends NativeActivity { // クリップボードの処理が完了したかどうかを保存するメンバー変数 int FinishClipboardProcess ; // クリップボードに設定したり取得したりするテキストを保存するためのメンバー変数 String ClipboardText ; // クリップボードにテキストを設定するための関数 public void SetClipboardText( String SetText ) { // クリップボードの処理が完了したかどうかを保存するメンバー変数に 0 を代入 FinishClipboardProcess = 0 ; // クリップボードに設定するテキストを ClipboardText にセット ClipboardText = SetText ; // UIスレッドで実行する処理を登録する runOnUiThread( new Runnable() { // UIスレッドで呼ばれる関数 @Override public void run() { // システムのクリップボードを取得 ClipboardManager cm = ( ClipboardManager )getSystemService( CLIPBOARD_SERVICE ) ; // クリップボードにテキストを設定 cm.setPrimaryClip( ClipData.newPlainText( "", ClipboardText.subSequence( 0, ClipboardText.length() ) ) ) ; // クリップボードの処理が完了したかどうかを保存するメンバー変数に 1 を代入 FinishClipboardProcess = 1 ; } } ) ; } // クリップボードのテキストを取得するための関数 public void GetClipboardText() { // クリップボードの処理が完了したかどうかを保存するメンバー変数に 0 を代入 FinishClipboardProcess = 0 ; // クリップボードのテキストを保存する変数を初期化 ClipboardText = "" ; // UIスレッドで実行する処理を登録する runOnUiThread( new Runnable() { // UIスレッドで呼ばれる関数 @Override public void run() { // システムのクリップボードを取得 ClipboardManager cm = ( ClipboardManager )getSystemService( CLIPBOARD_SERVICE ) ; // クリップボードにテキストが保存されていなければ何もしない if( cm.hasPrimaryClip() ) { // クリップボードからテキストを取得 ClipboardText = cm.getPrimaryClip().getItemAt( 0 ).getText().toString() ; } // クリップボードの処理が完了したかどうかを保存するメンバー変数に 1 を代入 FinishClipboardProcess = 1 ; } } ) ; } }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



4.Java の情報を取得する C++ のコードを入力する

  次に Java で書いたクリップボードの機能を使用する C++ 側のプログラムです。
  『DXライブラリ Android版』でのみ使用できる GetNativeActivity という関数を使用しています。
#include "DxLib.h" #include <string.h> // クリップボードにテキストを設定する int ClipboardText_Set( const char *Text ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; // アプリの NativeActivity を取得 NativeActivity = GetNativeActivity() ; // Java の関数 SetClipboardText の呼び出し { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // 関数の引数に渡す jstring を作成 jstring jstring_SetText = env->NewStringUTF( Text ) ; // Java のクラス (プロジェクト名) のメンバー関数 SetClipboardText の ID を取得 jmethodID jmethodID_SetClipboardText = env->GetMethodID( jclass_(プロジェクト名), "SetClipboardText", "(Ljava/lang/String;)V" ) ; // Java のクラス (プロジェクト名) のメンバー関数 SetClipboardText の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_SetClipboardText, jstring_SetText ) ; // 関数の引数用に作成した jstring の参照を削除 env->DeleteLocalRef( jstring_SetText ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // Java の関数 SetClipboardText の処理完了待ち while( true ) { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー関数 FinishClipboardProcess の ID を取得 jfieldID jfieldID_FinishClipboardProcess = env->GetFieldID( jclass_(プロジェクト名), "FinishClipboardProcess", "I" ) ; // Java のクラス (プロジェクト名) のメンバー変数 FinishClipboardProcess の値をローカル変数 FinishClipboardProcess に代入 int FinishClipboardProcess = env->GetIntField( NativeActivity->clazz, jfieldID_FinishClipboardProcess ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // 処理が完了していたらループを抜ける if( FinishClipboardProcess == 1 ) { break ; } } // 正常終了 return 0 ; } // クリップボードのテキストを取得する int ClipboardText_Get( char *TextBuffer, int TextBufferSize ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; // アプリの NativeActivity を取得 NativeActivity = GetNativeActivity() ; // Java の関数 GetClipboardText の呼び出し { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー関数 GetClipboardText の ID を取得 jmethodID jmethodID_GetClipboardText = env->GetMethodID( jclass_(プロジェクト名), "GetClipboardText", "()V" ) ; // Java のクラス (プロジェクト名) のメンバー関数 GetClipboardText の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_GetClipboardText ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // Java の関数 GetClipboardText の処理完了待ち while( true ) { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー関数 FinishClipboardProcess の ID を取得 jfieldID jfieldID_FinishClipboardProcess = env->GetFieldID( jclass_(プロジェクト名), "FinishClipboardProcess", "I" ) ; // Java のクラス (プロジェクト名) のメンバー変数 FinishClipboardProcess の値をローカル変数 FinishClipboardProcess に代入 int FinishClipboardProcess = env->GetIntField( NativeActivity->clazz, jfieldID_FinishClipboardProcess ) ; // 処理が完了していたらテキストを取得する if( FinishClipboardProcess == 1 ) { // Java のクラス (プロジェクト名) のメンバー変数 ClipboardText の ID を取得 jfieldID jfieldID_ClipboardText = env->GetFieldID( jclass_(プロジェクト名), "ClipboardText", "Ljava/lang/String;" ) ; // Java のクラス (プロジェクト名) のメンバー変数 ClipboardText の jstring オブジェクトを取得 jstring jstring_ClipboardText = ( jstring )env->GetObjectField( NativeActivity->clazz, jfieldID_ClipboardText ) ; // Java のクラス (プロジェクト名) のメンバー変数 ClipboardText の jstring から C++ 用の文字列のアドレスを取得 const char *chars_ClipboardText = env->GetStringUTFChars( jstring_ClipboardText, NULL ) ; // 文字列をローカル変数 ClipboardText にコピー strncpy( TextBuffer, chars_ClipboardText, TextBufferSize - 1 ) ; // Java のクラス (プロジェクト名) のメンバー変数 ClipboardText の jstring から取得した C++ 用の文字列のアドレスを解放 env->ReleaseStringUTFChars( jstring_ClipboardText, chars_ClipboardText ) ; // Java のクラス (プロジェクト名) のメンバー変数 ClipboardText の jstring オブジェクトの参照を削除 env->DeleteLocalRef( jstring_ClipboardText ) ; } // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // 処理が完了していたらループを抜ける if( FinishClipboardProcess == 1 ) { break ; } } // 正常終了 return 0 ; } int android_main( void ) { // 背景の色をグレーにする SetBackgroundColor( 128,128,128 ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面にする SetDrawScreen( DX_SCREEN_BACK ) ; // クリップボードにテキストを設定する ClipboardText_Set( "テストテキスト" ) ; // メインループ while( ProcessMessage() == 0 ) { char StringBuffer[ 256 ] ; // 画面のクリア ClearDrawScreen() ; // クリップボードのテキストを取得 ClipboardText_Get( StringBuffer, sizeof( StringBuffer ) ) ; // クリップボードのテキストを画面に表示 DrawFormatString( 0, 0, GetColor( 255,255,255 ), "クリップボードのテキスト[ %s ]", StringBuffer ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )

  以上です。
  これでプロジェクトを実行すると、画面に『クリップボードのテキスト[ テストテキスト ]』と表示されます。

  詳細は割愛しますが、C++ から Java にアクセスする基本的な流れは。

   ① AttachCurrentThreadAsDaemon でソフト実行用スレッドと JavaVM を関連付け、JNIEnv も取得。

   ② GetObjectClass でアプリの Java クラスを取得。

   ③ GetMethodID や GetFieldID で Java の関数や変数の ID を取得。

   ④ Call???Method や Get???Field で Java の関数の呼び出しや変数の値を取得。

   ⑤ DeleteLocalRef で Get???Field で取得したオブジェクト系の変数の参照を削除。

   ⑥ DeleteLocalRef で GetObjectClass で取得したアプリの Java クラスの参照を削除。

   ⑦ DetachCurrentThread でソフト実行用スレッドと JavaVM の関連付けを終了。

  となります。
  C++ と Java でのやり取りを行う機能( JNI( Java Native Interface ) ) の詳しい扱い方については、検索サイトで
 『Android Java JNI』などのキーワードで検索して調べてみてください。

  ただ、今回のサンプルでは JNI を使用した処理は ClipboardText_Set と ClipboardText_Get に纏められているので、
 そのまま使う場合は JNI のことを意識しなくてもクリップボードの機能を使用することができます。







戻る