วิธีจัดการกับการคลิกปุ่มโดยใช้ XML onClick ภายใน Fragments


440

Pre-Honeycomb (Android 3) แต่ละกิจกรรมได้รับการลงทะเบียนเพื่อจัดการการคลิกปุ่มผ่านonClickแท็กใน XML ของเค้าโครง:

android:onClick="myClickMethod"

ภายในเมธอดนั้นคุณสามารถใช้view.getId()และคำสั่ง switch เพื่อทำตรรกะปุ่ม

ด้วยการแนะนำ Honeycomb ฉันได้แบ่งกิจกรรมเหล่านี้เป็นชิ้นส่วนซึ่งสามารถนำกลับมาใช้ใหม่ได้ในกิจกรรมต่างๆ พฤติกรรมส่วนใหญ่ของปุ่มนั้นเป็นอิสระจากกิจกรรมและฉันต้องการให้โค้ดอยู่ในไฟล์ Fragments โดยไม่ต้องใช้วิธีเก่า (ก่อน 1.6) ในการลงทะเบียนOnClickListenerสำหรับแต่ละปุ่ม

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

ปัญหาคือเมื่อรูปแบบของฉันพองตัวมันยังคงเป็นพื้นที่กิจกรรมที่ได้รับการคลิกปุ่มไม่ใช่ชิ้นส่วนแต่ละชิ้น มีวิธีการที่ดีทั้ง

  • ลงทะเบียนชิ้นส่วนเพื่อรับการคลิกปุ่ม?
  • ส่งเหตุการณ์คลิกจากกิจกรรมไปยังส่วนที่เป็นของพวกเขาหรือไม่

1
คุณจัดการกับผู้ฟังที่ลงทะเบียนไม่ได้ใน onCreate fragment หรือไม่?
CL22

24
@ โหนดใช่ แต่ฉันไม่ต้องการที่จะใช้setOnClickListenerและfindViewByIdสำหรับแต่ละปุ่มนั่นคือเหตุผลที่onClickถูกเพิ่มเข้ามาเพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้น
smith324

4
ดูคำตอบที่ได้รับการยอมรับฉันคิดว่าการใช้ setOnClickListener มีการเชื่อมโยงกันอย่างหลวม ๆ มากกว่าการผสานกับวิธีการแบบ XML onClick หากกิจกรรมต้อง 'ส่งต่อ' การคลิกแต่ละครั้งไปยังส่วนที่ถูกต้องหมายความว่ารหัสจะต้องเปลี่ยนในแต่ละครั้งที่มีการเพิ่มส่วน การใช้อินเทอร์เฟซเพื่อแยกจากคลาสพื้นฐานของแฟรกเมนต์ไม่สามารถช่วยได้ หากแฟรกเมนต์รีจิสเตอร์ด้วยปุ่มที่ถูกต้องตัวเองกิจกรรมยังคงไม่เชื่อเรื่องพระเจ้าอย่างสมบูรณ์ซึ่งเป็น IMO สไตล์ที่ดีกว่า ดูคำตอบจาก Adorjan Princz
Adriaan Koster

@ smith324 ต้องเห็นด้วยกับ Adriaan ในอันนี้ มีคำตอบของ Adorjan และดูว่าชีวิตไม่ดีขึ้นหลังจากนั้น
user1567453

คำตอบ:


175

คุณสามารถทำสิ่งนี้ได้:

กิจกรรม:

Fragment someFragment;    

//...onCreate etc instantiating your fragments

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

Fragment:

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

Fragment:

