У меня есть для l oop, который на каждой итерации пытается:
- преобразовать макет CalendarCanvas в растровое изображение и записать его в папку Screenshots внешнего хранилища, а также
- обновите макет, нажав кнопку в этом действии NextMonthButton
Где из предыдущих действий были получены начальный месяц и конечный месяц, а методы NextMonthButton.onclick () и UpdateCalendarCanvas обновляют пользовательский интерфейс в стиле календаря , См. Код ниже:
private void SetImportGalleryButtonClick() {
ImportGalleryButton.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public void onClick(View v) {
Log.d("YourCalendarActivity", "ImportGalleryButton Clicked");
//Bring to first month
//Use mutex to ensure actions continue only after UI has updated.
final Semaphore mutev = new Semaphore(0);
YourCalendarActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
UpdateCalendarCanvas(Courses.get(0).getStartDate());
Log.d("YourCalendarActivity","ImportGallery: startmonth reached");
mutev.release();
}
});
try {
mutev.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("YourCalendarActivity","ImportGallery: error reaching startmonth with mutev Semaphore");
}
//Check user permissions and request if needed
isStoragePermissionGranted();
Calendar startmonth = Calendar.getInstance();
startmonth.setTime(Courses.get(0).getStartDate());
Log.d("YourCalendarActivity", "ImportGalleryButton: First month is: "+startmonth);
Calendar lastmonth = Calendar.getInstance();
lastmonth.setTime(Courses.get(0).getEndDate());
Log.d("YourCalendarActivity", "ImportGalleryButton: Last month is: "+lastmonth);
final int month_duration = lastmonth.get(Calendar.MONTH)-startmonth.get(Calendar.MONTH)+1;
Log.d("YourCalendarActivity","ImportGallery: month duration is "+month_duration);
//Save all month views as a PNG
for (int i = 0; i < month_duration; i++){
//Create the bitmap image of the current configuration of CalendarCanvas to be saved
//Use the thread to ensure that following actions only continue after drawing has finished
final String filename = "Structify"+(i+1)+".png";
Thread t = new Thread() {
public void run() {
CalendarCanvas.setDrawingCacheEnabled(true);
CalendarCanvas.setBackgroundColor(Color.WHITE);
Bitmap b = CalendarCanvas.getDrawingCache();
File sdCard = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_SCREENSHOTS);
if (!sdCard.exists()){
sdCard.mkdirs();
}
File file = new File(sdCard, filename);
if (!file.exists()){
try {
file.createNewFile();
Log.d("YourCalendarActivity","New file in gallery created");
} catch (IOException e) {
e.printStackTrace();
Log.d("YourCalendarActivity","Error creating new file in gallery");
}
}
FileOutputStream fos;
try {
fos = new FileOutputStream(file);
b.compress(Bitmap.CompressFormat.PNG, 95, fos);
fos.flush();
fos.close();
Log.d("YourCalendarActivity","Calendar "+filename+" added to Gallery");
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.d("YourCalendarActivity","Error importing to Gallery - File not found");
} catch (IOException e) {
e.printStackTrace();
Log.d("YourCalendarActivity","Error importing to Gallery - IO error");
}
}
};
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("YourCalendarActivity","InterruptedException joining Bitmap drawing's thread");
}
//Use mutex to ensure actions continue only after UI has updated.
final Semaphore mutex = new Semaphore(0);
YourCalendarActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
NextMonthButton.performClick();
mutex.release();
}
});
if ( i != month_duration-1 ){
try {
mutex.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Toast.makeText(YourCalendarActivity.this,"Calendars for all months have been imported to your gallery!", Toast.LENGTH_SHORT).show();
Log.d("YourCalendarActivity","All calendar images imported to gallery");
}
});
}
Все работает нормально:
- Чередующиеся операции обновления пользовательского интерфейса и записи в файл не выполняются одновременно. Я использовал семафоры и потоки, чтобы гарантировать, что запись в файл происходит только после того, как все изменения пользовательского интерфейса завершены, и учитывая сообщения журнала, я склонен полагать, что это работает, как предполагалось.
- FileOutputStream создает файл, как ожидается в каталог под названием Screenshots, как указано. Это выглядит грубым, но создает вывод - , безусловно, открыт для предложений по улучшению.
- Разрешения внешнего хранилища работают нормально. Соответствующее предложение было включено в папку Manifest, и, как видно из кода, я также динамически запрашиваю разрешение во время выполнения по мере необходимости - сообщение журнала в isStoragePermissionGranted () указывает, что действие действительно имеет разрешение, и этот метод, конечно, будет запросить разрешение, если оно недоступно.
Остальные проблемы:
- В галерею выводится только один файл, а не кратный ожидаемому, как указано в month_duration. переменная в методе. Иногда, когда я открываю галерею после запуска этого метода, я вижу, что она загружает ожидаемое количество картинок, но когда она улаживается, всегда остается только одна картинка. Сначала я думал, что обновление и запись пользовательского интерфейса будут происходить последовательно, а не одновременно, чтобы пролить некоторый свет на эту проблему - я ошибся.
- Единственное изображение, которое генерируется, всегда полностью белое. Раньше он был полностью черным, за исключением цветных элементов исходного макета CalendarCanvas, и, читая сообщения здесь, я обнаружил, что PNG по умолчанию являются черными (или что-то в этом роде), поэтому перед записью я установил для CalendarCanvas белый фон.
Кто-нибудь есть какие-либо предложения относительно того, что может быть причиной проблем выше? Все идеи приветствуются, и я хотел бы услышать лучшие практики для пути fileoutputstream в 2020 году, если кто-нибудь знает, так как большинство постов, которые я читал в этой теме, были примерно с 2010 года.