วิธีเรียกกิจกรรมจากอะแดปเตอร์


119

เป็นไปได้หรือไม่ที่จะเรียก method ที่กำหนดActivityจากListAdapter?

(ฉันต้องการที่จะทำให้Buttonในlist'sแถวและเมื่อกดปุ่มนี้มีการคลิกก็ควรดำเนินการวิธีการที่กำหนดไว้ในกิจกรรมที่สอดคล้องกัน. ผมพยายามที่จะตั้งค่าonClickListenerในของฉันListAdapterแต่ฉันไม่ทราบวิธีการเรียกวิธีการนี้สิ่งที่เป็นเส้นทางของมัน .. )

เมื่อฉันใช้Activity.this.method()ฉันได้รับข้อผิดพลาดต่อไปนี้:

No enclosing instance of the type Activity is accessible in scope

ความคิดใด ๆ ?


คุณไม่สามารถเรียกกิจกรรมนี้ในชั้นเรียนอื่น ๆ ได้เว้นแต่จะเป็นชั้นในของกิจกรรมนั้น ติดตาม @Eldhose M Babu solution สำหรับเคสของคุณ
Archie.bpgc

คำตอบ:


306

ใช่คุณสามารถ.

ในอะแด็ปเตอร์เพิ่มฟิลด์ใหม่:

private Context mContext;

ในตัวสร้างอะแด็ปเตอร์เพิ่มรหัสต่อไปนี้:

public AdapterName(......, Context context) {
  //your code.
  this.mContext = context;
}

ใน getView (... ) ของ Adapter:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
    if (mContext instanceof YourActivityName) {
      ((YourActivityName)mContext).yourDesiredMethod();
    }
  }
});

แทนที่ด้วยชื่อชั้นเรียนของคุณเองที่คุณเห็นรหัสกิจกรรมของคุณ ฯลฯ

หากคุณจำเป็นต้องใช้อะแดปเตอร์เดียวกันนี้มากกว่าหนึ่งกิจกรรมให้ทำดังนี้

สร้างอินเทอร์เฟซ

public interface IMethodCaller {
    void yourDesiredMethod();
}

ใช้อินเทอร์เฟซนี้ในกิจกรรมที่คุณต้องการเพื่อให้มีฟังก์ชันการเรียกใช้เมธอดนี้

จากนั้นใน Adapter getView () ให้เรียกเช่น:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mContext instanceof IMethodCaller) {
            ((IMethodCaller) mContext).yourDesiredMethod();
        }
    }
});

คุณทำเสร็จแล้ว หากคุณจำเป็นต้องใช้อะแด็ปเตอร์นี้สำหรับกิจกรรมที่ไม่ต้องการกลไกการโทรนี้โค้ดจะไม่ทำงาน (หากการตรวจสอบล้มเหลว)


29
เนื่องจากนี่เป็นคำถามยอดนิยมฉันจึงไม่แนะนำอย่างยิ่งให้ใช้วิธีนี้ คุณควรหลีกเลี่ยงการแคสต์คลาสที่นี่เพราะอาจทำให้เกิดข้อยกเว้นรันไทม์
Igor Filippov

3
พี่ชายฉันไม่มีอะไรต่อต้านคุณ แต่คำตอบด้านล่างคือแนวทางปฏิบัติที่ดีที่สุด คุณสามารถอ่านในความคิดเห็น คุณไม่ควรแคสต์ mContext ไปยังกิจกรรมของคุณเนื่องจากคุณกำลังหลีกเลี่ยงการใช้โค้ดซ้ำ ตอนนี้อะแด็ปเตอร์นี้สามารถใช้ได้เฉพาะในกิจกรรมที่คุณได้แคสต์ตัวแปร mContext ของคุณไว้เท่านั้นโดยที่หากคุณกำหนด Listener ไว้คุณจะสามารถใช้ Adapter เดิมซ้ำในกิจกรรมอื่นได้ เชื่อฉันว่าฉันใช้เวลาในอุตสาหกรรมมามากพอแล้วและฉันแค่พยายามช่วยคุณที่นี่โปรดอย่าใช้มันเป็นการส่วนตัว
Varundroid

