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

안드로이드 fragment에서 activty로 데이터를 보내고 intent를 할때 어떻게 하나요?

0 추천

지금 fragment에서 api 정보를 listview에 보여주고 있는데 item을 클릭했을때 상세페이지로 detailactivity로 intent시키고 싶은데 어떻게 해야하나요...ㅠㅠㅠ 도와주세요

지금 toast해서 식물이름까지는 나오는데 화면전환이 어떻게 하는지 잘 모르겠네요 ㅠㅠ

<search.fragment>

public class search extends Fragment {
    private View view;
    private EditText search_edit;
    private ListView listView;
    private Button button;
    XmlPullParser xpp;
    String key="20220921NVF7XYD8DH0PUMNT2RWG"; //농사로 검색 키
    Bitmap bitmap;
    PlantAdapter adapter;
    ArrayList<String> data1 = new ArrayList<>();
    ArrayList<String> imgdata = new ArrayList<>();
    public static ArrayList<Plant> plantList = new ArrayList<Plant>();
    SendEventListener SendEventListener;


    TextView textView;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup
            container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.search, container, false);
        search_edit = (EditText) view.findViewById(R.id.edit);
        button = view.findViewById(R.id.button);
        listView = view.findViewById(R.id.listview);
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                    switch( v.getId() ){

                        case R.id.button:

                            //Android 4.0 이상 부터는 네트워크를 이용할 때 반드시 Thread 사용해야 함

                            new Thread(new Runnable() {

                                @Override

                                public void run() {

                                    // TODO Auto-generated method stub

                                    try {
                                        adapter = new PlantAdapter();

                                        imgdata = getXmlImage();
                                        data1 = getXmlData();//아래 메소드를 호출하여 XML data를 파싱해서 String 객체로 얻어오기

                                        for(int i=0;i<data1.size();i++) {
                                            URL imgurl = new URL("http://www.nongsaro.go.kr/cms_contents/301/" + imgdata.get(i));
                                            HttpURLConnection conn = (HttpURLConnection) imgurl.openConnection();
                                            conn.setDoInput(true);
                                            conn.connect();


                                            InputStream is1 = conn.getInputStream();
                                            Log.d("img test", String.valueOf(is1));
                                            bitmap = BitmapFactory.decodeStream(is1); //bitmap으로 디코딩
                                            Log.d("test", String.valueOf(bitmap));
                                            adapter.addItem(new Plant(data1.get(i), bitmap));
                                        }

                                    } catch (UnsupportedEncodingException e) {
                                        e.printStackTrace();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }

                                    //UI Thread(Main Thread)를 제외한 어떤 Thread도 화면을 변경할 수 없기때문에

                                    //runOnUiThread()를 이용하여 UI Thread가 TextView 글씨 변경하도록 함

                                    getActivity().runOnUiThread(new Runnable() {

                                        @Override
                                        public void run() {

                                            listView.setAdapter(adapter);
                                            search_edit.setText(null);
                                            setUpOnClickListense();
                                        }
                                    });
                                }
                            }).start();
                            break;
                    }
                }//mOnClick method..
        });
        return view;
    }

    public void onAttach(Context context) {

        super.onAttach(context);
        try{
            SendEventListener = (SendEventListener) context;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void setUpOnClickListense() {
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(getActivity(),data1.get(position),Toast.LENGTH_LONG).show();
                
            }
        });
    }

    //XmlPullParser를 이용하여 농사로 에서 제공하는 OpenAPI XML 파일 파싱하기(parsing)

<detail.java>

package com.example.deletelater;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;

import android.app.TimePickerDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

public class PlantSearchDetail extends AppCompatActivity  {

    Plant selectedPlant;
    Bitmap img;
    String name;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_plant_search_detail);

    }


}

 

pium (120 포인트) 님이 2022년 11월 20일 질문
api 데이터를 activity로 끌고오는거랑 화면전환이 궁금합니다!

2개의 답변

0 추천

Intent를 통해 액티비티를 띄우는 방법을 알고 계시다면, 동일하게 처리하시면 됩니다.
코드를 약간 수정해서 조금 더 읽기 좋게 만들면서 필요한 코드를 집어 넣을게요.

