如何使用片段中的XML onClick处理按钮单击

smi*_*324 425 xml android button android-fragments

Pre-Honeycomb(Android 3),每个Activity都已注册,可通过onClickLayout的XML中的标签处理按钮点击:

android:onClick="myClickMethod"
Run Code Online (Sandbox Code Playgroud)

在该方法中,您可以使用view.getId()和switch语句来执行按钮逻辑.

随着Honeycomb的推出,我将这些活动分解为碎片,可以在许多不同的活动中重复使用.按钮的大部分行为都是与Activity无关的,我希望代码驻留在Fragments文件中,而不使用OnClickListener为每个按钮注册的旧(pre 1.6)方法.

final Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        // Perform action on click
    }
});
Run Code Online (Sandbox Code Playgroud)

问题是,当我的布局被夸大时,它仍然是接收按钮点击的托管活动,而不是单个碎片.对两者都有好的方法

  • 注册片段以接收按钮点击?
  • 将Click中的click事件传递给它们所属的片段?

Ado*_*ncz 593

我更喜欢使用以下解决方案来处理onClick事件.这适用于Activity和Fragments.

public class StartFragment extends Fragment implements OnClickListener{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.fragment_start, container, false);

        Button b = (Button) v.findViewById(R.id.StartButton);
        b.setOnClickListener(this);
        return v;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.StartButton:

            ...

            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是不是1987年[编程Windows](http://www.charlespetzold.com/pw5/ProgWinEditions.html)倡导的技术?不用担心.谷歌发展迅速,完全是关于开发人员的生产力 我敢肯定,事件处理与1991-eara Visual Basic一样好不久. (42认同)
  • 投票.这使得片段可以重复使用.否则为什么使用片段? (15认同)
  • 在onCreateView中,我遍历ViewGroup v的所有子项,并为我找到的所有Button实例设置onclicklistener.它比为所有按钮手动设置监听器要好得多. (9认同)
  • 你用过OnClickListener的女巫导入?Intellij建议我android.view.View.OnClickListener它不起作用:/(onClick永远不会运行) (7认同)
  • @NathanOsman我认为问题与xml onClick有关,因此接受的ans提供了确切的解决方案. (4认同)
  • @NathanOsman授予这个是我推荐和使用的解决方案,但问题是不要注册点击监听器 (2认同)
  • 我使用API​​ 21,这个解决方案不起作用......有什么建议吗?谢谢 (2认同)
  • 实现OnClickListener ....我们必须导入哪一个才能使这个实现工作? (2认同)

Blu*_*ell 170

你可以这样做:

活动:

Fragment someFragment;    

//...onCreate etc instantiating your fragments

public void myClickMethod(View v) {
    someFragment.myClickMethod(v);
}
Run Code Online (Sandbox Code Playgroud)

分段:

public void myClickMethod(View v) {
    switch(v.getId()) {
        // Just like you were doing
    }
}    
Run Code Online (Sandbox Code Playgroud)

回应@Ameen谁想要更少的耦合,所以片段是可重复使用的

接口:

public interface XmlClickable {
    void myClickMethod(View v);
}
Run Code Online (Sandbox Code Playgroud)

活动:

XmlClickable someFragment;    

//...onCreate, etc. instantiating your fragments casting to your interface.
Run Code Online (Sandbox Code Playgroud)
public void myClickMethod(View v) {
    someFragment.myClickMethod(v);
}
Run Code Online (Sandbox Code Playgroud)

分段:

public class SomeFragment implements XmlClickable {

//...onCreateView, etc.

@Override
public void myClickMethod(View v) {
    switch(v.getId()){
        // Just like you were doing
    }
}    
Run Code Online (Sandbox Code Playgroud)

  • 我遇到了同样的问题,即使我感谢你的回答,从软件工程的角度来看,这不是干净的代码.此代码导致活动与片段紧密耦合.您应该能够在多个活动中重复使用相同的片段,而无需了解片段的实现细节. (125认同)
  • 这就是我现在正在做的事情,但是当你有多个片段需要接收点击事件时,它会变得更加混乱.一般来说,我只是加剧了碎片,因为范式已经解散了. (51认同)
  • 您可以使用Euporie提到的现有OnClickListener,而不是定义自己的接口. (4认同)

Bri*_*pin 28

我认为问题是视图仍然是活动,而不是片段.片段没有自己的任何独立视图,并附加到父活动视图.这就是为什么事件最终在Activity中,而不是片段.不幸的是,但我认为你需要一些代码来完成这项工作.

我在转换过程中一直在做的只是添加一个调用旧事件处理程序的单击侦听器.

例如:

final Button loginButton = (Button) view.findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(final View v) {
        onLoginClicked(v);
    }
});
Run Code Online (Sandbox Code Playgroud)


Ald*_*epa 23

我最近解决了这个问题,无需向上下文Activity添加方法或必须实现OnClickListener.我不确定它是否是一个"有效"的解决方案,但它确实有效.

基于:https://developer.android.com/tools/data-binding/guide.html#binding_events

它可以通过数据绑定来完成:只需将您的片段实例添加为变量,然后您可以使用onClick链接任何方法.

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.testapp.fragments.CustomFragment">

    <data>
        <variable android:name="fragment" android:type="com.example.testapp.fragments.CustomFragment"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_place_black_24dp"
            android:onClick="@{() -> fragment.buttonClicked()}"/>
    </LinearLayout>
</layout>
Run Code Online (Sandbox Code Playgroud)

片段链接代码将是......

public class CustomFragment extends Fragment {