2
โซลูชันนี้เป็นหนึ่งในวิธีที่ดีที่สุดที่ฉันเคยพบใน SO ขอบคุณมากนายบาบู การเรียกใช้วิธีการของกิจกรรมด้วยสิ่งนี้ทำให้ทั้งแอปเพิ่มประสิทธิภาพการเตะขึ้น 500% -> ฉันไม่จำเป็นต้องโหลดฐานข้อมูลซ้ำในอะแดปเตอร์ซึ่งฉันต้องการเข้าถึง ฉันไม่สนใจ "ปีในอุตสาหกรรม" ฉันกำลังมองหาโซลูชันที่มั่นคงและใช้งานได้และฉันค่อนข้างแน่ใจว่าฉันจะไม่พบวิธีที่ดีกว่าในการเข้าถึง บางทีฉันอาจตั้งค่าฐานข้อมูลเป็นซิงเกิลตันได้ แต่นี่ไม่ใช่วิธีที่ฉันต้องการ "ยกเลิกโครงสร้าง" โครงการของฉัน
Martin Pfeffer

2
@RAM หากคุณต้องการเรียกใช้เมธอดเดียวกันในมากกว่าหนึ่งกิจกรรมคุณต้องสร้างอินเทอร์เฟซด้วยชื่อเมธอดนั้น จากนั้นใช้อินเทอร์เฟซนั้นในกิจกรรมทั้งหมดที่คุณต้องการให้เรียกใช้เมธอด จากนั้นใน click listener ให้ตรวจสอบอินสแตนซ์ของอินเทอร์เฟซของคุณแทนที่จะใช้ชื่อกิจกรรม
Eldhose M Babu

1
คำตอบที่ยอดเยี่ยม Thumbs up
Alok Rajasukumaran

126

คุณสามารถทำได้ด้วยวิธีนี้:

ประกาศอินเทอร์เฟซ:

public interface MyInterface{
    public void foo();
}

ให้กิจกรรมของคุณเสริม:

public class MyActivity extends Activity implements MyInterface{
    public void foo(){
        //do stuff
    }
}

จากนั้นส่งต่อกิจกรรมของคุณไปยัง ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

    public MyAdapter(MyInterface listener){
        this.listener = listener;
    }
}

และบางแห่งในอะแดปเตอร์เมื่อคุณต้องการเรียกใช้เมธอดกิจกรรมนั้น:

listener.foo();

5
ก่อนอื่นฉันนึกถึงวิธีการข้างต้นเหมือนกับที่คุณทำกับเศษเล็กเศษน้อย แต่นี่เป็นทางออกที่ดีที่สุด!
brux

1
InterfaceListener ของฉันคืออะไร จะเริ่มต้นได้อย่างไร? MyAdapter สาธารณะ (ผู้ฟัง MyInterface) {this.listener = listener; }
Gilberto Ibarra

6
คุณส่งอินเทอร์เฟซไปยังอะแดปเตอร์ได้อย่างไร (จากกิจกรรม)
Brandon

1
@ แบรนดอนคุณสามารถส่งผ่านมันเป็นพารามิเตอร์ตัวสร้างในอะแดปเตอร์
Igor Filippov

5
@Brandon เพียงแค่เพิ่ม 'this' เพื่อส่งผ่านอินเตอร์เฟสไปยังอะแดปเตอร์ myAdapter = MyAdapter ใหม่ (นี้);
ajinkya

67

เดิม:

ฉันเข้าใจคำตอบปัจจุบัน แต่ต้องการตัวอย่างที่ชัดเจนกว่านี้ นี่คือตัวอย่างของสิ่งที่ฉันใช้กับAdapter(RecyclerView.Adapter) และActivityไฟล์.

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

สิ่งนี้จะนำสิ่งinterfaceที่เรามีในAdapterไฟล์. ในตัวอย่างนี้จะเรียกเมื่อผู้ใช้คลิกที่รายการในไฟล์RecyclerView.

public class MyActivity extends Activity implements AdapterCallback {

