После изменения моего целевого Android API на 26 мое приложение падает при записи с использованием внешнего FileHandle.У меня это работало при нацеливании на более низкий API (20), добавив их в мой AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Но через несколько месяцев Google изменил минимальный API на 26, что требует обработки прав пользователя во время выполнения, поэтомуЗаписи AndroidManifest больше не достаточно, чтобы заставить ее работать.
У меня нет опыта работы с кодом, специфичным для Android, потому что все, что мне нужно было сделать до этого момента, я смог сделать полностью в LibGDX.Вот учебник, который я нашел для разрешения на внешнее чтение в Android, но я не уверен, как заставить это работать в проекте LigGDX (я не смог найти никаких руководств по LibGDX по этому вопросу).Это то, что мне нужно сделать, и это будет идти в AndroidLauncher.java или ему нужен отдельный файл в проекте Android?
Код: https://codinginflow.com/tutorials/android/run-time-permission-request
Ассоциированное видеоУчебное пособие: https://www.youtube.com/watch?v=SMrB97JuIoM
Вот мой простой пример LibGDX, который работает, если я нацеливаюсь на более низкий API (20), но происходит сбой, когда я изменяю его на API 26, потому что я не обрабатываю права пользователя во время выполнения.В моем примере нажатие кнопки «PRESS» приводит к сохранению строки в текстовом документе через JSON.Затем строка считывается обратно из текстового файла в метку и отображается на экране.Счетчик увеличивается при каждом нажатии кнопки, чтобы показать, что произошло новое событие сохранения и загрузки.
Если кто-то может помочь заставить мою программу-пример работать с API 26 (или более поздней версии) или предоставить другой рабочий пример вLibGDX, который я мог бы загрузить, запустить и проверить, я был бы очень признателен.Спасибо.
Test.java
public class Test implements ApplicationListener {
private Stage stage;
private Window window;
private Graphics graphics;
@Override
public void create () {
stage = new Stage();
stage.setViewport(new ScreenViewport(stage.getViewport().getCamera()));
Gdx.input.setInputProcessor(stage);
graphics = new Graphics();
window = new Window(graphics);
stage.addActor(window);
}
@Override
public void render () {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getRawDeltaTime());
stage.draw();
}
@Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
@Override
public void pause() { }
@Override
public void resume() { }
@Override
public void dispose () {
stage.dispose();
graphics.dispose();
}
}
Window.java
public class Window extends Table {
Graphics graphics;
FileHandle handle;
Label label;
int count=0;
public Window(Graphics graphics){
setFillParent(true);
this.graphics = graphics;
handle = Gdx.files.getFileHandle("ZTEST/test.txt", getFileType());
// label
Label.LabelStyle labelStyle = new Label.LabelStyle();
labelStyle.font = graphics.getFont();
labelStyle.fontColor = Color.WHITE;
label = new Label("", labelStyle);
label.setAlignment(Align.center);
add(label).size(graphics.programWidth*0.4f,graphics.programWidth*0.14f);
label.debug();
row().pad(graphics.programWidth*0.1f);
// button
TextButton.TextButtonStyle buttonStyle = new TextButton.TextButtonStyle();
buttonStyle.font = graphics.getFont();
buttonStyle.fontColor = Color.WHITE;
final TextButton button = new TextButton("PRESS", buttonStyle);
button.addListener(new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y){
// SAVE TO DISK
save(++count);
// LOAD FROM DISK
load();
}
});
add(button).size(graphics.programWidth*0.4f,graphics.programWidth*0.14f);
button.debug();
}
private Files.FileType getFileType(){
switch(Gdx.app.getType()){
case Desktop:
return Files.FileType.Local;
default:
return Files.FileType.External;
}
}
private void save(int count){
Json json = new Json();
json.setOutputType(JsonWriter.OutputType.json);
handle.writeString(json.prettyPrint("save: "+count), false);
}
private void load(){
if(handle.exists()){
final Json json = new Json();
label.setText(json.fromJson(null, handle.readString()).toString());
}
}
}
Graphics.java (Создает растровый шрифт)
public class Graphics implements Disposable {
public final int programWidth;
BitmapFont font;
public Graphics(){
programWidth = (Gdx.graphics.getWidth() < Gdx.graphics.getHeight()) ? Gdx.graphics.getWidth() : Gdx.graphics.getHeight();
font = generateNumberFont();
}
private BitmapFont generateNumberFont(){
final FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("internal/arialbd.ttf"));
final FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.characters = "abcdefthijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890:";
parameter.size = (int)(programWidth*0.08f);
final BitmapFont font = generator.generateFont(parameter);
generator.dispose();
return font;
}
public BitmapFont getFont(){ return font; }
@Override
public void dispose() {
font.dispose();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tekker.test" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/GdxTheme" >
<activity
android:name="com.tekker.test.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="fullUser"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>