455

onClickHoneycomb 以前 (Android 3)、各アクティビティは、レイアウトの XML のタグを介してボタンのクリックを処理するように登録されていました。

android:onClick="myClickMethod"

そのメソッド内でview.getId()、switch ステートメントを使用してボタン ロジックを実行できます。

Honeycomb の導入により、これらのアクティビティをさまざまなアクティビティ内で再利用できるフラグメントに分割しています。ボタンの動作のほとんどはアクティビティに依存しないため、ボタンごとに登録する古い (1.6 より前の) メソッドを使用せずに、コードを Fragments ファイル内に配置したいと考えています。OnClickListener

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

問題は、私のレイアウトが膨らんでも、個々のフラグメントではなく、ボタンのクリックを受け取っているホスティング アクティビティであることです。どちらかへの良いアプローチはありますか

  • ボタンのクリックを受け取るフラグメントを登録しますか?
  • クリック イベントをアクティビティから、それらが属するフラグメントに渡しますか?
4

19 に答える 19

617

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;
        }
    }
}
于 2013-01-28T20:52:22.420 に答える
178

これを行うことができます:

アクティビティ:

Fragment someFragment;    

//...onCreate etc instantiating your fragments

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

断片:

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

カップリングを減らしてフラグメントを再利用できるようにしたい@Ameenに応えて

インターフェース:

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

アクティビティ:

XmlClickable someFragment;    

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

断片:

public class SomeFragment implements XmlClickable {

//...onCreateView, etc.

@Override
public void myClickMethod(View v) {
    switch(v.getId()){
        // Just like you were doing
    }
}    
于 2011-06-07T21:03:59.230 に答える
31

私が思う問題は、ビューがまだフラグメントではなくアクティビティであることです。フラグメントには独自の独立したビューはなく、親のアクティビティ ビューに関連付けられています。そのため、イベントはフラグメントではなくアクティビティで終了します。残念ですが、これを機能させるにはいくつかのコードが必要になると思います。

変換中に私が行ってきたことは、古いイベント ハンドラーを呼び出すクリック リスナーを追加することだけです。

例えば:

final Button loginButton = (Button) view.findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(final View v) {
        onLoginClicked(v);
    }
});
于 2011-11-01T15:47:28.960 に答える
30

最近、コンテキスト アクティビティにメソッドを追加したり、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>

そして、フラグメントリンクコードは...

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

    ...

}
于 2015-09-11T18:56:16.250 に答える
7

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

実装

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);
            }
        }
    }
}
于 2014-04-19T17:13:17.150 に答える
7

onClickフラグメントを操作するときは、XML で属性を使用するよりも、コードでクリックを処理したいと思います。

アクティビティをフラグメントに移行すると、これはさらに簡単になります。クリック ハンドラー (以前android:onClickは XML で に設定されていました) を各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:
              ...
        }
    }
}

フラグメントでクリックを処理することになると、これは よりも簡単に見えますandroid:onClick

于 2012-09-27T05:22:50.580 に答える
3

You can define a callback as an attribute of your XML layout. The article Custom XML Attributes For Your Custom Android Widgets will show you how to do it for a custom widget. Credit goes to Kevin Dion :)

I'm investigating whether I can add styleable attributes to the base Fragment class.

The basic idea is to have the same functionality that View implements when dealing with the onClick callback.

于 2011-11-26T19:35:26.420 に答える
1

android:Onclick="" を使用して xml に登録すると、コールバックは、フラグメントが属するコンテキスト (getActivity()) の関連するアクティビティに与えられます。そのようなメソッドがアクティビティに見つからない場合、システムは例外をスローします。

于 2015-05-19T08:31:02.883 に答える
1

データバインディングに依存するいくつかの良い答えを見つけましたが、XML でフラグメントフリーのレイアウト定義を可能にしながらフラグメント解決を可能にするという意味で、そのアプローチで完全に機能するものは見当たりませんでした。

したがって、データバインディング有効になっていると仮定すると、私が提案できる一般的な解決策は次のとおりです。少し長いですが、間違いなく機能します(いくつかの注意事項があります):

ステップ 1: カスタム OnClick 実装

これにより、タップされたビュー (ボタンなど) に関連付けられたコンテキストを通じて、フラグメント対応の検索が実行されます。


// CustomOnClick.kt

@file:JvmName("CustomOnClick")

package com.example

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import java.lang.reflect.Method

fun onClick(view: View, methodName: String) {
    resolveOnClickInvocation(view, methodName)?.invoke(view)
}

private data class OnClickInvocation(val obj: Any, val method: Method) {
    fun invoke(view: View) {
        method.invoke(obj, view)
    }
}

private fun resolveOnClickInvocation(view: View, methodName: String): OnClickInvocation? =
    searchContexts(view) { context ->
        var invocation: OnClickInvocation? = null
        if (context is Activity) {
            val activity = context as? FragmentActivity
                    ?: throw IllegalStateException("A non-FragmentActivity is not supported (looking up an onClick handler of $view)")

            invocation = getTopFragment(activity)?.let { fragment ->
                resolveInvocation(fragment, methodName)
            }?: resolveInvocation(context, methodName)
        }
        invocation
    }

private fun getTopFragment(activity: FragmentActivity): Fragment? {
    val fragments = activity.supportFragmentManager.fragments
    return if (fragments.isEmpty()) null else fragments.last()
}

private fun resolveInvocation(target: Any, methodName: String): OnClickInvocation? =
    try {
        val method = target.javaClass.getMethod(methodName, View::class.java)
        OnClickInvocation(target, method)
    } catch (e: NoSuchMethodException) {
        null
    }

private fun <T: Any> searchContexts(view: View, matcher: (context: Context) -> T?): T? {
    var context = view.context
    while (context != null && context is ContextWrapper) {
        val result = matcher(context)
        if (result == null) {
            context = context.baseContext
        } else {
            return result
        }
    }
    return null
}

注: 元の Android 実装に大まかに基づいています ( https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#3025を参照) 。

ステップ 2: レイアウト ファイルでの宣言型アプリケーション

次に、データバインディング対応の XML で:

<layout>
  <data>
     <import type="com.example.CustomOnClick"/>
  </data>

  <Button
    android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
  </Button>
</layout>

注意事項

  • 「最新」FragmentActivityベースの実装を想定
  • スタック内の「最上位」(つまり最後の) フラグメントのルックアップ メソッドのみを使用できます (ただし、必要に応じて修正できます)。
于 2020-02-02T20:58:50.353 に答える
0

あなたのアクティビティは、使用したに違いないコールバックを受け取っています:

mViewPagerCloth.setOnClickListener((YourActivityName)getActivity());

フラグメントにコールバックを受信させたい場合は、次のようにします。

mViewPagerCloth.setOnClickListener(this);

onClickListenerFragmentにインターフェースを実装する

于 2016-04-13T05:25:14.107 に答える
0

最良の解決策私見:

断片的に:

protected void addClick(int id) {
    try {
        getView().findViewById(id).setOnClickListener(this);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void onClick(View v) {
    if (v.getId()==R.id.myButton) {
        onMyButtonClick(v);
    }
}

次に、フラグメントの onViewStateRestored で:

addClick(R.id.myButton);
于 2015-04-19T07:35:57.340 に答える
0

これは私のために働いています:(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
于 2014-11-26T13:12:04.800 に答える