    ...

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_person_profile, container, false);
        FragmentCustomBinding binding = DataBindingUtil.bind(view);
        binding.setFragment(this);
        return view;
    }

    ...

}
Run Code Online (Sandbox Code Playgroud)


Ron*_*nie 6

在使用onClick片段时,我宁愿在代码中使用单击处理而不是使用XML中的属性.

将活动迁移到片段时,这变得更加容易.您可以android:onClick直接从每个case块调用单击处理程序(以前设置为XML).

findViewById(R.id.button_login).setOnClickListener(clickListener);
...

OnClickListener clickListener = new OnClickListener() {
    @Override
    public void onClick(final View v) {
        switch(v.getId()) {
           case R.id.button_login:
              // Which is supposed to be called automatically in your
              // activity, which has now changed to a fragment.
              onLoginClick(v);
              break;

           case R.id.button_logout:
              ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在处理碎片中的点击时,这看起来比我简单android:onClick.


ser*_*1pt 6

ButterKnife可能是解决杂乱问题的最佳解决方案.它使用注释处理器生成所谓的"旧方法"样板代码.

但是仍然可以使用onClick方法,使用自定义充气程序.

如何使用

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup cnt, Bundle state) {
    inflater = FragmentInflatorFactory.inflatorFor(inflater, this);
    return inflater.inflate(R.layout.fragment_main, cnt, false);
}
Run Code Online (Sandbox Code Playgroud)

履行

public class FragmentInflatorFactory implements LayoutInflater.Factory {

    private static final int[] sWantedAttrs = { android.R.attr.onClick };

    private static final Method sOnCreateViewMethod;
    static {
        // We could duplicate its functionallity.. or just ignore its a protected method.
        try {
            Method method = LayoutInflater.class.getDeclaredMethod(
                    "onCreateView", String.class, AttributeSet.class);
            method.setAccessible(true);
            sOnCreateViewMethod = method;
        } catch (NoSuchMethodException e) {
            // Public API: Should not happen.
            throw new RuntimeException(e);
        }
    }

    private final LayoutInflater mInflator;
    private final Object mFragment;

    public FragmentInflatorFactory(LayoutInflater delegate, Object fragment) {
        if (delegate == null || fragment == null) {
            throw new NullPointerException();
        }
        mInflator = delegate;
        mFragment = fragment;
    }

    public static LayoutInflater inflatorFor(LayoutInflater original, Object fragment) {
        LayoutInflater inflator = original.cloneInContext(original.getContext());
        FragmentInflatorFactory factory = new FragmentInflatorFactory(inflator, fragment);
        inflator.setFactory(factory);
        return inflator;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        if ("fragment".equals(name)) {
            // Let the Activity ("private factory") handle it
            return null;
        }

        View view = null;

        if (name.indexOf('.') == -1) {
            try {
                view = (View) sOnCreateViewMethod.invoke(mInflator, name, attrs);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            } catch (InvocationTargetException e) {
                if (e.getCause() instanceof ClassNotFoundException) {
                    return null;
                }
                throw new RuntimeException(e);
            }
        } else {
            try {
                view = mInflator.createView(name, null, attrs);
            } catch (ClassNotFoundException e) {
                return null;
            }
        }

        TypedArray a = context.obtainStyledAttributes(attrs, sWantedAttrs);
        String methodName = a.getString(0);
        a.recycle();

        if (methodName != null) {
            view.setOnClickListener(new FragmentClickListener(mFragment, methodName));
        }
        return view;
    }

    private static class FragmentClickListener implements OnClickListener {

        private final Object mFragment;
        private final String mMethodName;
        private Method mMethod;

        public FragmentClickListener(Object fragment, String methodName) {
            mFragment = fragment;
            mMethodName = methodName;
        }

        @Override
        public void onClick(View v) {
            if (mMethod == null) {
                Class<?> clazz = mFragment.getClass();
                try {
                    mMethod = clazz.getMethod(mMethodName, View.class);
                } catch (NoSuchMethodException e) {
                    throw new IllegalStateException(
                            "Cannot find public method " + mMethodName + "(View) on "
                                    + clazz + " for onClick");
                }
            }

            try {
                mMethod.invoke(mFragment, v);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Eup*_*rie 5

这是另一种方式:

1.像这样创建一个BaseFragment:

public abstract class BaseFragment extends Fragment implements OnClickListener
Run Code Online (Sandbox Code Playgroud)

2.使用

public class FragmentA extends BaseFragment 
Run Code Online (Sandbox Code Playgroud)

代替

public class FragmentA extends Fragment
Run Code Online (Sandbox Code Playgroud)

3.在你的活动中:

public class MainActivity extends ActionBarActivity implements OnClickListener
Run Code Online (Sandbox Code Playgroud)

BaseFragment fragment = new FragmentA;

public void onClick(View v){
    fragment.onClick(v);
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.


Chr*_*ght 5

在我的用例中,我需要将 50 个奇数的 ImageView 挂接到单个 onClick 方法中。我的解决方案是遍历片段内的视图并在每个视图上设置相同的 onclick 侦听器:

    final View.OnClickListener imageOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            chosenImage = ((ImageButton)v).getDrawable();
        }
    };

    ViewGroup root = (ViewGroup) getView().findViewById(R.id.imagesParentView);
    int childViewCount = root.getChildCount();
    for (int i=0; i < childViewCount; i++){
        View image = root.getChildAt(i);
        if (image instanceof ImageButton) {
            ((ImageButton)image).setOnClickListener(imageOnClickListener);
        }
    }
Run Code Online (Sandbox Code Playgroud)