Android 防止AlertDialog显示在另一个DialogFragment的dim后面
我正在创建一个Android 防止AlertDialog显示在另一个DialogFragment的dim后面,android,android-dialogfragment,android-dialog,bottom-sheet,runtime-permissions,Android,Android Dialogfragment,Android Dialog,Bottom Sheet,Runtime Permissions,我正在创建一个BottomSheetDialogFragment,允许用户拍照或从他们的库中选择一张。要访问任一功能,需要WRITE\u EXTERNAL\u STORAGE权限 因此,我想向底部SheetDialogFragment请求权限,以防止用户在授予权限之前单击任何其他内容。如果我在onViewCreated中请求权限,则权限对话框将显示为罚款: 但是,如果权限被拒绝并且用户再次尝试,我会尝试在警报对话框中显示基本原理,但对话框会被阻止;大概是通过对话框片段中的dim实现的: pub
BottomSheetDialogFragment
,允许用户拍照或从他们的库中选择一张。要访问任一功能,需要WRITE\u EXTERNAL\u STORAGE
权限
因此,我想向底部SheetDialogFragment
请求权限,以防止用户在授予权限之前单击任何其他内容。如果我在onViewCreated
中请求权限,则权限对话框将显示为罚款:
但是,如果权限被拒绝并且用户再次尝试,我会尝试在警报对话框中显示基本原理,但对话框会被阻止;大概是通过对话框片段中的dim实现的:
public class ImageChooserDialogFragment extends BottomSheetDialogFragment {
public interface OnImageChosenListener {
void onImageChosen(Uri data);
}
private static final String PREFIX_IMAGE_CAPTURE = "IMG_";
private static final int REQUEST_PERMISSION_CAMERA = 0;
private static final int REQUEST_PERMISSION_STORAGE = 1;
private static final int REQUEST_IMAGE_CAPTURE = 2;
private static final int REQUEST_IMAGE_SELECTION = 3;
private static final String[] PERMISSIONS_CAMERA = new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA
};
private static final String[] PERMISSIONS_STORAGE = new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private boolean hasFeatureCamera;
private Uri mCurrentPhotoResource;
private View mView;
private OnImageChosenListener mOnImageChosenListener;
public static ImageChooserDialogFragment newInstance() {
return new ImageChooserDialogFragment();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Timber.d("onAttach");
if(context instanceof OnImageChosenListener) {
mOnImageChosenListener = (OnImageChosenListener) context;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment parent = getParentFragment();
if(parent != null) {
onAttachToFragment(parent);
}
PackageManager manager = getContext().getPackageManager();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
} else {
hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|| manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}
}
public void onAttachToFragment(Fragment fragment) {
if(fragment instanceof OnImageChosenListener) {
mOnImageChosenListener = (OnImageChosenListener) fragment;
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.dialog_image_chooser, container, false);
ButterKnife.bind(this, mView);
if(!hasFeatureCamera) {
mView.setVisibility(View.GONE);
}
return mView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE);
} else if(!hasFeatureCamera) {
dispatchImageSelectionIntent();
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch(requestCode) {
case REQUEST_PERMISSION_CAMERA:
if(PermissionUtil.verifyPermissions(grantResults)) {
dispatchImageCaptureIntent();
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_camera);
} break;
case REQUEST_PERMISSION_STORAGE:
if(PermissionUtil.verifyPermissions(grantResults)) {
if(!hasFeatureCamera) {
dispatchImageSelectionIntent();
}
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage);
} break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_IMAGE_CAPTURE:
if(resultCode == Activity.RESULT_OK) {
handleImageCaptureResult(data);
} else {
destroyTemporaryFile();
} break;
case REQUEST_IMAGE_SELECTION:
if(resultCode == Activity.RESULT_OK) {
handleImageSelectionResult(data);
} break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
@OnClick(R.id.photo_take)
public void onClickCapture() {
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_CAMERA)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_CAMERA, PERMISSIONS_CAMERA);
} else {
dispatchImageCaptureIntent();
}
}
@OnClick(R.id.photo_choose)
public void onClickChoose() {
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE);
} else {
dispatchImageSelectionIntent();
}
}
private void dispatchImageCaptureIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
try {
File image = createTemporaryFile();
Uri data = FileProvider.getUriForFile(getContext(), "com.example.app.fileprovider", image);
intent.putExtra(MediaStore.EXTRA_OUTPUT, data);
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
} catch (IOException exception) {
Timber.w(exception, "Error occurred while creating image file");
}
} else {
// TODO: handle no application to handle intent
}
}
private void dispatchImageSelectionIntent() {
final Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_IMAGE_SELECTION);
} else {
// TODO: handle no application to handle intent
} dismiss();
}
private void dispatchDetailSettingsIntent() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getContext().getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
startActivity(intent);
} else {
// TODO: handle no application to handle intent
} dismiss();
}
private void dispatchMediaScanIntent(Uri data) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data);
getContext().sendBroadcast(intent);
}
private void displayPermissionsRationale(int requestCode) {
switch (requestCode) {
case REQUEST_PERMISSION_CAMERA:
Timber.d("Request Image capture rationale");
DialogFactory.createRationaleAlert(getContext(),
R.string.title_dialog_rationale_camera,
R.string.msg_dialog_rationale_camera).show();
break;
case REQUEST_PERMISSION_STORAGE:
Timber.d("Request Image selection rationale");
DialogFactory.createRationaleAlert(getContext(),
R.string.title_dialog_rationale_storage,
R.string.msg_dialog_rationale_storage).show();
break;
default:
Timber.d("No rationale");
}
}
private void displayRequestPermissionsAlert(@StringRes int message) {
Snackbar.make(mView, message, Snackbar.LENGTH_LONG)
.setAction(R.string.action_settings, view -> dispatchDetailSettingsIntent()).show();
dismiss();
}
private void requestPermissionsWithRationale(int requestCode, @NonNull String[] permissions) {
Timber.d("Request permissions with rationale");
if(PermissionUtil.shouldShowRequestPermissionsRationale(this, permissions)) {
Timber.d("Display rationale");
displayPermissionsRationale(requestCode);
} else {
Timber.d("Request Permissions");
requestPermissions(permissions, requestCode);
}
}
private File createTemporaryFile() throws IOException {
String fileName = PREFIX_IMAGE_CAPTURE /*+ TimeUtil.getTimeStamp()*/;
File directory = getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM);
File file = File.createTempFile(fileName, ".jpeg", directory);
mCurrentPhotoResource = Uri.fromFile(file);
return file;
}
private void destroyTemporaryFile() {
File file = new File(mCurrentPhotoResource.getPath());
if(file.delete()) {
Timber.i("Temporary file deleted");
} else {
Timber.w("Failed to delete temporary file: " + file);
}
}
private void handleImageCaptureResult(Intent intent) {
if(mCurrentPhotoResource != null) {
dispatchMediaScanIntent(mCurrentPhotoResource);
if (mOnImageChosenListener != null) {
mOnImageChosenListener.onImageChosen(mCurrentPhotoResource);
} else {
Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; captured result cannot be used");
}
}
}
private void handleImageSelectionResult(Intent intent) {
Timber.d("Selection: " + intent.getData());
if(mOnImageChosenListener != null) {
mOnImageChosenListener.onImageChosen(intent.getData());
} else {
Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; selected result cannot be used");
}
}
}
我认为这是由BottomSheetDialogFragment
的动画造成的,该动画在片段完成动画之前不会显示背景暗显。这恰好发生在创建视图之后。是否有人知道是否有办法在不关闭或关闭对话框片段的情况下将AlertDialog
强制到前面?或者是否有办法监听BottomSheetDialogFragment
动画完成
我知道我可以在添加BottomSheetDialogFragment
之前请求权限,但我宁愿在对话框中请求权限,以便为用户提供一些上下文
以下是片段
:
public class ImageChooserDialogFragment extends BottomSheetDialogFragment {
public interface OnImageChosenListener {
void onImageChosen(Uri data);
}
private static final String PREFIX_IMAGE_CAPTURE = "IMG_";
private static final int REQUEST_PERMISSION_CAMERA = 0;
private static final int REQUEST_PERMISSION_STORAGE = 1;
private static final int REQUEST_IMAGE_CAPTURE = 2;
private static final int REQUEST_IMAGE_SELECTION = 3;
private static final String[] PERMISSIONS_CAMERA = new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA
};
private static final String[] PERMISSIONS_STORAGE = new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private boolean hasFeatureCamera;
private Uri mCurrentPhotoResource;
private View mView;
private OnImageChosenListener mOnImageChosenListener;
public static ImageChooserDialogFragment newInstance() {
return new ImageChooserDialogFragment();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Timber.d("onAttach");
if(context instanceof OnImageChosenListener) {
mOnImageChosenListener = (OnImageChosenListener) context;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment parent = getParentFragment();
if(parent != null) {
onAttachToFragment(parent);
}
PackageManager manager = getContext().getPackageManager();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
} else {
hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|| manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}
}
public void onAttachToFragment(Fragment fragment) {
if(fragment instanceof OnImageChosenListener) {
mOnImageChosenListener = (OnImageChosenListener) fragment;
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.dialog_image_chooser, container, false);
ButterKnife.bind(this, mView);
if(!hasFeatureCamera) {
mView.setVisibility(View.GONE);
}
return mView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE);
} else if(!hasFeatureCamera) {
dispatchImageSelectionIntent();
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch(requestCode) {
case REQUEST_PERMISSION_CAMERA:
if(PermissionUtil.verifyPermissions(grantResults)) {
dispatchImageCaptureIntent();
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_camera);
} break;
case REQUEST_PERMISSION_STORAGE:
if(PermissionUtil.verifyPermissions(grantResults)) {
if(!hasFeatureCamera) {
dispatchImageSelectionIntent();
}
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage);
} break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_IMAGE_CAPTURE:
if(resultCode == Activity.RESULT_OK) {
handleImageCaptureResult(data);
} else {
destroyTemporaryFile();
} break;
case REQUEST_IMAGE_SELECTION:
if(resultCode == Activity.RESULT_OK) {
handleImageSelectionResult(data);
} break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
@OnClick(R.id.photo_take)
public void onClickCapture() {
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_CAMERA)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_CAMERA, PERMISSIONS_CAMERA);
} else {
dispatchImageCaptureIntent();
}
}
@OnClick(R.id.photo_choose)
public void onClickChoose() {
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE);
} else {
dispatchImageSelectionIntent();
}
}
private void dispatchImageCaptureIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
try {
File image = createTemporaryFile();
Uri data = FileProvider.getUriForFile(getContext(), "com.example.app.fileprovider", image);
intent.putExtra(MediaStore.EXTRA_OUTPUT, data);
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
} catch (IOException exception) {
Timber.w(exception, "Error occurred while creating image file");
}
} else {
// TODO: handle no application to handle intent
}
}
private void dispatchImageSelectionIntent() {
final Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_IMAGE_SELECTION);
} else {
// TODO: handle no application to handle intent
} dismiss();
}
private void dispatchDetailSettingsIntent() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getContext().getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(intent.resolveActivity(getContext().getPackageManager()) != null) {
startActivity(intent);
} else {
// TODO: handle no application to handle intent
} dismiss();
}
private void dispatchMediaScanIntent(Uri data) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data);
getContext().sendBroadcast(intent);
}
private void displayPermissionsRationale(int requestCode) {
switch (requestCode) {
case REQUEST_PERMISSION_CAMERA:
Timber.d("Request Image capture rationale");
DialogFactory.createRationaleAlert(getContext(),
R.string.title_dialog_rationale_camera,
R.string.msg_dialog_rationale_camera).show();
break;
case REQUEST_PERMISSION_STORAGE:
Timber.d("Request Image selection rationale");
DialogFactory.createRationaleAlert(getContext(),
R.string.title_dialog_rationale_storage,
R.string.msg_dialog_rationale_storage).show();
break;
default:
Timber.d("No rationale");
}
}
private void displayRequestPermissionsAlert(@StringRes int message) {
Snackbar.make(mView, message, Snackbar.LENGTH_LONG)
.setAction(R.string.action_settings, view -> dispatchDetailSettingsIntent()).show();
dismiss();
}
private void requestPermissionsWithRationale(int requestCode, @NonNull String[] permissions) {
Timber.d("Request permissions with rationale");
if(PermissionUtil.shouldShowRequestPermissionsRationale(this, permissions)) {
Timber.d("Display rationale");
displayPermissionsRationale(requestCode);
} else {
Timber.d("Request Permissions");
requestPermissions(permissions, requestCode);
}
}
private File createTemporaryFile() throws IOException {
String fileName = PREFIX_IMAGE_CAPTURE /*+ TimeUtil.getTimeStamp()*/;
File directory = getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM);
File file = File.createTempFile(fileName, ".jpeg", directory);
mCurrentPhotoResource = Uri.fromFile(file);
return file;
}
private void destroyTemporaryFile() {
File file = new File(mCurrentPhotoResource.getPath());
if(file.delete()) {
Timber.i("Temporary file deleted");
} else {
Timber.w("Failed to delete temporary file: " + file);
}
}
private void handleImageCaptureResult(Intent intent) {
if(mCurrentPhotoResource != null) {
dispatchMediaScanIntent(mCurrentPhotoResource);
if (mOnImageChosenListener != null) {
mOnImageChosenListener.onImageChosen(mCurrentPhotoResource);
} else {
Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; captured result cannot be used");
}
}
}
private void handleImageSelectionResult(Intent intent) {
Timber.d("Selection: " + intent.getData());
if(mOnImageChosenListener != null) {
mOnImageChosenListener.onImageChosen(intent.getData());
} else {
Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; selected result cannot be used");
}
}
}
以及对话框工厂
类:
public final class DialogFactory {
public static AlertDialog createRationaleAlert(
Context context, @StringRes int title, @StringRes int message) {
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(title).setMessage(message);
return createRationaleAlert(context, builder);
}
public static AlertDialog createRationaleAlert(
Context context, CharSequence title, CharSequence message) {
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(title).setMessage(message);
return createRationaleAlert(context, builder);
}
private static AlertDialog createRationaleAlert(
Context context, AlertDialog.Builder builder) {
builder.setPositiveButton(R.string.btn_try, (dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
context.startActivity(intent);
}).setNegativeButton(R.string.btn_cancel, (dialog, which) -> {
dialog.cancel();
});
return builder.create();
}
}
当片段
调用DialogFactory.CreateOrionaleAlert().show()
时,就会出现此问题
有没有办法监听片段动画的结束
经过深思熟虑,我终于找到了解决办法。诀窍是实现,并在onShow()
回调中创建新的对话框:
public class ImageChooserDialogFragment extends BottomSheetDialogFragment
implements DialogInterface.OnShowListener {
@Override @NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
getDialog().setOnShowListener(this);
}
@Override
public void onShow(DialogInterface dialog) {
if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) {
requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE);
} else {
displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage);
}
}
// ...
}
将对话框创建为类型\系统\警报
使用以下命令:
builder.create().getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
禁用负责暗淡背景的标志
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG\u DIM\u落后) 我的处境也一样。使用了什么解决方案?@AitorGómez没有。当前,我仅在授予权限后显示BottomSheetDialogFragment
。我尝试将对话框放入从第一个DialogFragment调用的子DialogFragment中,但也不起作用。谢谢。@Bryan,你能用这种行为发布一个简单的项目吗?@azizbekian用代码更新了。这似乎不起作用。首先,显然没有为初始状态调用onStateChanged()
;因此,除非我自己设置状态,否则在移动BottomSheetDialog
之前不会调用它。其次,如果我确实将状态设置为state\u EXPANDED
,出于某种原因,我只会收到state\u setting
的回调。如果我实际将对话框移动到展开状态,我只会收到一个STATE\u EXPANDED
的回调。不过,考虑到如果我手动将对话框移动到STATE\u EXPANDED
,这确实证明了我的怀疑,这与对话框的动画有关,它确实会在背景dim上方显示“基本原理”对话框。然后,可能有一种解决方案(有点老套)是在启动“基本原理”对话框之前手动调用setState。我无法解释您得到的行为(如果您没有从侦听器中操作底部表单),这只是抛出一个BadTokenException
,并显示消息“无法添加窗口android.view.ViewRootImpl”$W@5ae4ab--窗口类型2003的权限被拒绝”。这使对话框本身的背景透明,不是对话框的背景尺寸。请尝试此alertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_dim_BEHIND);