【问题标题】:Dispatch Touch event from DialogFragment view to parent activity view将触摸事件从 DialogFragment 视图分派到父活动视图
【发布时间】:2014-07-24 07:55:01
【问题描述】:

这是我的布局的样子:

我有一个父活动,它有一个自定义视图(view1 自己处理 onTouch 事件)和 2 个按钮(view2 和 view3)。 DialogFragment 具有显示的可见布局,其余部分是透明的。 我的对话框片段如下所示:

public class FragmentText extends DialogFragment{

  public static FragmentText newInstance() {
        FragmentText frag = new FragmentText();

        frag.setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Translucent_NoTitleBar);
        return frag;
    }

  @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);

        // instantiate the custom layout
        final View layout = inflater.inflate(R.layout.fragment_layout, null);

        .....
        }

}

布局文件如下所示:

<com.TransparentView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layMain"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_marginBottom="70dp"
    android:background="@color/transparent"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="70dp" >

    <LinearLayout
        android:id="@+id/layContent"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_margin="5dp"
        android:background="@drawable/bk_text_edit"
        android:orientation="vertical"
        android:padding="@dimen/margin" >

          all my layouts and buttons
    </LinearLayout>
</com.TransparentView>

public class TransparentView extends LinearLayout {

    @SuppressLint("NewApi")
    public TransparentView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public TransparentView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TransparentView(Context context) {
        super(context);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false; // event get propagated
    }

}

当用户在 DialogFragment 的可见布局之外按下时,我想: 1.关闭对话框片段 2. 将 onTouch 传递给父 Activity 并允许用户与视图交互。

所以基本上,如果我将手指拖到 View1 上,我想关闭对话框并继续与 view1 进行拖动交互。

这有可能实现吗?

LE:这也不起作用:

layMain.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            getActivity().dispatchTouchEvent(event);

            return false;
        }
    });

【问题讨论】:

  • 您可能希望删除DialogFragment 并将其作为浮动View 直接在处理触摸事件的自定义视图(子子视图)中实现。如果我没记错的话,你不能像现在这样通过这些接触。

标签: android android-fragments touch-event ontouchlistener android-dialogfragment


【解决方案1】:

我会使用警报对话框来代替您获得所需的效果:

AlertDialog.Builder Dialog = new AlertDialog.Builder(this);
        Dialog.setMessage("Text you wish to enter in the dialog"));
//Use this if you would like to include a button at the bottom of the alert dialog. Otherwise just leave it blank.
        Dialog.setNeutralButton("Back to App", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
        Dialog.show();

希望这会有所帮助:)

【讨论】:

  • 对不起,但这不是我的选择。我有一个复杂的对话框片段布局和对父活动的回调。
【解决方案2】:

您可以在对话框的透明视图中覆盖 boolean onInterceptTouchEvent(MotionEvent ev),捕获事件,将其发送到您需要的地方,关闭对话框并返回 false(这意味着未消耗触摸事件)。

或者,如果您不想覆盖 View 类,您可以在对话框中覆盖 boolean dispatchTouchEvent (MotionEvent ev) 并尝试通过分析 MotionEvent 对象找出点击发生的位置。

【讨论】:

  • 第一个想法听起来非常好,似乎正是我需要的......但它不起作用。我已经用一些示例代码更新了这个问题。我的猜测是对话框片段的行为与其他视图不同。
【解决方案3】:

如果您已经在LinearLayout 中成功捕获了触摸事件,我认为这是围绕Dialog 的透明视图,那么您应该能够通过返回父级Activity 的接口来完成此操作,并且对对话类中的公共方法的调用。我会做以下修改:

首先 - 在您的 TransparentView 中定义一个接口并添加一个公共方法来建立对您的父活动的回调。

public class TransparentView extends LinearLayout {

   public interface TouchCallback{
       public void onMotionEvent(MotionEvent e);
   }

   private TouchCallback mCallback;

   @SuppressLint("NewApi")
   public TransparentView(Context context, AttributeSet attrs, int defStyle) {
       super(context, attrs, defStyle);
   }

    public TransparentView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TransparentView(Context context) {
        super(context);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        callback.onMotionEvent(ev);
        return false; // event get propagated
    }

    public setCallback(TouchCallback callback){
        mCallback = callback;
    }

}

这个想法是捕捉运动事件并手动将其传递回您的托管活动。 将运动事件传递回活动后,您需要做的就是关闭对话框。 DialogFragment 已经内置了 dismiss method,只要您使用实例变量保留对对话框的引用,您应该能够执行以下操作

@Override
public void onMotionEvent(MotionEvent e){
    mDialog.dismiss();
}

如果这不起作用,只需在对话框类中添加一个方法并调用它。从您的示例中我可以看出,我们甚至不需要对透明视图捕获的运动事件做任何事情,因为我们要做的就是在发生触摸时关闭对话框。由于回调应该只在触摸发生时才被激活,因此无需检查(尽管您可以稍后扩展它以实现 Handler 并且仅在触摸持续指定的时间时才关闭对话框)。

【讨论】:

  • 感谢 Rarw 的回答。我实现了你的答案,但它没有按我的需要工作。 Activity 确实收到了回调,但实际的触摸并没有进入 Activity 的视图,所以基本上没有与按钮的交互。
  • 没问题。您可以使用原始设置执行此操作。我以前做过。问题似乎是您实际上并没有像您想象的那样捕捉触摸。一些日志标签会显示触摸停止的位置,我们可以从那里排除故障。我看到你切换到片段,但应该没有必要这样做。
【解决方案4】:

最后,正如 Luksprog 建议的那样,我最终放弃了 DialogFragment。我使用简单的 Fragment 进行了测试,该 Fragment 托管在活动的 FrameLayout 中,因此它看起来就像我需要它的覆盖层。通过这种方式,我仍然可以与其余视图进行所有我需要的交互。

感谢大家的支持。

【讨论】:

  • 我最初的想法是一个简单的视图(为了更容易定位),但你可以使用FragmentDialogFragment 也可以通过不使用 show() 方法而是使用 FragmentTransaction 来嵌入到布局中(就像普通片段一样)(这样你就可以使用你拥有的当前类)。
  • 请将此添加为答案,以便我将赏金分配给您。
【解决方案5】:

即使您放弃了 DialogFragment,这也可能对其他人有所帮助。

您需要获取实际的装饰视图并在那里拦截触摸事件:

 @Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);


    getDialog().getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            getActivity().dispatchTouchEvent(event);
            return false;
        }
    });

    return super.onCreateView(inflater, container, savedInstanceState);
}

【讨论】:

  • @Finn Marquardt 我有一个类似的用例。您的 getDialog().getWindow() 方法不能在 DialogFragment 维度内工作吗?我认为问题是关于在 DialogFragment 之外捕获触摸。
【解决方案6】:

BottomSheetDialog 也有同样的问题。我解决了我的问题:

final BottomSheetDialog dialog = getDialog();
final View touchOutside = dialog.findViewById(R.id.touch_outside);
touchOutside.setOnTouchListener((v, event) -> {
    getActivity().dispatchTouchEvent(event);
    return false;
});

所以看起来 Finn 的解决方案应该可以正常工作。

【讨论】:

  • 是的,不是,这个解决方案并非完美无缺 - 至少对我来说,当我点击 touch_outside 下方的输入时,它没有显示键盘......有人有解决方案吗?
猜你喜欢
  • 1970-01-01
  • 2017-04-28
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多