android - texto - Cómo manejar los clics de botón usando el XML onClick dentro de fragmentos




onclick android studio (12)

Agregando a la respuesta de Blundell,
Si tienes más fragmentos, con un montón de onClicks:

Actividad:

Fragment someFragment1 = (Fragment)getFragmentManager().findFragmentByTag("someFragment1 "); 
Fragment someFragment2 = (Fragment)getFragmentManager().findFragmentByTag("someFragment2 "); 
Fragment someFragment3 = (Fragment)getFragmentManager().findFragmentByTag("someFragment3 "); 

...onCreate etc instantiating your fragments

public void myClickMethod(View v){
  if (someFragment1.isVisible()) {
       someFragment1.myClickMethod(v);
  }else if(someFragment2.isVisible()){
       someFragment2.myClickMethod(v);
  }else if(someFragment3.isVisible()){
       someFragment3.myClickMethod(v); 
  }

} 

En tu fragmento:

  public void myClickMethod(View v){
     switch(v.getid()){
       // Just like you were doing
     }
  } 

Antes de Honeycomb (Android 3), cada actividad se registró para manejar los clics de los botones a través de la etiqueta onClick en el XML de un diseño:

android:onClick="myClickMethod"

Dentro de ese método, puede usar view.getId() y una instrucción switch para hacer la lógica del botón.

Con la introducción de Honeycomb, estoy dividiendo estas actividades en fragmentos que se pueden reutilizar en muchas actividades diferentes. La mayor parte del comportamiento de los botones es independiente de la actividad, y me gustaría que el código residiera dentro del archivo de Fragmentos sin usar el método antiguo (pre 1.6) de registrar el OnClickListener para cada botón.

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

El problema es que cuando se inflan mis diseños, sigue siendo la Actividad de hospedaje la que recibe los clics del botón, no los Fragmentos individuales. ¿Hay un buen enfoque para cualquiera

  • Registrar el fragmento para recibir los clics del botón?
  • ¿Pasar los eventos de clic de la Actividad al fragmento al que pertenecen?

Como veo respuestas son de alguna manera viejas. Recientemente, Google introdujo DataBinding que es mucho más fácil de manejar onClick o asignando en su xml.

Aquí hay un buen ejemplo que puedes ver cómo manejar esto:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
           android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
   </LinearLayout>
</layout>

También hay un tutorial muy bueno sobre DataBinding que puedes encontrar Here .


En mi caso de uso, tengo 50 Visiones de imagen impares que necesitaba para conectar en un solo método onClick. Mi solución es recorrer las vistas dentro del fragmento y establecer el mismo oyente onclick en cada uno:

    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);
        }
    }

Es posible que desee considerar el uso de EventBus para eventos desacoplados. Puede escuchar eventos con mucha facilidad. También puede asegurarse de que el evento se reciba en el subproceso de la interfaz de usuario (en lugar de llamar a runOnUiThread para usted por cada suscripción de evento)

https://github.com/greenrobot/EventBus

desde Github:

Bus de eventos optimizado para Android que simplifica la comunicación entre Actividades, Fragmentos, Subprocesos, Servicios, etc. Menos código, mejor calidad


Esto ha estado funcionando para mí: (estudio de Android)

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

        View rootView = inflater.inflate(R.layout.update_credential, container, false);
        Button bt_login = (Button) rootView.findViewById(R.id.btnSend);

        bt_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                System.out.println("Hi its me");


            }// end onClick
        });

        return rootView;

    }// end onCreateView

Me gustaría añadir a la answer Adjorn Linkz.

Si necesita varios manejadores, puede usar referencias lambda

void onViewCreated(View view, Bundle savedInstanceState)
{
    view.setOnClickListener(this::handler);
}
void handler(View v)
{
    ...
}

El truco aquí es que la firma del método del handler coincide View.OnClickListener.onClick firma View.OnClickListener.onClick . De esta manera, no necesitará la interfaz View.OnClickListener .

Además, no necesitará ninguna instrucción de cambio.

Lamentablemente, este método solo se limita a las interfaces que requieren un solo método, o un lambda.


Prefiero optar por el manejo de clics en el código que usar el atributo onClick en XML cuando se trabaja con fragmentos.

Esto se vuelve aún más fácil al migrar sus actividades a fragmentos. Solo puede llamar al controlador de clic (previamente establecido en android:onClick en XML) directamente desde cada bloque de case .

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:
              ...
        }
    }
}

Cuando se trata de manejar clics en fragmentos, me parece más simple que android:onClick .


Prefiero usar la siguiente solución para manejar eventos onClick. Esto funciona también para Actividad y Fragmentos.

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;
        }
    }
}

Recientemente resolví este problema sin tener que agregar un método al contexto Actividad o tener que implementar OnClickListener. No estoy seguro si es una solución "válida" tampoco, pero funciona.

Basado en: https://developer.android.com/tools/data-binding/guide.html#binding_events

Se puede hacer con enlaces de datos: simplemente agregue su instancia de fragmento como una variable, luego puede vincular cualquier método con 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 name="fragment" 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>

Y el código de vinculación de fragmentos sería ...

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;
    }

    ...

}

Si se registra en xml con android: Onclick = "", se devolverá la llamada a la Actividad respetada en cuyo contexto pertenece su fragmento (getActivity ()). Si tal método no se encuentra en la Actividad, entonces el sistema lanzará una excepción.


Usted podría hacer esto:

Actividad:

Fragment someFragment;    

//...onCreate etc instantiating your fragments

public void myClickMethod(View v) {
    someFragment.myClickMethod(v);
}

Fragmento:

public void myClickMethod(View v) {
    switch(v.getId()) {
        // Just like you were doing
    }
}    

En respuesta a @Ameen que quería menos acoplamiento para que los Fragmentos sean reutilizables

Interfaz:

public interface XmlClickable {
    void myClickMethod(View v);
}

Actividad:

XmlClickable someFragment;    

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

Fragmento:

public class SomeFragment implements XmlClickable {

//...onCreateView, etc.

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

ButterKnife es probablemente la mejor solución para el problema del desorden. Utiliza procesadores de anotación para generar el código de repetición denominado "método antiguo".

Pero el método onClick todavía se puede utilizar, con un inflador personalizado.

Cómo utilizar

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

Implementación

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);
            }
        }
    }
}






android-fragments