public class SomeFragment implements XmlClickable {

//...onCreateView, etc.

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

52
นั่นคือสิ่งที่ฉันกำลังทำอยู่ตอนนี้ แต่มันเป็นเรื่องยุ่งมากเมื่อคุณมีหลายส่วนที่แต่ละคนต้องได้รับการคลิก ฉันแค่กำเริบชิ้นส่วนโดยทั่วไปเพราะกระบวนทัศน์ได้ละลายไปรอบ ๆ พวกเขา
smith324

128
ฉันพบปัญหาเดียวกันและแม้ว่าฉันจะชื่นชมการตอบสนองของคุณนี่ไม่ใช่โค้ดที่ชัดเจนจากมุมมองด้านวิศวกรรมซอฟต์แวร์ รหัสนี้ส่งผลในกิจกรรมที่ถูกควบคู่ไปกับชิ้นส่วนอย่างแน่นหนา คุณควรจะสามารถนำแฟรกเมนต์เดียวกันกลับมาใช้ใหม่ในหลาย ๆ กิจกรรมได้โดยที่กิจกรรมไม่ทราบรายละเอียดการนำไปใช้ของแฟรกเมนต์
Ameen

1
ควรเป็น "switch (v.getId ()) {" และไม่ใช่ "switch (v.getid ()) {"
eb80

5
แทนการกำหนดอินเทอร์เฟซของคุณเองคุณสามารถใช้ OnClickListener ที่มีอยู่แล้วตามที่ Euporie กล่าวถึง
fr00tyl00p

1
ฉันร้องไห้เมื่อฉันอ่านสิ่งนี้มันเป็นสิ่งที่ทำให้เดือดร้อนมาก ... คำตอบด้านล่างจาก @AdorjanPrincz เป็นวิธีที่จะไป
Wjdavis5

603

ฉันชอบใช้วิธีแก้ไขปัญหาต่อไปนี้เพื่อจัดการกับเหตุการณ์ onClick ใช้งานได้กับกิจกรรมและแฟรกเมนต์เช่นกัน

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

9
ใน onCreateView ฉันวนซ้ำไอเท็มลูกทั้งหมดของ ViewGroup v และตั้งค่า onclicklistener สำหรับอินสแตนซ์ปุ่มทั้งหมดที่ฉันค้นหา มันดีกว่าการตั้งค่าฟังสำหรับปุ่มทั้งหมดด้วยตนเอง
บุคคล

17
โหวต ทำให้ชิ้นส่วนนั้นสามารถนำกลับมาใช้ใหม่ได้ มิฉะนั้นทำไมต้องใช้ชิ้นส่วน?
ทัณฑ์

44
นี่ไม่ใช่เทคนิคแบบเดียวกันที่สนับสนุนโดยการเขียนโปรแกรม Windowsในปี 1987 ใช่ไหม ไม่ต้องกังวลไป Google ก้าวไปอย่างรวดเร็วและเป็นเรื่องเกี่ยวกับประสิทธิภาพของนักพัฒนา ฉันแน่ใจว่ามันจะไม่นานจนกว่าการจัดการเหตุการณ์จะดีเท่า 1991-eara Visual Basic
Edward Brey

7
คุณเคยนำเข้าแม่มดสำหรับ OnClickListener แล้วหรือยัง? Intellij แนะนำให้ฉัน android.view.ViewOnClickListener และมันไม่ทำงาน: / (onClick ไม่ทำงาน)
ลูคัส Jota

4
@NathanOsman ฉันคิดว่าคำถามเกี่ยวกับ xml onClick ดังนั้น ans ที่ยอมรับจึงให้คำตอบที่ถูกต้อง
อาเหม็ด Naveed

29

ปัญหาที่ฉันคิดคือมุมมองยังคงเป็นกิจกรรมไม่ใช่ชิ้นส่วน แฟรกเมนต์ไม่มีมุมมองอิสระของตนเองและถูกแนบกับมุมมองกิจกรรมพาเรนต์ นั่นเป็นสาเหตุที่เหตุการณ์จบลงในกิจกรรมไม่ใช่ส่วนย่อย มันโชคร้าย แต่ฉันคิดว่าคุณจะต้องมีรหัสเพื่อให้งานนี้

สิ่งที่ฉันทำระหว่างการแปลงคือการเพิ่มฟังการคลิกที่เรียกตัวจัดการเหตุการณ์เก่า

เช่น:

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

1
ขอบคุณ - ฉันใช้สิ่งนี้กับการปรับเปลี่ยนเล็กน้อยหนึ่งครั้งที่ฉันผ่านมุมมองชิ้นส่วน (เช่นผลลัพธ์ของ inflater.inflate (R.layout.my_fragment_xml_resource)) ไปยัง onLoginClicked () เพื่อให้สามารถเข้าถึงมุมมองย่อยของแฟรกเมนต์ได้ เช่น EditText, ผ่าน view.findViewById () (ถ้าฉันเพียงแค่ผ่านมุมมองกิจกรรมการโทรไปที่ view.findViewById (R.id.myfragmentwidget_id) ส่งคืนค่า null)
Michael Nelson

สิ่งนี้ใช้ไม่ได้กับ API 21 ในโครงการของฉัน มีความคิดเกี่ยวกับวิธีการใช้วิธีนี้หรือไม่?
Darth Coder

รหัสพื้นฐานที่ค่อนข้างใช้กับแอพทุกตัว คุณช่วยอธิบายสิ่งที่เกิดขึ้นกับคุณได้ไหม
Brill Pappin

1
ทำให้คำตอบของฉัน upvote นี้สำหรับคำอธิบายของปัญหาที่เกิดขึ้นเพราะเค้าโครงของชิ้นส่วนที่แนบมากับมุมมองกิจกรรม
fllo

25

ฉันเพิ่งแก้ไขปัญหานี้โดยไม่ต้องเพิ่มวิธีในบริบทของกิจกรรมหรือต้องใช้ 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;
    }

    ...

}

