마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Recyclerview adapter에서 startActivityForResult 사용하기

0 추천

안드로이드 스튜디오 초보입니다.

 

Recyclerview Adapter: 리사이클러뷰의 어댑터

AppCompatActivity: 액티비티를 팝업창으로 만들어 사용

최종 AppCompatActivity: 리사이클러뷰가 존재하는 액티비티

 

Recyclerview Adapter(리스트뷰의 버튼 클릭)-> AppCompatActivity(버튼과 일치하는 정보가 팝업창에 존재, 팝업창의 버튼 클릭시 해당 정보를 다음 액티비티에 옮김) -> 최종 AppCompatActivity(팝업창의 정보를 받음) 순으로 데이터를 이동하려 합니다.

 

Adapte가 액티비티가 아니기 때문에 아래와 같이 Activity를 앞에 붙여서 startActivityForResult를 사용했습니다.

((Activity)mContext).startActivityForResult(intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 1);

 

그러나 팝업창은 열리지 않고 앱이 종료되며 아래와 같은 오류가 계속 지속됩니다.

android.app.Application cannot be cast to android.app.Activity

 

아래 오류를 해결할 수 있는 방안이 무엇일까요?

 

(아래는 startActivityForResult를 사용하기 전 코드입니다. (이때는 팝업창 실행이 잘 되었습니다.))

mContext.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

 

익명입니다 (160 포인트) 님이 10월 11일 질문
익명입니다님이 10월 12일 수정

2개의 답변

0 추천

RecyclerView.Adpater 는 Context계열의 클래스가 아니기  때문에 액티비티를 띄울 수가 없습니다. 그리고 어앱터의 아이템 클릭과 같은 이벤트는 어댑터의 역할이 아니기 때문에 어탭터를 호출하는 쪽에서 처리하도록 바꾸시는게  좋습니다.

public interface ItemClickListener {
    vod onItemClicked(ListItem item);
}

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
   
      private ItemClickListener listener;

      public void setItemClickListener(ItemClickListener listener) {
           listener.mListener.= listener;
      }
   
     @Overrid
      public MyViewHolder onCreateViewHolder (ViewGroup parent, 
                int viewType) {
              View itemView = LayoutInflater.from(parentl.context).inflater(R.layout.item_layout, parent, false);
              return new MyViewHolder(itemView, listener);
       }

      @Override
      public void onBindViewHolder (MyViewHolder holder, 
                int position) {
            holder.bindItem(getItem(position));
      }

      private ListItem getItem(int position) {
             // ...
      }
}

public class MyViewHolder extends RecyclerView.ViewHolder {

        private ItemClickListener listener;

        public MyViewHolder(View itemView, ItemClickListener listener) {
             super(itemView);
             this.listener = listener;
        }     
   
       public void bindItem(ListItem item) {
           itemView.setOn.setOnClickListener(new View.OnClickListener() {
                   @Override
                    public void onClick(View view) {
                          if (listener != null) {
                                listener.onItemClicked(item);
                          }
                    }
            });
           ...
       }
}


Activity
Adatper myAdapter = new MyAdapter();
myAdapter.setItemClickListener( new ItemClickListener() {
    @Override
    public void onItemClicked(ListItem item) {
          //여기에서 startActivityForResult 호출
          startActivityForResult(...)
    }
});



 

위와 같은 식으로 어댑터외부에서 리스너를 이용해 처리를 하시면 됩니다.

spark (79,410 포인트) 님이 10월 11일 답변
adapter 각각 버튼을 클릭했을 때 각 위치(position)에 일치하는 API 정보를 팝업창에 불러옵니다. 각각 정보를 어떻게 intent 해야할지 모르겠습니다.
0 추천

제가 보여드린 샘플에서 ItemClickListener를 잘 보시면 ListItem을 Adapter에서 아이템을 클릭할 때 받아오도록 되어 있습니다. 혹 어떤 아이템의 position 이 추가로 필요하시면

public interface ItemClickListener {
    vod onItemClicked(ListItem item, int position);
}
을 추가하시고 ViewHolder 에서
 listener.onItemClicked(item, getBindingAdapterPosition());

을 호출해 주면 되는데, 거의 대부분의 경우는 position 정보보다는 item정보만 있으면 될 겁니다.

그리고 Intent에 받아온  item 정보를 전달하는 부분은 안드로이드의 Intent와 Bundle에 대해서 이해하셔야 합니다. 안드로이드에서는 액티비티 간 데이터를 전달할 때 Intent라는 것을 통해서 하게 되는게, pritimitive type(String, int long, float, double, boolean) 들은 바로 전달이 가능하지만 오브젝트는 해당 클래스가 Serializable 이나 Parcelable 인터페이스를 구현해야 합니다. 이건 JVM을 사용하는 안드로이드 시스템의 디자인 때문에 효율성과 안전을 위해 오브젝트를 바로 넘기지 못하고 serialization/deserialization 이라는 과정을 거치기 때문인데요. 이렇게 하려면 해당 오브젝트는 Serializable이 되거나 Parcelable 이 되어야 합니다. 둘의 차이는 복잡한 구조의 클래스의 경우는 Parcelable 이 빠르다고 알려져 있는데, Serializable은 별도의 구현이 필요하지 않지만(코틀린은 그런데, 자바는 아마 id하나를 static field로 제공해주야 했던 걸로 기억합니다. Android studio 힌트를 누르시면 자동으로 해줍니다.), Parcelable은 코드를 좀 작성 해주어야 합니다. 이건 Android Studio에 Java Parcelable 코드를 자동으로 생성해주는 plugin을 사용하거나 안드로이드 스튜디오에서 제공하는 기능을 사용하시면 됩니다.

Serializable을 사용할 것이냐 Parcelable을 사용할 것인지는 님이 사용하는 클래스를 보시고 판단하시던가 항상 Parcelable 만 사용하셔도 될 것 같습니다.

Intent에는 putExtra메소드가 있습니다. 이 메소드는 다양한 타입을 받아들이도록 overload 되어 있습니다. 여기에 필요한 object를 넘기시고 받는 쪽에서는 Intent.get*을 이용해 읽어오시면 됩니다.

public class ListItem implements Serializable {
   ....
}


public static final String KEY_LISTITEM = "listItemKey"; // 원하시는 키값

myAdapter.setItemClickListener( new ItemClickListener() {
    @Override
    public void onItemClicked(ListItem item) {
          Intent intent = new Intent(this, TargetActivity.class);
          intent.putExtra(KEY_LISTITEM, item)
          startActivityForResult(intent)
    }
});


// TargetActivity

@Override
public void onCreate(Bundle saveInstance) {
      super.onCreate(saveInstance);
      ListItem intentedListItem = (ListItem) getIntent(). getSerializableExtra(KEY_LISTITEM);
      if (intentedListItem != null) {
           // 원하는 처리
      }

}

 

위처럼 Intent 를 통해 넘기시고 intent에서 같은 키값에 해당하는 데이터를 읽어오면 됩니다. 

개발자 문서를 보시면 예제와 함께 설명이 되어 있으니 참고하세요.

https://developer.android.com/training/basics/firstapp/starting-activity

spark (79,410 포인트) 님이 10월 12일 답변
...