Dialog bugfix

This commit is contained in:
cgspine
2020-02-12 01:37:55 +08:00
parent 9eb9fac369
commit da785c4ce2
5 changed files with 107 additions and 52 deletions
@@ -74,4 +74,27 @@ public class QMUIWindowHelper {
}
return null;
}
@Nullable
@SuppressWarnings({"JavaReflectionMemberAccess"})
public static Rect unSafeGetContentInsets(View view) {
try {
// Android P forbid the reflection for @hide filed,
// fortunately now it is in light greylist, just be warned.
Field field = View.class.getDeclaredField("mAttachInfo");
field.setAccessible(true);
Object attachInfo = field.get(view);
Field visibleInsetsField = attachInfo.getClass().getDeclaredField("mContentInsets");
visibleInsetsField.setAccessible(true);
Object visibleInsets = visibleInsetsField.get(attachInfo);
if (visibleInsets instanceof Rect) {
return (Rect) visibleInsets;
}
} catch (Throwable e) {
if (BuildConfig.DEBUG) {
e.printStackTrace();
}
}
return null;
}
}
@@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.qmuiteam.qmui.R;
@@ -52,6 +53,7 @@ import java.util.ArrayList;
import java.util.BitSet;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.appcompat.widget.AppCompatImageView;
@@ -143,7 +145,7 @@ public class QMUIDialog extends QMUIBaseDialog {
@Nullable
@Override
protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {
protected View onCreateContent(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {
if (mMessage != null && mMessage.length() != 0) {
QMUISpanTouchFixTextView tv = new QMUISpanTouchFixTextView(context);
assignMessageTvWithAttr(tv, hasTitle(), R.attr.qmui_dialog_message_content_style);
@@ -156,7 +158,7 @@ public class QMUIDialog extends QMUIBaseDialog {
@Nullable
@Override
protected View onCreateTitle(QMUIDialog dialog, QMUIDialogView parent, Context context) {
protected View onCreateTitle(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {
View tv = super.onCreateTitle(dialog, parent, context);
if (tv != null && (mMessage == null || mMessage.length() == 0)) {
TypedArray a = context.obtainStyledAttributes(null,
@@ -843,6 +845,8 @@ public class QMUIDialog extends QMUIBaseDialog {
*/
public static abstract class AutoResizeDialogBuilder extends QMUIDialogBuilder {
protected ScrollView mScrollView;
public AutoResizeDialogBuilder(Context context) {
super(context);
setCheckKeyboardOverlay(true);
@@ -851,7 +855,8 @@ public class QMUIDialog extends QMUIBaseDialog {
@Nullable
@Override
protected View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context) {
return wrapWithScroll(onBuildContent(dialog));
mScrollView = wrapWithScroll(onBuildContent(dialog));
return mScrollView;
}
public abstract View onBuildContent(QMUIDialog dialog);
@@ -103,12 +103,7 @@ public class QMUIDialogBlockBuilder extends QMUIDialogBuilder<QMUIDialogBlockBui
a.recycle();
}
contentTv.setText(mContent);
QMUIWrapContentScrollView scrollView = new QMUIWrapContentScrollView(context);
scrollView.setMaxHeight(getContentAreaMaxHeight());
scrollView.addView(contentTv);
return scrollView;
return wrapWithScroll(contentTv);
}
return null;
}
@@ -30,6 +30,7 @@ import com.qmuiteam.qmui.R;
import com.qmuiteam.qmui.layout.QMUIButton;
import com.qmuiteam.qmui.layout.QMUILinearLayout;
import com.qmuiteam.qmui.skin.QMUISkinHelper;
import com.qmuiteam.qmui.skin.QMUISkinManager;
import com.qmuiteam.qmui.skin.QMUISkinValueBuilder;
import com.qmuiteam.qmui.util.QMUIDisplayHelper;
import com.qmuiteam.qmui.util.QMUIResHelper;
@@ -92,9 +93,6 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
protected List<QMUIDialogAction> mActions = new ArrayList<>();
private QMUIDialogView.OnDecorationListener mOnDecorationListener;
private int mContentAreaMaxHeight = -1;
@Orientation private int mActionContainerOrientation = HORIZONTAL;
private boolean mChangeAlphaForPressOrDisable = true;
private int mActionDividerThickness = 0;
@@ -102,35 +100,18 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
private int mActionDividerInsetStart = 0;
private int mActionDividerInsetEnd = 0;
private boolean mCheckKeyboardOverlay = false;
private boolean mFollowSkin = true;
private QMUISkinManager mSkinManager;
private float mMaxPercent = 0.75f;
public QMUIDialogBuilder(Context context) {
this.mContext = context;
}
protected int getContentAreaMaxHeight() {
if (mContentAreaMaxHeight == -1) {
// 屏幕高度的0.85 - 预估的 title 和 action 高度
return (int) (QMUIDisplayHelper.getScreenHeight(mContext) * 0.85) - QMUIDisplayHelper.dp2px(mContext, 100);
}
return mContentAreaMaxHeight;
mSkinManager = QMUISkinManager.defaultInstance(context);
}
public Context getBaseContext() {
return mContext;
}
/**
* 设置内容区域最高的高度
*
* @param contentAreaMaxHeight
*/
public T setContentAreaMaxHeight(int contentAreaMaxHeight) {
mContentAreaMaxHeight = contentAreaMaxHeight;
return (T) this;
}
/**
* 设置对话框顶部的标题文字
*/
@@ -195,8 +176,8 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
}
@SuppressWarnings("unchecked")
public T setFollowSkin(boolean followSkin) {
mFollowSkin = followSkin;
public T setSkinManager(@Nullable QMUISkinManager skinManager) {
mSkinManager = skinManager;
return (T) this;
}
@@ -344,6 +325,12 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
mDialogView = onCreateDialogView(dialogContext);
mRootView = new QMUIDialogRootLayout(dialogContext, mDialogView, onCreateDialogLayoutParams());
mRootView.setCheckKeyboardOverlay(mCheckKeyboardOverlay);
mRootView.setOverlayOccurInMeasureCallback(new QMUIDialogRootLayout.OverlayOccurInMeasureCallback() {
@Override
public void call() {
onOverlayOccurredInMeasure();
}
});
mRootView.setMaxPercent(mMaxPercent);
configRootLayout(mRootView);
mDialogView = mRootView.getDialogView();
@@ -401,12 +388,16 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mDialog.setCancelable(mCancelable);
mDialog.setCanceledOnTouchOutside(mCanceledOnTouchOutside);
mDialog.setFollowSkin(mFollowSkin);
mDialog.setSkinManager(mSkinManager);
onAfterCreate(mDialog, mRootView, dialogContext);
return mDialog;
}
protected void onAfterCreate(QMUIDialog dialog, QMUIDialogRootLayout rootLayout, Context context){
protected void onAfterCreate(@NonNull QMUIDialog dialog, @NonNull QMUIDialogRootLayout rootLayout, @NonNull Context context){
}
protected void onOverlayOccurredInMeasure(){
}
@@ -416,12 +407,12 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
}
}
protected void configRootLayout(QMUIDialogRootLayout rootLayout){
protected void configRootLayout(@NonNull QMUIDialogRootLayout rootLayout){
}
@NonNull
protected QMUIDialogView onCreateDialogView(Context context){
protected QMUIDialogView onCreateDialogView(@NonNull Context context){
QMUIDialogView dialogView = new QMUIDialogView(context);
dialogView.setBackground(QMUIResHelper.getAttrDrawable(context, R.attr.qmui_skin_support_dialog_bg));
dialogView.setRadius(QMUIResHelper.getAttrDimen(context, R.attr.qmui_dialog_radius));
@@ -439,7 +430,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
}
@Nullable
protected View onCreateTitle(QMUIDialog dialog, QMUIDialogView parent, Context context) {
protected View onCreateTitle(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {
if (hasTitle()) {
TextView tv = new QMUISpanTouchFixTextView(context);
tv.setId(R.id.qmui_dialog_title_id);
@@ -456,7 +447,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
}
@NonNull
protected ConstraintLayout.LayoutParams onCreateTitleLayoutParams(Context context) {
protected ConstraintLayout.LayoutParams onCreateTitleLayoutParams(@NonNull Context context) {
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;
lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;
@@ -467,7 +458,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
@Nullable
protected abstract View onCreateContent(QMUIDialog dialog, QMUIDialogView parent, Context context);
protected abstract View onCreateContent(@NonNull QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context);
protected QMUIWrapContentScrollView wrapWithScroll(@NonNull View view){
@@ -477,7 +468,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
return scrollView;
}
protected ConstraintLayout.LayoutParams onCreateContentLayoutParams(Context context) {
protected ConstraintLayout.LayoutParams onCreateContentLayoutParams(@NonNull Context context) {
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;
lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;
@@ -487,7 +478,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
@Nullable
protected View onCreateOperatorLayout(final QMUIDialog dialog, QMUIDialogView parent, Context context) {
protected View onCreateOperatorLayout(@NonNull final QMUIDialog dialog, @NonNull QMUIDialogView parent, @NonNull Context context) {
int size = mActions.size();
if (size > 0) {
TypedArray a = context.obtainStyledAttributes(null, R.styleable.QMUIDialogActionContainerCustomDef, R.attr.qmui_dialog_action_container_style, 0);
@@ -593,7 +584,7 @@ public abstract class QMUIDialogBuilder<T extends QMUIDialogBuilder> {
}
@NonNull
protected ConstraintLayout.LayoutParams onCreateOperatorLayoutLayoutParams(Context context) {
protected ConstraintLayout.LayoutParams onCreateOperatorLayoutLayoutParams(@NonNull Context context) {
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.leftToLeft = ConstraintLayout.LayoutParams.PARENT_ID;
lp.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;
@@ -18,17 +18,18 @@ package com.qmuiteam.qmui.widget.dialog;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.qmuiteam.qmui.R;
import com.qmuiteam.qmui.util.QMUIDisplayHelper;
import com.qmuiteam.qmui.util.QMUIResHelper;
import com.qmuiteam.qmui.util.QMUIWindowHelper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class QMUIDialogRootLayout extends ViewGroup {
private QMUIDialogView mDialogView;
@@ -39,6 +40,9 @@ public class QMUIDialogRootLayout extends ViewGroup {
private int mInsetVer;
private boolean mCheckKeyboardOverlay = false;
private float mMaxPercent = 0.75f;
private boolean isOverlayOccurEventNotified = false;
private OverlayOccurInMeasureCallback mOverlayOccurInMeasureCallback;
private int mLastContentInsetTop = 0;
public QMUIDialogRootLayout(@NonNull Context context, @NonNull QMUIDialogView dialogView,
@@ -73,6 +77,10 @@ public class QMUIDialogRootLayout extends ViewGroup {
mInsetVer = insetVer;
}
public void setOverlayOccurInMeasureCallback(OverlayOccurInMeasureCallback overlayOccurInMeasureCallback) {
mOverlayOccurInMeasureCallback = overlayOccurInMeasureCallback;
}
public void setCheckKeyboardOverlay(boolean checkKeyboardOverlay) {
mCheckKeyboardOverlay = checkKeyboardOverlay;
}
@@ -84,11 +92,17 @@ public class QMUIDialogRootLayout extends ViewGroup {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int keyboardOverlayHeight = 0;
int contentInsetVer = 0;
if (mCheckKeyboardOverlay) {
Rect visibleInsetRect = QMUIWindowHelper.unSafeGetWindowVisibleInsets(this);
Rect contentInsetRect = QMUIWindowHelper.unSafeGetContentInsets(this);
if (visibleInsetRect != null) {
keyboardOverlayHeight = visibleInsetRect.bottom;
}
if (contentInsetRect != null) {
mLastContentInsetTop = contentInsetRect.top;
contentInsetVer = contentInsetRect.top + contentInsetRect.bottom;
}
}
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
@@ -111,13 +125,24 @@ public class QMUIDialogRootLayout extends ViewGroup {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mDialogViewLp.height, MeasureSpec.EXACTLY);
} else {
int childMaxHeight;
if(keyboardOverlayHeight > 0){
childMaxHeight = Math.max(heightSize - 2 * mInsetVer - keyboardOverlayHeight, 0);
}else{
if (keyboardOverlayHeight > 0) {
if (getRootView() != null && getRootView().getHeight() > 0) {
// the overlay occurred with this height, we can't change it.
heightSize = getRootView().getHeight();
if (!isOverlayOccurEventNotified) {
isOverlayOccurEventNotified = true;
if (mOverlayOccurInMeasureCallback != null) {
mOverlayOccurInMeasureCallback.call();
}
}
}
childMaxHeight = Math.max(heightSize - 2 * mInsetVer - keyboardOverlayHeight - contentInsetVer, 0);
} else {
// use maxPercent to keep dialog from being too high and calculated based on
// screen height because height size while change to actual height when multi onMeasure.
childMaxHeight = Math.min(heightSize - 2 * mInsetVer,
(int)(QMUIDisplayHelper.getScreenHeight(getContext()) * mMaxPercent - 2 * mInsetVer));
isOverlayOccurEventNotified = false;
childMaxHeight = Math.min(heightSize - 2 * mInsetVer - contentInsetVer,
(int) (QMUIDisplayHelper.getScreenHeight(getContext()) * mMaxPercent - 2 * mInsetVer));
}
if (mDialogViewLp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childMaxHeight, MeasureSpec.EXACTLY);
@@ -132,7 +157,7 @@ public class QMUIDialogRootLayout extends ViewGroup {
}
// InsetVer works when keyboard overlay occurs
setMeasuredDimension(mDialogView.getMeasuredWidth(),
mDialogView.getMeasuredHeight() + 2 * mInsetVer + keyboardOverlayHeight);
mDialogView.getMeasuredHeight() + 2 * mInsetVer + keyboardOverlayHeight + contentInsetVer);
}
@Override
@@ -147,4 +172,20 @@ public class QMUIDialogRootLayout extends ViewGroup {
public QMUIDialogView getDialogView() {
return mDialogView;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// I think this is a android system bug:
// When show keyboard In fullscreen and the content overlaps with keyboard,
// then the mAttachInfo.mContentInset.top equals notch's height
// but the event's y and draw position has different behavior if notch exist.
if (mLastContentInsetTop > 0) {
ev.offsetLocation(0, -mLastContentInsetTop);
}
return super.dispatchTouchEvent(ev);
}
interface OverlayOccurInMeasureCallback {
void call();
}
}