1
ฉันเพิ่งมีความรู้สึกรายละเอียดบางอย่างขาดหายไปเนื่องจากฉันไม่สามารถใช้งานโซลูชันนี้ได้
Tima

บางทีคุณอาจจะพลาดบางสิ่งบางอย่างจาก "Build Environment" ในเอกสารประกอบ: developer.android.com/tools/data-binding/
......

@Aldo สำหรับวิธี onClick ใน XML ฉันเชื่อว่าคุณควรมี android: onClick = "@ {() -> แฟรกเมนต์ buttonClicked ()}" แทน นอกจากนี้สำหรับคนอื่น ๆ คุณควรประกาศฟังก์ชั่น buttonClicked () ภายในส่วนและวางตรรกะของคุณไว้ข้างใน
Hayk Nahapetyan

ใน xml ควรเป็นandroid:name="fragment" android:type="com.example.testapp.fragments.CustomFragment"/>
COYG

1
ฉันแค่พยายามนี้และnameและtypeคุณลักษณะของvariableแท็กควรไม่ได้มีandroid:คำนำหน้า อาจมีเลย์เอาต์ Android เวอร์ชันเก่าที่ต้องการหรือไม่
JohnnyLambada

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

การดำเนินงาน

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

6

ฉันอยากจะไปที่การจัดการการคลิกในรหัสมากกว่าการใช้onClickคุณลักษณะใน XML เมื่อทำงานกับชิ้นส่วน

สิ่งนี้จะยิ่งง่ายขึ้นเมื่อทำการย้ายกิจกรรมของคุณไปเป็นชิ้นส่วน คุณสามารถเรียกตัวจัดการคลิก (ก่อนหน้านี้ตั้งค่าเป็นandroid:onClickXML) ได้โดยตรงจากแต่ละ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เมื่อมันมาถึงการจัดการการคลิกในชิ้นส่วนนี้มีลักษณะที่เรียบง่ายกับผมมากกว่า


5

นี่เป็นอีกวิธีหนึ่ง:

1. สร้าง BaseFragment เช่นนี้

public abstract class BaseFragment extends Fragment implements OnClickListener

2.use

public class FragmentA extends BaseFragment 

แทน

public class FragmentA extends Fragment

3. ในกิจกรรมของคุณ:

public class MainActivity extends ActionBarActivity implements OnClickListener

และ

BaseFragment fragment = new FragmentA;

public void onClick(View v){
    fragment.onClick(v);
}

หวังว่ามันจะช่วย


1 ปี, 1 เดือนและ 1 วันหลังจากคำตอบของคุณ: มีเหตุผลอื่นนอกจากการใช้งาน OnClickListener ซ้ำ ๆ กันในทุก ๆ ส่วนของ Fragment เพื่อสร้าง BaseFragment ที่เป็นนามธรรมหรือไม่?
Dimitrios K.

5

ในกรณีการใช้งานของฉันฉันมีภาพตัวอย่างแปลก ๆ 50 ภาพที่ฉันต้องการเพื่อใช้เป็นวิธีการ 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);
        }
    }

2

อย่างที่ฉันเห็นคำตอบพวกมันเก่ามาก เมื่อเร็ว ๆ นี้GoogleแนะนำDataBindingซึ่งง่ายต่อการจัดการonClick มากหรือกำหนดใน XML ของคุณ

นี่คือตัวอย่างที่ดีที่คุณสามารถดูวิธีจัดการกับสิ่งนี้:

<?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>

นอกจากนี้ยังมีการกวดวิชาที่ดีมากเกี่ยวกับ DataBinding คุณสามารถค้นหาได้ที่นี่


2

คุณสามารถกำหนดการโทรกลับเป็นแอตทริบิวต์ของเค้าโครง XML ของคุณ บทความคุณสมบัติ XML ที่กำหนดเองสำหรับวิดเจ็ต Android ที่กำหนดเองของคุณจะแสดงวิธีการทำสำหรับวิดเจ็ตที่กำหนดเอง เครดิตไปที่ Kevin Dion :)