    private MyAdapter myAdapter;

    @Override
    public void onMethodCallback() {
       // do something
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        myAdapter = new MyAdapter(this);
    }
}

ในอะแดปเตอร์ของคุณ:

ในการActivityเริ่มต้นของเราAdapterและส่งผ่านสิ่งนี้เป็นข้อโต้แย้งให้กับผู้สร้าง สิ่งนี้จะเริ่มต้นinterfaceสำหรับวิธีการโทรกลับของเรา คุณจะเห็นได้ว่าเราใช้วิธีโทรกลับสำหรับการคลิกของผู้ใช้

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private AdapterCallback adapterCallback;

    public MyAdapter(Context context) {
        try {
            adapterCallback = ((AdapterCallback) context);
        } catch (ClassCastException e) {
            throw new ClassCastException("Activity must implement AdapterCallback.", e);
        }
    }

    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int position) {
        // simple example, call interface here
        // not complete
        viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    adapterCallback.onMethodCallback();
                } catch (ClassCastException e) {
                   // do something
                }
            }
        });
    }

    public static interface AdapterCallback {
        void onMethodCallback();
    }
}

6
ขอบคุณสำหรับสิ่งนี้มันคือสิ่งที่ฉันกำลังมองหา โหวตขึ้น
Nactus

สิ่งนี้ได้ผลสำหรับฉัน! imo รหัสการทำงานที่สมบูรณ์ที่สุดจนถึงตอนนี้!
publicknowledge

2
ขอบคุณสำหรับวิธีแก้ปัญหานี้
Steve

4
คำตอบนี้ควรได้รับการโหวตมากกว่านี้เป็นทางออกที่สะอาดและสมบูรณ์แบบ
Opiatefuchs

สวัสดี @Jared - คุณสามารถใช้ตัวอย่างเดียวกันกับEventBusที่คุณแนะนำได้หรือไม่?
Tohid

15

พื้นฐานและเรียบง่าย

ในอะแดปเตอร์ของคุณเพียงแค่ใช้สิ่งนี้

((YourParentClass) context).functionToRun();


4
ไม่ใช่แนวทางปฏิบัติที่ดีเพราะคุณกำลัง จำกัด ชั้นเรียนของคุณให้ทำงานเฉพาะกับประเภท YourParentClass แทนที่จะเป็นคลาสใด ๆ แต่จะได้ผลถ้าคุณไม่สนใจที่จะนำกลับมาใช้ใหม่หากคุณเปลี่ยนชื่อคลาสด้วยรหัสจะแบ่ง ...
Gustavo Baiocchi Costa

7

อีกวิธีหนึ่งคือ ::

เขียนวิธีการในอะแดปเตอร์ของคุณเพื่อบอกว่า public void callBack () {}

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

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};

5

สำหรับ Kotlin:

ในอะแดปเตอร์ของคุณเพียงโทร

(context as Your_Activity_Name).yourMethod()

0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

เงื่อนไขนี้จะช่วยให้คุณสามารถดำเนินการบางอย่างได้หากกิจกรรมที่มีการGroupViewร้องขอมุมมองจากgetView()วิธีการของคุณadapterคือyourActivity

หมายเหตุ: parentนั่นคือ GroupView


GroupView นี้ร้องขอมุมมองจากอะแดปเตอร์เมื่อเรียกใช้getView()เมธอด ..... ดังนั้นเราจึงได้รับบริบทของ GroupView นั้นและตรวจสอบตามเงื่อนไขด้านบน
Abd Salam Baker

0

ใน Kotlin มีวิธีที่สะอาดกว่าโดยใช้ฟังก์ชันแลมบ์ดาไม่จำเป็นต้องใช้อินเทอร์เฟซ:

class MyAdapter(val adapterOnClick: (Any) -> Unit) {
    fun setItem(item: Any) {
        myButton.setOnClickListener { adapterOnClick(item) }
    }
}

class MyActivity {
    override fun onCreate(savedInstanceState: Bundle?) {
        var myAdapter = MyAdapter { item -> doOnClick(item) }
    }


    fun doOnClick(item: Any) {

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