Избегание увольнения PopupWindow после касания снаружи
Я хотел бы использовать PopupWindow со следующими типами поведения:
- Он настраивается (имеет интерактивные элементы управления внутри, например, кнопок)
- В представлении "под" popupwindow необходимо правильно использовать штрихи вне всплывающего окна.
- .., но popupwindow должен оставаться на экране даже после нажатия на кнопку
Я нашел кучу сообщений о PopupWindow, но никто из них не задал вопрос, как справиться с такой ситуацией.
Думаю, я пробовал всевозможные комбинации setOutsideTouchable(), setFocusable(), setTouchable(), но я застрял. Popup имеет дело с щелчками на нем правильно, но он всегда убирается, когда касается снаружи.
Мой текущий код:
View.OnTouchListener customPopUpTouchListenr = new View.OnTouchListener(){
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
Log.d("POPUP", "Touch false");
return false;
}
};
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout= (LinearLayout)inflater.inflate(R.layout.insert_point_dialog, null);
PopupWindow pw = new PopupWindow(layout,400,200,true);
pw.setOutsideTouchable(true);
pw.setTouchable(true);
pw.setBackgroundDrawable(new BitmapDrawable());
pw.setTouchInterceptor(customPopUpTouchListenr);
pw.showAtLocation(frameLayout, Gravity.BOTTOM, 0, 0);
Моя общая цель - создать плавающее окно, которое ведет себя как "палитра инструментов" в программном обеспечении, таком как gimp: имеет некоторые элементы управления внутри, остается на вершине, пока не закрывается кнопкой "X", и позволяет взаимодействовать с элементами управления снаружи Это..
Может быть, есть лучший способ сделать это, а не PopupWindow? Но я все еще не нашел более подходящего контроля.
Ответы
Ответ 1
это слишком поздно, но для людей, которые google этот материал
просто измените порядок строк
pw.showAtLocation(frameLayout, Gravity.BOTTOM, 0, 0);
pw.setOutsideTouchable(true);
pw.setTouchable(true);
pw.setBackgroundDrawable(new BitmapDrawable());
pw.setTouchInterceptor(customPopUpTouchListenr);
вместо
pw.setOutsideTouchable(true);
pw.setTouchable(true);
pw.setBackgroundDrawable(new BitmapDrawable());
pw.setTouchInterceptor(customPopUpTouchListenr);
pw.showAtLocation(frameLayout, Gravity.BOTTOM, 0, 0);
размещение чего-либо после метода showatlocation делает его не похожим на
Ответ 2
Просто удалите pw.setBackgroundDrawable(new BitmapDrawable());
Ответ 3
pw.setOutsideTouchable(ложь);
Ответ 4
Пытаться
pw.setBackgroundDrawable(null);
Ответ 5
Решение:
popupWindow.setFocusable(истина);
popupWindow.update();
Благодаря:
http://android-er.blogspot.ch/2012/04/disable-outside-popupwindow-by.html
Ответ 6
Прежде всего, вы должны выяснить, почему popupWindow отклоняется, когда вы касаетесь снаружи.
После чтения исходного кода PopupWindow и файла ресурсов styles.xml,
<style name="Widget.PopupWindow">
<item name="popupBackground">@drawable/editbox_dropdown_background_dark</item>
<item name="popupAnimationStyle">@style/Animation.PopupWindow</item>
</style>
<style name="Widget">
<item name="textAppearance">?textAppearance</item>
</style>
Итак, нет ничего похожего на тему диалога:
<style name="Theme.Dialog">
<item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
</style name="Theme.Dialog">
Но Somethings происходят, когда вызывается PopupWindow.setBackgroundDrawable(),
private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
}
if (mBackground != null) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int height = ViewGroup.LayoutParams.MATCH_PARENT;
if (layoutParams != null &&
layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
// when a background is available, we embed the content view
// within another view that owns the background drawable
PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height
);
popupViewContainer.setBackgroundDrawable(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
}
mPopupViewInitialLayoutDirectionInherited =
(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
Создается представление контейнера "PopupViewContainer".
private class PopupViewContainer extends FrameLayout {
private static final String TAG = "PopupWindow.PopupViewContainer";
public PopupViewContainer(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
return true;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
}
}
Теперь вы знаете причину, которая имеет значение.
Таким образом, есть два способа, с помощью которых вы можете отключить всплывающее окно PopupWindow после касания снаружи.
1. Просто, как @Raaga. удалить pw.setBackgroundDrawable(новый BitmapDrawable());
- вы можете реализовать OnTouchListener для фильтрации событий касания вне PopupWindow.
Ответ 7
можно установитьFocusable (false) для PopupWindow
Кнопки по-прежнему доступны для кликов, но без визуального нажатия (какой-то пользовательский обработчик, чтобы заставить показывать клик?)
ниже - образец для плавающего окна с опцией "всегда сверху"
оригинальная компоновка рядом с плавающим окном полностью работоспособна в обоих случаях, более того, можно использовать диалоги и другие всплывающие окна, когда окно все еще плавает
также окно повторно используется
final static int buttonAlpha = 0xDF;
final static float buttonTextSize = 12f;
public final void addPopupButton(LinearLayout linearLayout, String title, android.view.View.OnClickListener onClickListener)
{
Button button = new Button(this.getContext());
button.setText(title);
button.setTextSize(buttonTextSize);
button.getBackground().setAlpha(buttonAlpha);
button.setOnClickListener(onClickListener);
linearLayout.addView(button, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
}
public final Button addPopupCheckbox(LinearLayout linearLayout, String title, boolean isChecked, android.view.View.OnClickListener onClickListener)
{
final Button button = new Button(getContext());
button.setText(title);
button.setTextSize(buttonTextSize);
final int buttonHeight = button.getHeight();
setButtonChecked(button, isChecked);
button.setHeight(buttonHeight);
button.getBackground().setAlpha(buttonAlpha);
button.setOnClickListener(onClickListener);
linearLayout.addView(button, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
return button;
}
public final void setButtonChecked(Button button, boolean isChecked)
{
button.setCompoundDrawablesWithIntrinsicBounds(Resources.getSystem().getIdentifier(isChecked ? "android:drawable/btn_check_on" : "android:drawable/btn_check_off", null, null), 0, 0, 0);
}
private boolean isMenuAlwaysOnTop = true;
private PopupWindow popupWindowMenuV2 = null;
public final void popupMenuNav2()
{
if (popupWindowMenuV2 == null)
{
// [start] layout
ScrollView scrollView = new ScrollView(this.getContext());
final LinearLayout linearLayoutNavigation = new LinearLayout(this.getContext());
linearLayoutNavigation.setOrientation(LinearLayout.VERTICAL);
linearLayoutNavigation.setBackgroundColor(0x7FFFFFFF);
linearLayoutNavigation.setPadding(20, 10, 20, 10);
scrollView.addView(linearLayoutNavigation, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
popupWindowMenuV2 = new PopupWindow(this);
popupWindowMenuV2.setBackgroundDrawable(new BitmapDrawable());
popupWindowMenuV2.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
popupWindowMenuV2.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
popupWindowMenuV2.setTouchable(true);
popupWindowMenuV2.setOutsideTouchable(!isMenuAlwaysOnTop);
popupWindowMenuV2.setFocusable(!isMenuAlwaysOnTop);
popupWindowMenuV2.setTouchInterceptor(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_OUTSIDE)
{
if (!isMenuAlwaysOnTop)
popupWindowMenuV2.dismiss();
else
return false;
return true;
}
return false;
}
});
popupWindowMenuV2.setContentView(scrollView);
// [end] layout
// [start] always on top checkbox
final Button buttonMenuAlwaysOnTop = addPopupCheckbox(linearLayoutNavigation, "always on top", isMenuAlwaysOnTop, null);
buttonMenuAlwaysOnTop.setOnClickListener(
new OnClickListener()
{
@Override
public void onClick(View vv)
{
isMenuAlwaysOnTop = !isMenuAlwaysOnTop;
setButtonChecked(buttonMenuAlwaysOnTop, isMenuAlwaysOnTop);
popupWindowMenuV2.dismiss();
popupWindowMenuV2.setOutsideTouchable(!isMenuAlwaysOnTop);
popupWindowMenuV2.setFocusable(!isMenuAlwaysOnTop);
popupWindowMenuV2.showAtLocation(((Activity) getContext()).getWindow().getDecorView(), Gravity.CENTER_VERTICAL + Gravity.RIGHT, 0, 0);
}
});
// [end] always on top checkbox
addPopupButton(linearLayoutNavigation, "some button",
new OnClickListener()
{
@Override
public void onClick(View vv)
{
if (!isMenuAlwaysOnTop)
popupWindowMenuV2.dismiss();
someAction();
}
});
}
popupWindowMenuV2.showAtLocation(((Activity) getContext()).getWindow().getDecorView(), Gravity.CENTER_VERTICAL + Gravity.RIGHT, 0, 0);
}
// somewhere in handler:
if (someCondition)
{
if (popupWindowMenuV2 != null && popupWindowMenuV2.isShowing())
popupWindowMenuV2.dismiss();
else
popupMenuNav2();
return true;
}
Ответ 8
Ничто не предлагалось здесь или где-то еще, казалось, работало для меня. Итак, я сделал это:
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getX() < 0 || motionEvent.getX() > viewWidth) return true;
if (motionEvent.getY() < 0 || motionEvent.getY() > viewHight) return true;
return false;
}
});
Если касание находится в пределах всплывающего окна, событие касания не используется (поэтому будут работать кнопки или просмотры прокрутки). Если касание находится за пределами границ, касание используется и не передается всплывающему окну, поэтому оно НЕ закрывается.