我尝试复制/迁移,Matsui Nobuyuki Matsui 先生在 GitHub 上上传的 AndAR 示例代码,“nmatsui / AR_Speeker”,可以从这里下载,我在 Android Studio (0.4.0) 上得到了一个奇怪的行为。提供的代码在 Eclipse (Juno) 中工作正常。
问题是我可以在 Android Studio 中毫无错误地编译,但是当我调试到我的设备时,我得到一个黑屏并且根本没有任何功能 - 没有相机预览 - 没有模型加载。
更重要的是,我在 logcat 中没有遇到致命错误,只要我能根据我对 java/android 的有限知识来解释这些错误。
我在这里寻求别人的帮助,因为我需要让这段代码在 Android Studio 上运行,所以我可以在大约 2 周的截止日期内构建一个练习应用程序。
有没有人成功地尝试为 Android Studio 复制/迁移“nmatsui / AR_Speeker”,并且可以为我提供一个 GitHub 链接,也许?
谢谢大家。
这是项目(结构):
这是我的 Android 清单(@drawable/icon 出现错误,我暂时通过工具修复它:ignore="MissingApplicationIcon"):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="jp.co.tis.stc"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" android:maxSdkVersion="10"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" />
<supports-screens android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true" />
<application
android:allowBackup="true"
android:label="AR_Speaker"
tools:ignore="MissingApplicationIcon">
<activity android:name=".AR_SpeakerActivity"
android:clearTaskOnLaunch="true"
android:noHistory="true"
android:screenOrientation="landscape"
android:label="AR_Speaker">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
这是 build.gradle.xml:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.7.+'
}
}
apply plugin: 'android'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile files('libs/AndAR.jar')}
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
instrumentTest.setRoot('tests')
}
}
Cannot assign ArrayList<String> to "HashSet<Iterable<?>>
我在第 23 行和第 24 行收到“'”的警告。
这是主要活动 AR_SpeakerActivity (所有评论都是日文,我无法解释!),其中显示第 123、125 行不推荐使用“getWidth()”和“getHeight()”的警告。我该怎么办?
按照 Raghav Sood 在第 123-124 行(现为 125-133)中的建议进行编辑。还是黑屏。我是否仍应在此代码段中使用“比率”?我应该如何编辑它?
package jp.co.tis.stc;
import android.app.ProgressDialog;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.WindowManager;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.AndARActivity;
import edu.dhbw.andobjviewer.graphics.LightingRenderer;
import jp.co.tis.stc.player.Elaine;
import jp.co.tis.stc.player.Porl;
import util.MarkerInfo;
public class AR_SpeakerActivity extends AndARActivity {
private static final float THRESHOLD = 50.0f;
// タップ位置とマーカー中心のズレの許容範囲 ピ ク セル)
private ARToolkit arToolkit;
private GestureDetector gd;
private SoundPool sp;
private float xRatio;
private float yRatio;
private List<PlayerBase> players = new ArrayList<PlayerBase>();
@SuppressWarnings("unchecked")
// AsyncTaskへplayersを可変引数として渡す際に、型パラメータが落ちるという警告を抑制
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gd = new GestureDetector(this, onGestureListener); // タップを検出するDetector
super.setNonARRenderer(new LightingRenderer());
arToolkit = super.getArtoolkit();
players.add(new Porl());
// マーカー・3Dモデル・音声・動作を定義したPlayer "Porl" を追加
players.add(new Elaine());
// マーカー・3Dモデル・音声・動作を定義したPlayer "Elaine" を追加
new ModelLoader().execute(players); // 非同期処理でPlayerを読み込み
}
@Override
protected void onResume() {
super.onResume();
// Activityが前面になったら音声再生用にSoundPoolを作成
sp = new SoundPool(players.size(), AudioManager.STREAM_MUSIC, 0);
sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
// SoundPoolの初期化が完了した際に呼ばれるコールバック関数を定義
@Override
public void onLoadComplete(SoundPool sp, int soundId, int status) {
Log.d("AR_Speaker", String.format("load complete soundId=%d:status=%d", soundId, status));
for (PlayerBase player : players) {
player.notifyLoadComplete(soundId);
// Playerに「このsoundIdの初期化が完了したよ」と通知
}
}
});
for (PlayerBase player : players) {
player.loadSound(this, sp); // すべてのPlayerの音声を読み込む
}
}
@Override
protected void onPause() {
sp.release(); // Activityが背面にまわったらSoundPoolを解放
super.onPause();
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e("AR_Speaker", ex.getMessage());
finish();
}
// 非同期処理でPlayerを読み込み
private class ModelLoader extends AsyncTask<List<PlayerBase>, Void, Void> {
private ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
// 非同期処理開始前にプログレスダイアログを表示
progressDialog = new ProgressDialog(AR_SpeakerActivity.this);
progressDialog.setMessage(AR_SpeakerActivity.this.getString(R.string.loading));
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
}
@Override
protected Void doInBackground(List<PlayerBase>... args) {
try {
for (PlayerBase player : args[0]) {
// Playerの3DモデルをARObjectとしてARToolkitへ登録する
arToolkit.registerARObject(player.getModel3d(getResources()));
}
} catch (Exception e) {
Log.e("AR_Speaker", e.getMessage());
finish();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// ARToolkitによって初期化されたcameraインスタンスから、カメラ座標系のパラメータを取得
Size cameraSize = camera.getParameters().getPreviewSize();
// WindowManagerから、スクリーン座標系のパラメータを取得
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
// スクリーン座標系もカメラ座標系も左上隅が原点位置
// カメラ座標系のX座標をスクリーン座標系のX座標へ変換する係数を計算
// Edit code as Raghav Sood suggested/
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
/**xRatio = (float) display.getWidth() / (float) cameraSize.width;
// カメラ座標系のY座標をスクリーン座標系のY座標へ変換する係数を計算
yRatio = (float) display.getHeight() / (float) cameraSize.height;
// 非同期処理が終了したので、プログレスダイアログを消去*/
progressDialog.dismiss();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return gd.onTouchEvent(event);
}
// タップ処理
private final SimpleOnGestureListener onGestureListener = new SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
// タップされた位置をスクリーン座標系として取得
float touchX = e.getX();
float touchY = e.getY();
// 認識しているすべてのマーカーの位置情報を取得
Map<Integer, MarkerInfo> markerInfos = arToolkit.getMarkerInfos();
for (MarkerInfo markerInfo : markerInfos.values()) {
// カメラ座標系でのマーカー位置(マーカーの中心点)を取得
float markerX = markerInfo.getPos()[0];
float markerY = markerInfo.getPos()[1];
// 認識しているマーカーの中心点をカメラ座標系からスクリーン座標系に変換し、
// |マーカーの中心 - タップ位置| < THRESHOLD
// であるかチェック
if (Math.abs(markerX * xRatio - touchX) < THRESHOLD
&& Math.abs(markerY * yRatio - touchY) < THRESHOLD) {
Log.d("AR_Speaker", String.format("marker %s is touched", markerInfo.getFileName()));
for (PlayerBase player : players) {
// すべてのPlayerに、「XXという名前のマーカーがタップされた」と通知
player.notifyTouch(new File(markerInfo.getFileName()).getName(), sp);
}
}
}
return true;
}
};}
这是另一个活动 PlayerBase:
package jp.co.tis.stc;
import java.io.BufferedReader;
import java.io.IOException;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.content.res.Resources;
import android.media.SoundPool;
import edu.dhbw.andobjviewer.graphics.Model3D;
import edu.dhbw.andobjviewer.models.Model;
import edu.dhbw.andobjviewer.parser.ObjParser;
import edu.dhbw.andobjviewer.parser.ParseException;
import edu.dhbw.andobjviewer.util.AssetsFileUtil;
import edu.dhbw.andobjviewer.util.BaseFileUtil;
public abstract class PlayerBase {
private static final double MARKER_WIDTH = 80.0;
private static final double[] MARKER_CENTER = new double[] { 0, 0 };
private final String modelFile;
private final String markerFile;
private final int voiceR;
private int soundId;
private boolean loaded = false;
protected boolean doAnimate = false;
public PlayerBase(String modelFile, String markerFile, int voiceR) {
this.modelFile = modelFile;
this.markerFile = markerFile;
this.voiceR = voiceR;
}
// WaveFront形式の3Dモデルファイルを読み込み、ARToolkitが認識できる3Dモデルを構築して返す
public Model3D getModel3d(Resources resource) throws IOException, ParseException {
BaseFileUtil fileUtil = new AssetsFileUtil(resource.getAssets());
Model3D model3D = null;
if (modelFile.endsWith(".obj")) {
ObjParser parser = new ObjParser(fileUtil);
if (fileUtil != null) {
BufferedReader fileReader = fileUtil.getReaderFromName(modelFile);
if (fileReader != null) {
// Wavefront形式の3Dモデルファイルから3Dモデルを構築
Model model = parser.parse("Model", fileReader);
// 3Dモデルファイルとマーカーを指定して、ARToolkitへ登録するためのModel3Dオブジェクトを作成する
model3D = new Model3D(model, markerFile, MARKER_WIDTH, MARKER_CENTER) {
private static final long serialVersionUID = 1L;
// Model3Dに仕掛けたフックの中身を定義
// 実際のanimate処理は、PlayerBaseを継承した具象クラスのanimateメソッドに実装することになる
@Override
protected void animate(GL10 gl) {
PlayerBase.this.animate(gl);
}
};
}
}
}
return model3D;
}
// animate処理の抽象メソッド
protected abstract void animate(GL10 gl);
// SoundPoolへ音声をロードするメソッド
public void loadSound(Context context, SoundPool sp) {
// res/rawに格納した音声ファイルを指定してロードすると、その音声のsoundIdが得られる
// このメソッドは実際のロードが完成する前にリターンする
soundId = sp.load(context, voiceR, 1);
}
// SoundPoolへの音声ロードが完成すると呼ばれるメソッド
public void notifyLoadComplete(int soundId) {
// 自分のsoundIdのロードが完了したのならば、loadedをtrueにする
if (this.soundId == soundId) loaded = true;
}
// マーカーがタップされた際に呼ばれるメソッド
public void notifyTouch(String fileName, SoundPool sp) {
// 自分のマーカーがタップされたのならば、自分のsoundIdを指定して音声を再生し、doAnimateをtrueにする
if (this.markerFile.equals(fileName) && loaded) {
sp.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f);
if (!this.doAnimate) this.doAnimate = true;
}
} }