public class search extends Fragment {
    private static final String key="20220921NVF7XYD8DH0PUMNT2RWG"; //농사로 검색 키

    ...
    
    private XmlPullParser xpp;
    private Bitmap bitmap;
    private PlantAdapter adapter;
    private final List<String> data1 = new ArrayList<>();
    private final Lis<String> imgdata = new ArrayList<>();
     // 액티비티 안에 public static 으로 데이터를 공유하시지 마세요. 라이프 사이클에 따라서 데이터가 초기화 될 수 있습니다.
    private final List<Plant> plantList = new ArrayList<Plant>();
    private SendEventListener SendEventListener;
 
    private TextView textView;
 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup
            container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.search, container, false);
    }
 
    public void onAttach(Context context) {
 
        ...
    }

   @Override
   public void onViewCreated (View view, Bundle savedInstanceState) {
          super.onViewCreated(view, savedInstanceState);
          setupViews();
   }
 
    private void setupViews() {
        View view = requireView();

        search_edit = (EditText) view.findViewById(R.id.edit);

        button = view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
                @Override
                 public void onClick(View view) {
                      fetchPlantList();
                 }
        });

        listView = view.findViewById(R.id.listview);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 showPlantDetails(position);
            }
        }); 
        adapter = new PlantAdapter();
        listView.setAdapter(adapter);
    }

   private void fetchPlantList() {
     
   }

   private void showPlantDetails(int position) {

   }
}

 

fetchPlantList에 기존에 네트워크를 통해 식물리스트를 가져오는 코드를 위치시킵니다. 그런데 대부분의 안드로이드 개발에서는 쓰레드를 직접 사용해서 REST API를 호출하지 않습니다. 쓰레드를 직접 핸들링할 수는 있지만, 처리해야 할 일들이 많기 때문에, 대부분은 라이브러리를 통해 처리합니다. Retrofit 이 제일 많이 사용되고 있고,  Volley도 종종 사용됩니다.  안드로이드를 계속 하실 계획이라면, 이 라이브러리들은 별도로 공부를 하시는게 좋습니다. 여기서는 기존 코드를 사용하는 걸로 하겠습니다.

이미지를 가져오는 부분은 대부분의 개발자가 사용하는 Glide 라는 라이브러리를 사용하시기 바랍니다. (https://github.com/bumptech/glide) 이미지 네트워크를 통해 가져오는 코드는 쓰레딩, 네트워크 취소, 스케일링, 캐시 등등 고려해야할 요소가 상당히 많기 때문에, 동작원리는 이해하되 라이브러리를 사용하는게 낫습니다.

Plant 클래스를 약간 수정해서 이름과 이미지 URL경로를 필드로 설정하도록 하겠습니다.
 

public class Plant {
    private final static String IMAGE_BASE_URL = "http://www.nongsaro.go.kr/cms_contents/301/";
    
    private final String name;
    private final String imageUrl;

    public Plant(String name, String imagePath) {
        this.name = name;
        this.imageUrl = IMAGE_BASE_URL + imagePath;
    }

    public String getName() {
        return name;
    }

    public String getImageUrl() {
        return imageUrl;
    }
}

 

이미지 데이터를 가져오는 부분은 비동기이므로 작업이 끝난 것을 알려면 콜백을 통해 처리해야 합니다. 버튼 클릭 이벤트를 처리할 때 리스너를 사용하는 것과 동일한 구조입니다. 이미지 데이터를 처리 결과를 받을 수 있도록 리스너 인터페이스를 하나 만듭니다.

interface PlantFetchListener {
    void onPlantFetchSuccess(List<Plant> plantList);

    void onPlantFetchFailure(Exception e);
}

 

fragment에 구현되지 않았던 메소들을 채워 넣겠습니다. 먼저 fragment가 PlantFetchListener를 구현하도록 변경합니다.

// PlantAdapter의 아이템 리스트를 외부에서 설정할 수 있도록 코드 추가.
public class PlantAdapter ... {
    private List<Plant> items = new ArrayList<>();

    public void setItems(List<Plant> items) {
         this.items = items;
         notifyDataSetChanged();
    }

    public int getItemCount() {
        return items.size();
    }

    ...
}

public class search extends Fragment implements PlantFetchListener {
   
    @Override
    public void onPlantFetchSuccess(List<Plant> plantList) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                search_edit.setText(null);
                setUpOnClickListense();
                adapter.setItems(plantList);
            }
        });
    }   


    @Override
    public void onPlantFetchFailure(Exception e) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
               // TODO: 에러메세지 출력(Toast, Snackbar..)
            }
        });
    }

}

 

 

