【问题标题】:Passing a listener to a custom Fragment in Android将侦听器传递给 Android 中的自定义片段
【发布时间】:2018-03-17 04:00:40
【问题描述】:

我正在我的应用程序中创建一个视图寻呼机,并使用一个在其上扩展 Fragment 的类。当我创建一个实例时,我可以传递所有元素(图像、文本等)并将其与 Bundle 一起存储以在 onCreate 中使用它。但我无法在片段中存储按钮的侦听器。这是我的课:

public class RegWizardFragmentInfo extends Fragment {

private static final String IMAGE = "image";
private static final String TEXT = "text";
private static final String BUTTON = "buttonText";
private View.OnClickListener buttonCallBack;

private Button button;
private int image;
private int text;
private int buttonText;


public RegWizardFragmentInfo newInstance(int image, int text, int buttonText, View.OnClickListener callback) {

    RegWizardFragmentInfo fragment = new RegWizardFragmentInfo();
    Bundle bundle = new Bundle();
    bundle.putInt(IMAGE, image);
    bundle.putInt(BUTTON, buttonText);
    bundle.putInt(TEXT, text);
    fragment.setArguments(bundle);
    fragment.setRetainInstance(true);
    return fragment;

}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.onActivityCreated(savedInstanceState);
    this.image = getArguments().getInt(IMAGE);
    this.text = (getArguments() != null) ? getArguments().getInt(TEXT)
            : -1;
    this.buttonText = (getArguments() != null) ? getArguments().getInt(BUTTON)
            : -1;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    ViewGroup rootView = (ViewGroup) inflater.inflate(
            R.layout.fragment, container, false);
    //Extract all the views and add the image and texts

    return rootView;

}

那么,如何存储我在 newInstance 中获得的侦听器以将其添加到 onCreateView 方法上的按钮?

感谢您的帮助。

【问题讨论】:

  • 为什么不在fragment本身中定义OnClickListener来处理点击呢?我认为在托管 UI 的活动/片段之外的其他地方有任何 UI 回调是不好的做法。
  • @Headcracker 我需要从活动中传递它。

标签: java android android-fragments fragment


【解决方案1】:

您可以在Fragment 中使用回调:

public class RegWizardFragmentInfo extends Fragment {

    private Button button;

    private OnClickCallback callback;

    public interface OnClickCallback {
        void onClick();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        callback = (OnClickCallback) context;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callback.onClick();
            }
        });
    }
}

并在你的父级Activity中实现这个新接口

【讨论】:

    【解决方案2】:

    其他答案分配onAttach 中的侦听器。虽然这会起作用,但它要求调用 Activity(而不是匿名类)实现您的接口。此外,它会迫使您将onAttach 中提供给您的Context 强制转换为您的接口实例,这可能导致崩溃并且通常被认为是错误的形式。您可以改为创建一个方法来在您的 Fragment 中设置侦听器:

    public class RegWizardFragmentInfo extends Fragment {
    
        private OnClickListener mListener;
    
        public interface OnClickListener {
            void onClick();
        }
    
        /**
         * Call at any time after this fragment has been constructed.
         */
        public void setListener(OnClickListener listener) {
            mListener = listener;
        }
    
        /* ...other stuff... */
    }
    

    我可以想到这种方法的三个缺点:

    1. 每次要实例化Fragment 时都需要调用一个额外的方法。
    2. 您不能保证随时设置mListener。您可能需要在您的 Fragment 代码中添加空检查。
    3. 您需要小心确保侦听器在屏幕旋转等生命周期事件之后保持设置状态。

    【讨论】:

    • 但是你不能保证当你用按钮注册它时会设置mListener。
    • @MattD 至少当您的活动为 1k LOC 时,您不会花时间查看哪个 @Override 属于哪个接口,而是将侦听器作为类字段。 IMO,更好的方法。
    • UPD:​​> 一个月后,我撤回了我的论点。该解决方案无法在方向更改后继续存在。我的建议:要么使用不太好但快速的onAttach() 解决方案转换上下文,要么选择使用ViewModel 的更好方法,因为它可以在方向变化中存活。
    • 所以总结一下:您的解决方案只适用于应该在某些时候崩溃的应用程序。
    【解决方案3】:

    我想您想将Fragment 与各种不同的听众重复使用。因此,您的方法并不理想,因为您不能为此使用Bundle。更好的方法是使用回调设计模式,例如

    public class RegWizardFragmentInfo extends Fragment {
    
        public interface RegWizardCallback {
            void onClick();
        }
    }
    

    您的Activity 将实现该接口。因为Fragment 只存在于一个Activity 中,你可以使用生命周期方法onAttach() 从中获取回调实例。它看起来像这样

    public class RegWizardFragmentInfo extends Fragment {
        private RegWizardCallback callback;
    
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            try {
                callback = (RegWizardCallback) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString() + " must implement RegWizardCallback ");
            }
        }
    
        public interface RegWizardCallback {
            void onClick();
        }
    }
    

    这样,您可以在 Button 的侦听器中简单地调用 callback.onClick

    【讨论】:

    • onAttach(Activity activity) 实际上是不推荐使用的方法。
    • @OscarCaballeroPozas >= 23 已弃用。对于较低的 sdk 版本,您仍然需要使用 Activity 差异。
    • @OscarCaballeroPozas,穆拉特是正确的。另一方面,我们也可以使用 public void onAttach(Context context) {} 类似的方法。
    猜你喜欢
    • 2010-12-24
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    • 2019-03-29
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多