ฉันกำลังตรวจสอบว่าฉันสามารถเพิ่มคุณสมบัติที่เป็นสไตล์ให้กับคลาส Fragment ฐานได้หรือไม่

แนวคิดพื้นฐานคือการมีฟังก์ชั่นเดียวกับที่ใช้ในการจัดการกับการเรียกกลับ onClick


1

เพิ่มไปยังคำตอบของ Blundell
หากคุณมีแฟรกเมนต์มากขึ้นด้วย onClicks มากมาย:

กิจกรรม:

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

} 

ในส่วนของคุณ:

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

1

หากคุณลงทะเบียนใน xml โดยใช้ Android: Onclick = "" การโทรกลับจะถูกส่งไปยังกิจกรรมที่ได้รับการเคารพภายใต้บริบทที่แฟรกเมนต์ของคุณอยู่ (getActivity ()) หากไม่พบวิธีดังกล่าวในกิจกรรมระบบจะส่งข้อยกเว้น


ขอบคุณไม่มีใครอธิบายว่าทำไมความผิดพลาดจึงเกิดขึ้น

1

คุณอาจต้องการพิจารณาใช้ EventBus เพื่อแยกกิจกรรม .. คุณสามารถฟังเหตุการณ์ได้ง่ายมาก คุณสามารถตรวจสอบให้แน่ใจว่าได้รับเหตุการณ์ในเธรด ui (แทนที่จะเรียกใช้ runOnUiThread .. สำหรับตัวคุณเองสำหรับการสมัครรับข้อมูลทุกกิจกรรม)

https://github.com/greenrobot/EventBus

จาก Github:

Event bus ที่ได้รับการปรับแต่ง Android ให้การสื่อสารระหว่างกิจกรรม, Fragments, Threads, Services และอื่น ๆ ลดความซับซ้อนของรหัส, คุณภาพที่ดีขึ้น


1

ฉันต้องการที่จะเพิ่ม Adjorn Linkz ของคำตอบ

หากคุณต้องการเครื่องมือจัดการหลายตัวคุณสามารถใช้การอ้างอิงแลมบ์ดาได้

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

เคล็ดลับที่นี่คือhandlerลายเซ็นของวิธีการนั้นตรงกับView.OnClickListener.onClickลายเซ็น ด้วยวิธีนี้คุณไม่จำเป็นต้องมีView.OnClickListenerส่วนต่อประสาน

นอกจากนี้คุณไม่จำเป็นต้องมีคำสั่งสลับใด ๆ

น่าเศร้าที่วิธีนี้ จำกัด เฉพาะอินเตอร์เฟสที่ต้องใช้วิธีการเดียวหรือแลมบ์ดา


1

แม้ว่าฉันจะเห็นคำตอบที่ดีโดยอาศัยการเชื่อมโยงข้อมูล แต่ฉันก็ไม่เห็นว่าจะใช้วิธีการนั้นได้อย่างเต็มที่ - ในแง่ของการเปิดใช้งานการแยกส่วนของความละเอียด

ดังนั้นข้อมูลที่สมมติว่าผลผูกพันจะเปิดการใช้งานที่นี่เป็นวิธีการแก้ปัญหาทั่วไปที่ฉันสามารถเสนอ; ค่อนข้างยาว แต่ใช้งานได้จริง (กับคำเตือน):

ขั้นตอนที่ 1: การใช้งาน OnClick แบบกำหนดเอง

สิ่งนี้จะทำการค้นหาส่วนที่รับรู้ผ่านบริบทที่เกี่ยวข้องกับมุมมอง tapped-on (เช่นปุ่ม):


// 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การดำเนินงานตาม
  • สามารถค้นหาด้วยวิธีการ "ส่วนบนสุด" (เช่นล่าสุด ) ส่วนในสแต็ก (แม้ว่าจะสามารถแก้ไขได้ถ้าจำเป็น)

0

สิ่งนี้ได้ผลสำหรับฉัน: (Android studio)

 @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

1
นี้ทำซ้ำคำตอบ @Brill Pappin ของ
naXa

0

ทางออกที่ดีที่สุด IMHO:

ในส่วน:

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

0

กิจกรรมของคุณได้รับการติดต่อกลับตามที่ต้องใช้:

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

หากคุณต้องการให้แฟรกเมนต์ได้รับการติดต่อกลับให้ทำดังนี้

mViewPagerCloth.setOnClickListener(this);

และใช้onClickListenerอินเทอร์เฟซบนชิ้นส่วน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.