spark (224,800 포인트) 님이 2022년 11월 20일 답변
spark님이 2022년 11월 20일 수정
0 추천

search 프레그먼트에 구현되지 않는 메소들을 구현합니다.

// 아래 두 멤버 변수는 필요없으므로 삭제.
// ArrayList<String> data1 = new ArrayList<>();
// ArrayList<String> imgdata = new ArrayList<>();

private void setupViews() {
        ..
        button.setOnClickListener(new View.OnClickListener() {
                @Override
                 public void onClick(View view) {
                      fetchPlantList(search.this);
                 }
        });
 
        ...
}

private void fetchPlantList(PlantFetchListener listener) {
 new Thread(new Runnable() {
            @Override
            public void run() {
                plantList.clear();
                try {
                    List<String> plantImageUrlList = getXmlImage();
                    List<String> plantNameList = getXmlData();//아래 메소드를 호출하여 XML data를 파싱해서 String 객체로 얻어오기

                    for (int i = 0; i < plantNameList.size(); i++) {
                        Plant plant = new Plant(plantNameList.get(i), plantImageUrlList.get(i));
                        plantList.add(plant);
                    }

                    if (listener!= null) listener.onPlantFetchSuccess(plantList);

                } catch (Exception e) {
                    if (listener!= null) listener.onPlantFetchFailure(e);
                }
            }
        }).start();
}

보시면 알겠지만, 네트워크 호출을 쓰레드 안에서 처리하고, 성공적으로 끝나거나 에러가 나면 전달받은 listener의 메소드를 호출하면 되겠죠? 이렇게 하면 리스너는 작업이 완료된 시점에 결과값을 받을 수 있습니다. 리스너 안에서는 UI 대한 처리가 이루어지면 되겠구요.

네트워트 호출 전에 프로그레스바를 보여주고 완료되면 사라지게 하면 user experience가 좀 더 좋아지겠죠?

리스트뷰에서 아이템을 클릭하면 디테일 화면으로 선택된 plant를 보여주는 건 어댑터에서 아이템을 클릭할 때 plantList에 관련 데이터가 있으므로 여기서 필요한 데이터를 꺼내서 PlanSearchDetail로 전달하면 됩니다. 안드로이드에서는 Class 타입을 화면간 전달하려면 Serializable이나 Parcelable 타입이 되어야 합니다. 따라서 Plant 클래스를 Serializable로 만듭니다.

public class Plant implements Serializable 

search 프레그먼트의 showPlantDetails 메소드를 구현합니다.

public class PlantSearchDetail extends AppCompatActivity  {
   public static final EXTRA_DETAILS = "Details';

   ...
}

// search
private void showPlantDetails(int position) {
     Plant plant = Objects.requireNonNull(plantList.get(position));
     Intent intent = new Intent();
     intent.putExtra(PlantSearchDetail.EXTRA_DETAILS, plant);
     startActivity(intent)
}

PlantSearchDetail에서는 intent에서 전달된 plant를 꺼내서 사용하면 됩니다.

public class PlantSearchDetail extends AppCompatActivity  {
     
     ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_plant_search_detail);
        handleIntent();
    }
 
   private void handleIntent() {
        Plant plan = (Plant) getItnent().getSerializableExtraIEXTRA_DETAILS);
   }
}

 

spark (224,800 포인트) 님이 2022년 11월 20일 답변
spark님이 2022년 11월 20일 수정
...