ListFragment에 Custom ListView 만드는 방법. (ListFragment with Custom ListView)

2016. 6. 14. 17:56


1. ListFragment

Fragment에 ListView를 만드는 방법이 그리 어렵지는 않지만, 그 마저도 쉽게 구현할 수 있도록, 안드로이드에서는 ListFragment라는 클래스를 제공하고 있습니다. 지난 글 [ListView를 가지는 Fragment 만들기]을 통해 기본적인 형태(TextView만 사용)의 ListFragment를 구현하는 방법에 대해 살펴볼 수 있습니다.


ListFragment에 Adapter를 지정하는 setListAdapter() 함수의 존재를 보면 알 수 있듯이, ListFragment는 ListView의 Wrapper(감싸기, 포장) 클래스 역할을 하는 Fragment라고 정의할 수 있습니다. 즉, ListFragment는 ListView를 멤버로 가지면서, ListView에 대한 구현 절차를 ListView에 대한 직접적인 접근이 아닌, ListFragment에서 제공하는 함수를 통해 수행하도록 만든 클래스인 것입니다.


이렇게 함으로써 Fragment와 ListView가 제공하는 기능들을 한번에 구현할 수 있고, 각각을 구현하는 과정을 다소 간소화할 수 있는 장점이 있습니다. 하지만 ListFragment를 익숙하게 사용하기 위해서는 Fragment와 ListView의 구현작업에 대한 이해가 필수적이므로, 먼저 설명한 [ListView] 및 [Fragment] 에 관한 내용들을 살펴보시길 바랍니다.


1.1 Custom ListView

[안드로이드 커스텀 리스트뷰 만드는 방법]에서 살펴봤듯이, ListView를 사용할 때, 하나의 ListView 아이템에 이미지나 버튼, 텍스트 등의 View 위젯을 조합하여 여러 정보를 한번에 보여주는 경우가 흔합니다. 하지만 텍스트만으로 아이템을 표시하는 [안드로이드 리스트뷰 기본 사용법]에 비해 몇 가지 추가적인 작업이 필요합니다.


ListFragment 또한 마찬가지입니다. ListFragment에 Custom ListView를 만들기 위해서는 [ListView를 가지는 Fragment 만들기]에서 살펴본 과정에 몇 가지 작업을 더 해줘야 합니다.


2. ListFragment에 Custom ListView 사용하기

[안드로이드 커스텀 리스트뷰 만드는 방법]에서 작성한 예제를 ListFragment를 사용하여 만들어 보도록 하겠습니다.


ListFragment 커스텀 리스트뷰 아이템 레이아웃



2.1 워크 플로우


ListFragment 커스텀 리스트뷰 작성 절차



2.2 Activity에 Fragment 추가

Activity의 Layout 리소스 XML에 ListFragment를 추가합니다.


[STEP-1] "activity_main.xml" - MainActivity에 Fragment 추가.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.customlistfragmentexample1.MainActivity"
    tools:showIn="@layout/activity_main">

    <fragment
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/customlistfragment"
        android:name="com.recipes4dev.examples.customlistfragmentexample1.CustomListFragment"
        tools:layout="@layout/activity_main" />

</RelativeLayout>


2.3 ListFragment 상속 및 구현

ListFragment를 상속한 새로운 클래스를 추가합니다.


[STEP-2] "CustomListFragment.java" - ListFragment 클래스 상속.
import android.support.v4.app.ListFragment;

public class CustomListFragment extends ListFragment {
    // TODO
}


2.4 ListView 아이템에 대한 Layout 구성

ListFragment의 ListView 아이템에 표시될 Layout을 구성합니다. [안드로이드 커스텀 리스트뷰 만드는 방법]에서 사용한 Layout 리소스 XML을 그대로 사용합니다.


[STEP-3] "/res/layout/listview_item.xml" - ListView 아이템 Layout 작성.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/imageView1"
        android:layout_weight="1" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="4">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:id="@+id/textView1"
            android:textSize="24sp"
            android:textColor="#000000"
            android:gravity="center_vertical"
            android:layout_weight="2" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:id="@+id/textView2"
            android:textSize="16sp"
            android:textColor="#666666"
            android:layout_weight="1" />
    </LinearLayout>

</LinearLayout>


2.5 아이템 데이터에 대한 클래스 정의

ListView 아이템 클래스 또한 [안드로이드 커스텀 리스트뷰 만드는 방법]의 클래스를 사용합니다.


[STEP-4] "ListViewItem.java" - ListView 아이템 데이터 클래스 정의.
package com.recipes4dev.examples.customlistfragmentexample1;

import android.graphics.drawable.Drawable;

public class ListViewItem {
    private Drawable iconDrawable ;
    private String titleStr ;
    private String descStr ;

    public void setIcon(Drawable icon) {
        iconDrawable = icon ;
    }
    public void setTitle(String title) {
        titleStr = title ;
    }
    public void setDesc(String desc) {
        descStr = desc ;
    }

    public Drawable getIcon() {
        return this.iconDrawable ;
    }
    public String getTitle() {
        return this.titleStr ;
    }
    public String getDesc() {
        return this.descStr ;
    }
}


2.6 Adapter 클래스 상속 및 구현.

BaseAdapter를 상속하여 새로운 Adapter 클래스를 추가합니다. (ListViewAdapter의 각 함수에 관한 설명은 [안드로이드 커스텀 리스트뷰 만드는 방법] 또는 코드의 주석을 참고하시기 바랍니다.)


[STEP-5] "ListViewAdapter.java" - BaseAdapter 상속 및 ListViewAdapter 구현.
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

public class ListViewAdapter extends BaseAdapter {
    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
    private ArrayList<ListViewItem> listViewItemList = new ArrayList<ListViewItem>() ;

    // ListViewAdapter의 생성자
    public ListViewAdapter() {

    }

    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
    @Override
    public int getCount() {
        return listViewItemList.size() ;
    }

    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final int pos = position;
        final Context context = parent.getContext();

        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.listview_item, parent, false);
        }

        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
        ImageView iconImageView = (ImageView) convertView.findViewById(R.id.imageView1) ;
        TextView titleTextView = (TextView) convertView.findViewById(R.id.textView1) ;
        TextView descTextView = (TextView) convertView.findViewById(R.id.textView2) ;

        // Data Set(listViewItemList)에서 position에 위치한 데이터 참조 획득
        ListViewItem listViewItem = listViewItemList.get(position);

        // 아이템 내 각 위젯에 데이터 반영
        iconImageView.setImageDrawable(listViewItem.getIcon());
        titleTextView.setText(listViewItem.getTitle());
        descTextView.setText(listViewItem.getDesc());

        return convertView;
    }

    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
    @Override
    public long getItemId(int position) {
        return position ;
    }

    // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
    @Override
    public Object getItem(int position) {
        return listViewItemList.get(position) ;
    }

    // 아이템 데이터 추가를 위한 함수. 개발자가 원하는대로 작성 가능.
    public void addItem(Drawable icon, String title, String desc) {
        ListViewItem item = new ListViewItem();

        item.setIcon(icon);
        item.setTitle(title);
        item.setDesc(desc);

        listViewItemList.add(item);
    }
}


2.7 Adapter 생성 후 ListView에 지정.

[ListView를 가지는 Fragment 만들기]에서는 Activity에서 Adapter를 생성한 다음, ListFragment.setListAdapter() 함수를 호출하여 Adapter를 지정하였습니다. 하지만 ListFragment를 상속받은 이상, 굳이 Adapter를 ListFragment의 외부에서 생성할 필요가 없죠. 대신, ListFragment의 내부에서 Adapter와 관련된 처리를 수행해주면 됩니다.


[STEP-6] "CustomListFragment.java" - CustomListFragment에서 Adapter 생성 및 지정.
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.R.id.*;
import android.widget.ListView;

public class CustomListFragment extends ListFragment {

    ListViewAdapter adapter ;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Adapter 생성 및 Adapter 지정.
        adapter = new ListViewAdapter() ;
        setListAdapter(adapter) ;

        // 첫 번째 아이템 추가.
        adapter.addItem(ContextCompat.getDrawable(getActivity(), R.drawable.ic_account_box_black_36dp),
                "Box", "Account Box Black 36dp") ;
        // 두 번째 아이템 추가.
        adapter.addItem(ContextCompat.getDrawable(getActivity(), R.drawable.ic_account_circle_black_36dp),
                "Circle", "Account Circle Black 36dp") ;
        // 세 번째 아이템 추가.
        adapter.addItem(ContextCompat.getDrawable(getActivity(), R.drawable.ic_assignment_ind_black_36dp),
                "Ind", "Assignment Ind Black 36dp") ;

        return super.onCreateView(inflater, container, savedInstanceState);
    }
}


2.8 ListView 클릭 이벤트 처리

ListFragment의 onListItemClick() 함수를 override하여 ListView 아이템 클릭 이벤트를 처리합니다.


[STEP-7] "CustomListFragment.java" - onListItemClick() 함수를 override하여 클릭 이벤트 처리
public class CustomListFragment extends ListFragment {

    // 코드 계속 ...

    @Override
    public void onListItemClick (ListView l, View v, int position, long id) {
        // get TextView's Text.
        ListViewItem item = (ListViewItem) l.getItemAtPosition(position) ;

        String titleStr = item.getTitle() ;
        String descStr = item.getDesc() ;
        Drawable iconDrawable = item.getIcon() ;

        // TODO : use item data.
    }

    // ... 코드 계속
}


2.9 ListFragment 외부(Activity)에서 아이템 추가를 위한 함수 구현.

[STEP-6]의 onCreateView에서 Adapter를 생성하고 ListView에 지정한 다음, 샘플 데이터를 추가하였지만, 이는 앱 시작 시 ListFragment 내부에서만 사용할 수 있는 방법입니다. 일반적으로 ListView에 아이템이 추가될 때는 Activity에 정의된 이벤트 핸들러 함수에서 데이터가 로드 되는 것과 같이 앱 실행 중 동적으로 이루어지므로 ListFragment 외부에서 데이터를 추가할 수 있는 방법이 있어야 합니다.


이를 위해 CustomListFragment에 아이템 추가를 위한 함수를 정의하고, Activity에서 해당 함수를 통해 아이템을 추가하는 코드를 작성하겠습니다.


[STEP-8] "CustomListFragment.java" - 아이템 추가 함수 정의.
public class CustomListFragment extends ListFragment {

    // 코드 계속 ...

    public void addItem(Drawable icon, String title, String desc) {
        adapter.addItem(icon, title, desc) ;
    }
}


[ListView를 가지는 Fragment 만들기]에서 살펴봤듯이, Activity에 추가한 CustomListFragment의 참조를 가져오기 위해, FragmentManager의 findFragmentById() 함수를 호출합니다. 그리고 addItem() 함수를 호출하여 새로운 아이템을 추가하였습니다.


[STEP-9] "MainActivity.java" - Activity에서 아이템 추가.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        CustomListFragment customListFrgmt = (CustomListFragment) getSupportFragmentManager().findFragmentById(R.id.customlistfragment);
        customListFrgmt.addItem(ContextCompat.getDrawable(this, R.drawable.ic_account_box_black_36dp),
                "New Box", "New Account Box Black 36dp") ;
    }
}


3. 예제 실행 화면

예제를 작성하고 실행하면 다음과 같은 실행화면이 표시됩니다.


ListFragment 커스텀 리스트뷰 실행 화면



4. 참고.

.END.


ANDROID 프로그래밍/FRAGMENT , , , , , , , , ,

  1. 이전 댓글 더보기
  2. Blog Icon
    초보요옹

    안녕하세요. 얼마전 네비게이션 글에서 질문을 남겼던 학생입니다.
    일단 덕분에 뷰페이저에서 각 프래그먼트에 커스텀 리스트뷰를 띄울수 있게 되었습니다.
    정말 정말 감사합니다!
    다름이 아니라
    public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    CustomListFragment customListFrgmt = (CustomListFragment) getSupportFragmentManager().findFragmentById(R.id.customlistfragment);
    customListFrgmt.addItem(ContextCompat.getDrawable(this, R.drawable.ic_account_box_black_36dp),
    "New Box", "New Account Box Black 36dp") ;
    }
    }

    volley 라이브러리를 통해 서버에서 데이터를 가져오는 부분이 있어,
    가져온 데이터를 리스트뷰의 아이템들을 셋팅 해주고 싶은데,
    위의 MainActivity 클래스에서 저 코드를 사용하게 될 경우, 아무런 에러코드 없이 앱이 죽어버립니다..ㅠㅠ

    그래서 혹시!
    1. ListFragment를 상속받은 클래스에서도 데이터 통신이 가능한지 궁금합니다!
    2. 만약 안된다면 MainActivity의 아이템 추가부분의 코드가 있을 경우 에러코드가 없이 앱이 종료되는지 알고 싶습니다!
    바쁘실텐데 정말 죄송합니다 ㅠㅠ 시간나실때 답변해주시면 정말 감사하겠습니다!
    작정자님의 가설이나 힌트를 좀 받고 싶습니다!
    항상 좋은 글 작성해주셔서 감사합니다. 좋은 하루 되세요!

  3. 아무런 에러코드 없이 앱이 죽지는 않을 것 같은데요. 아마 에러 메시지를 놓치신 건 아닌가 하는 생각이 드네요.

    올려주신 코드에서 문제가 생겼다면, 아마도...
    customListFrgmt에 대한 참조를 getSupportFragmentManager().findFragmentById() 함수로 가져온 다음, null인지 체크하지 않고 addItem() 함수를 호출했기 때문일텐데요.

    질문글의 코드에서 customListFrgmt가 null인지 한번 체크해줄 필요가 있을 것 같습니다. 아래 코드 처럼 말이죠.

    CustomListFragment customListFrgmt = (CustomListFragment) getSupportFragmentManager().findFragmentById(R.id.customlistfragment);
    if (customListFrgmt != null) {
    customListFrgmt.addItem(ContextCompat.getDrawable(this, R.drawable.ic_account_box_black_36dp),
    "New Box", "New Account Box Black 36dp") ;
    }


    그리고 추가 질문에 대한 답변을 달자면...

    1. ListFragment를 상속받은 클래스에서도 데이터 통신이 가능한지 궁금합니다!
    ==> 가능합니다. 하지만 어떤 종류의 데이터 통신이냐에 따라 다르겠죠. 안드로이드 메인스레드 제약으로 별도의 스레드에서 처리해야 하는 경우가 많습니다.

    2. 만약 안된다면 MainActivity의 아이템 추가부분의 코드가 있을 경우 에러코드가 없이 앱이 종료되는지 알고 싶습니다!
    ==> 질문의 내용이 정확히 파악되지 않는데요. 아이템 추가부분 코드가 있는지 여부와 에러코드가 없이 앱이 종료되는 것은 관계가 없습니다.

    질문에 대한 답이 되었는지 모르겠네요.
    추가적인 질문 내용이 있으면 다시 질문글 남겨주세요.

    감사합니다.

  4. Blog Icon
    초보요옹

    답변해주셔서 정말 감사합니다!
    일단 질문드렷던 1번을 이용해서 제가 원하는 결과물을 얻었습니다!
    공부를 하며 Fragment를 사용해본적이 없어 고생 많이 했는데 ㅠㅠ
    정말 덕분에 해결되어 정말 너무 감사드립니다!
    앞으로도 좋은 글 많이 참고하며 감사한 마음으로 공부하겠슴다 ㅎㅎ
    좋은 하루 되세요!

  5. 저의 답변이 문제 해결에 도움이 된 것 같아서 뿌듯하네요.

    다른 도움이 필요하시면 언제든 질문 글 남겨주세요.

    감사합니다.

  6. Blog Icon
    도와주십쇼오오오

    안녕하세요. 세개의 탭을 만들고, 그 탭 각각 안에 프래그먼트 1,2,3 을 리스트뷰로 만들려고 하는데요..

    여기 글을 보면서 공부하고 있습니다.

    ListViewAdpater.java 에서

    @Override
    public View getView(int position, View convertView, Viewgroup parent) {

    겟뷰 메소드 안의 모든 parent 에 대해 에러가 뜹니다..

    책에서는 파라메터로 Viewgroup viewGroup 으로 하라고 하는데.. 이렇게 해도 에러가 뜨네요

    해결 부탁드립니다 ㅜㅜ



  7. Viewgroup을 ViewGroup으로 바꾸십시오오오오.

    대소문자 구분하셔야 합니다. ^^

  8. Blog Icon
    도와주십쇼오오오

    아이고 세상에 감사합니다 ..!
    금방 또 찾아뵙겠습니다

  9. 별 말씀을요!!
    또 궁금한 게 있으면 질문글 남겨주세요.
    즉답을 드리긴 힘들겠지만,
    질문글 확인하는대로, 답글 달아드릴게요.

    그리고 에러 메시지를 잘 보세요.
    에러 메시지 안에 해결에 대한 답이 있습니다.

    감사합니다.

  10. Blog Icon
    도와주십쇼오오오

    또 혹시...세개의 탭 을 각각 터치 했을 때 프래그먼트에 각각 다른 리스트뷰가 뜨게 하려면

    어댑터를 세개나 써야 하는건가요...?

    항상 정리해주시는 워크플로우 로 답변좀 부탁드리겠습니다 ㅜㅜ

  11. "다른 리스트뷰"라는 말에서, 뭐가 다른 걸 의미하나요?
    리스트뷰의 아이템 레이아웃은 동일하고 표시되는 데이터 내용만 다르다면 어댑터를 각각 만들지 않아도 되지만, 리스트뷰 아이템 레이아웃이 아예 다르다면 각각의 리스트뷰에 대해 어댑터를 만들어줘야 하겠죠.

    조금 더 개념과 원리에 대해 고민해보실 필요가 있을 것 같습니다.
    그리고 직접 구현과 비교를 통해, 조금 더 경험해보시길 바랄게요.

    올려주신 질문에 대한 저의 답변이, 오히려 반복학습과 시행착오를 통한 경험에 방해물이 될 수 있으니까요.

    감사합니다.

  12. Blog Icon
    송성철

    올려주신 자료들 덕분에 안드로이드 공부를 보다 쉽게 하고 있습니다. 감사합니다.

    뽀따님의 강의 중 '프레그먼트 기본 사용법'을 활용하여 버튼을 누를 때마다 다른 Custom ListView가 나오도록 코딩을 하고 있는데, 계속 에러가 뜨는데 해결하는 방법을 몰라서 문의 드립니다. 에러가 나는 부분은MainActivity의
    - 28번째줄 'R.id.customlistfragment,new CustomListFragment()'
    - 70번째줄 'fr'
    입니다.


    전체 코딩은 아래와 같습니다.
    package com.example.nuriapp_p.fragment_customlistview;

    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.content.DialogInterface;
    import android.support.v4.app.Fragment;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;

    public class MainActivity extends AppCompatActivity {

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

    Button btn_left = findViewById(R.id.btn_left);
    Button btn_center = findViewById(R.id.btn_center);
    Button btn_right = findViewById(R.id.btn_right);

    FragmentManager fm = getFragmentManager();
    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction.add(R.id.customlistfragment,new CustomListFragment());
    fragmentTransaction.commit();

    btn_left.setOnClickListener(new Button.OnClickListener(){
    @Override
    public void onClick(View v) {
    switchFragment(0);
    }
    });

    btn_center.setOnClickListener(new Button.OnClickListener(){
    @Override
    public void onClick(View v) {
    switchFragment(1);
    }
    });

    btn_right.setOnClickListener(new Button.OnClickListener(){
    @Override
    public void onClick(View v) {
    switchFragment(2);
    }
    });

    CustomListFragment customListFrgmt = (CustomListFragment) getSupportFragmentManager().findFragmentById(R.id.customlistfragment);
    customListFrgmt.addItem(ContextCompat.getDrawable(this, R.drawable.burj),
    "New Box", "New Account Box Black 36dp");
    }

    public void switchFragment(int isFragment) {
    Fragment fr;

    if (isFragment == 0) {
    fr = new CustomListFragment();
    } else if (isFragment == 1) {
    fr = new CustomListFragment2();
    } else if (isFragment == 2) {
    fr = new CustomListFragment3();
    }

    FragmentManager fm = getFragmentManager();
    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction.replace(R.id.customlistfragment, fr);
    fragmentTransaction.commit();

    Log.d("ksj","isFragment : " + isFragment);
    }

    public void onBackPressed() {
    new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("아날로그 엔진 종료")
    .setMessage("아날로그 엔진을 종료하시겠습까?").setPositiveButton("확인", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    finish();
    }
    }).setNegativeButton("아니요",null).show();
    }
    }

    import 문제가 있는것 같은 추측은 됩니다만, 혼자서 해결이 잘 안되네요 ㅠㅠ 혹시 답변 가능하실까요?

  13. 답글을 늦게 달아드려 죄송합니다.
    그런데 이미 해결 방법을 찾으셨군요!

  14. Blog Icon
    송성철

    안녕하세요 위 질문드린 내용 알아보니까 CustomListFragment 클래스와 MainActivity 클래스에 import한 Fragment 버젼이 달라서 생긴 문제더라구요!

  15. 아마 android.app 패키지와 android.support.v4.app 패키지의 프래그먼트를 혼용하여 사용해서 생긴 문제였던 것 같네요.
    직접 문제 해결 방법을 찾으시고, 답글까지 남겨주셔서 감사합니다.

    직접 도움은 못 드렸지만, 다른 내용이 궁금하시면 언제든 질문글 남겨주세요.

    감사합니다.

  16. Blog Icon
    최진우

    아이템 추가를 위한 구현을 하였습니다.
    버튼을 이용해서 추가하려고 시도하였습니다.
    public void onPostButtonClicked(View v){
    String a= post1.getText().toString();
    String b= post2.getText().toString();
    String c= post3.getText().toString();
    Toast.makeText(getApplicationContext(),a+" "+b+" "+c,Toast.LENGTH_LONG).show();

    MainFragment mf = (MainFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_main);

    if (mf != null) {
    mf.addItemPetSitter(ContextCompat.getDrawable(this, R.drawable.alistar2),
    "가", "나", "다");

    }

    }
    이와 같이 null을 잡으면 문제없이 토스트로 가 나 다 까지는 출력이 되지만 아이템이 추가가 되지 않습니다. 무슨 문제일까요? 이와같은 현상은 onCreate에 넣어도 마찬가지였습니다.

  17. 올려주신 코드 만으로는 정확한 문제를 찾기가 쉽지 않을 것 같습니다.

    R.id.fragment_main 이 제대로 만들어져 있는지, 그리고 addItemPetSitter() 함수 이후의 코드가 제대로 작성되어 있는지 등에 대한 확인이 필요할 것 같네요.

    하지만 이미 문제가 뭔지 확인하고 해결 방법을 찾으셨겠죠?

    답글을 너무 늦게 달아드려 죄송합니다.

  18. Blog Icon
    트래져

    혹시 탭을 만들어서 프래그먼트를 뷰페이저로 나타낼때, MainActivity에서 프래그먼트접근을 어떻게 해야 될까요?

  19. Blog Icon
    트래져

    뷰 페이저 어댑터는
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentStatePagerAdapter;

    import com.treasure.appmanager.FragmentA;
    import com.treasure.appmanager.FragmentB;
    import com.treasure.appmanager.FragmentC;

    public class ContentsPagerAdapter extends FragmentStatePagerAdapter {
    private int mPageCount;

    public ContentsPagerAdapter (FragmentManager fm, int pageCount) {
    super(fm);
    this.mPageCount = pageCount;
    }

    @Override
    public Fragment getItem(int position) {

    switch(position) {
    case 0:
    FragmentA fragmentA= new FragmentA();
    return fragmentA;
    case 1:
    FragmentB fragmentB= new FragmentB();
    return fragmentB;
    case 2:
    FragmentC fragmentC= new FragmentC();
    return fragmentC;
    default:
    return null;
    }
    }

    @Override
    public int getCount() {
    return mPageCount;
    }
    }
    이렇게 썼고,
    MainActivity에서
    FragmentA mFrag = (FragmentA) mAdapter.getItem(tab.getPosition());
    mFrag.addItem(~);
    이렇게 접근하니까 java.lang.NullPointerException: Attempt to invoke virtual method 'void com.treasure.appmanager.ListViewAdapter.addItem(android.graphics.drawable.Drawable, java.lang.String, java.lang.String)' on a null object reference
    이렇게 뜨네요.

    참고로 뷰페이저 어댑터는
    final ContentsPagerAdapter mAdapter = new ContentsPagerAdapter(getSupportFragmentManager(), myTab.getTabCount());
    이렇게 선언했습니다.

  20. 정확히 어떤 용도로 프래그먼트에 접근하려는 건지 모르겠지만, 아래 코드에서 getItem()으로 프래그먼트 참조하는 방법은 의미가 없을 것 같습니다.

    getItem() 메서드에서는 Fragment 인스턴스를 new 키워드로 새로 만들기 때문에, 현재 화면에 보이는 프래그먼트와 getItem()으로 가져온 프래그먼트는 다른 인스턴스이기 때문입니다.

    원하시는 기능 구현을 위해서는, 어댑터에서 현재 생성된 프래그먼트 리스트를 별도로 관리하여, 인덱스에 따른 인스턴스 참조를 리턴하거나, tag 값과 findViewWithTag() 메서드를 조합하여 뷰페이저에서 child를 얻어오는 방법을 사용하면 될 것 같습니다.

    음.. 그런데 가장 깔끔한 방법은 메인액티비티에서 뷰페이저에 포함된 프래그먼트의 참조를 가져오지 않게 만드는 것이겠죠? ^^;;

    답변이 되었는지 모르겠네요.

    추가적으로 궁금한 사항 있으시면 질문글 남겨주세요.

    감사합니다.

  21. Blog Icon
    id001

    똑같이 구현했는데 getView()가 호출되지 않고 그래서인지 데이터 값이 보이지 않네요 ㅠㅠ
    무엇이 문제일까요?

  22. 음, 일단 다시 한번, 작성하신 코드와 본문의 예제 코드를 비교해 보시는 게 좋을 것 같습니다.

    아니면 프로젝트를 다시 만들어서, 본문에서 정리한 각 단계별로 코드를 한번 더 작성해 보세요.

    어떻게 구현하신 건지 제가 확인할 수가 없어서 어떻게 도움을 드려야할지 모르겠네요.

    확인해보시고, 잘 안되시면 다시 질문 글 남겨주세요.

    감사합니다.

  23. Blog Icon

    비밀댓글입니다

  24. 예제를 직접 만들어서 보내드리는 건 아닌 것 같구요.

    일단 먼저 질문을 드리고 싶은게, 각 프래그먼트에 있는 리스트뷰에 아이템을 추가하는 게 전혀 안된다고 하셨는데요.

    어떻게 안되죠? 에러가 발생하나요? 아니면 에러는 발생하지 않는데, 아무런 반응도 없는건가요?

    조금 더 구체적인 설명이 필요할 것 같습니다. "문제의 현상"과 "구현하신 방법"에 대해서요.

    아마도 페이지뷰의 각 페이지를 프래그먼트로 구성하고, 프래그먼트에 리스트뷰가 들어가는 구조인 것 같은데, 리스트뷰에 표시될 데이터 리스트는 어디서 관리하는지, 어떤 방식으로 접근했는지 등등.. 조금 더 자세하게.

    그리고 본문의 내용을 보시면 ListFragment는 "ListView의 Wrapper 역할을 하는 Fragment"라고 볼 수 있습니다. 기본적으로 하나의 ListView를 가지게 만들어져 있는데, 그 ListView를 ListFragment가 찾아서 접근할 수 있도록 id를 list로 고정시켜 사용해야 합니다.

    만약 프래그먼트에 두 개 이상의 리스트뷰를 넣고 싶다면, ListFragment를 사용하지 마시고, Fragment에 직접 ListView를 추가하시면 됩니다.

    블로그의 FRAGMENT와 LISTVIEW의 내용을 차근차근 살펴보시면 어렵지 않게 구현하실 수 있습니다. 절대 어려운 내용이 아니니, 차근차근 구현해보시길 바랍니다.

    그리고 조금 더 질문을 구체적으로 정리해서 올려주시길 바랄게요.

    감사합니다.

  25. Blog Icon
    질문있습니다.

    님 설명따라 HomeFragment에 customlistview추가 하는거 성공했습니다. 감사합니다.
    이후에 제가 HomeFragment에 toolbar를 추가할려고 하는데 문제가 생겨 질문드립니다.

    HomeFragment의 onCreateView안에 아래 코드를 추가시켜도 툴바가 생기지않습니다.

    View view = inflater.inflate(R.layout.fragment_home, container, false);
    Toolbar toolbar = view.findViewById(R.id.toolbar);
    toolbar.inflateMenu(R.menu._action_bar);
    view.setFocusableInTouchMode(true);
    ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);

    이유가 먼지 생각해보니깐 onCreateView에서
    view 대신에 super.onCreateView(inflater, container, savedInstanceState)를 반환해서 그러는거 같은데요..
    return view로 바꿔봐도 오류나고 listFragment파일 찾아도 어떻게 손댈지 모르겠네요ㅜㅜ
    도움주시면 정말 감사하겠습니다..

  26. 툴바를 하나 추가하신다고 하셨는데요.
    툴바를 어디에 표시하려고 하는건가요?

    HomeFragment에 추가하는 건가요?
    아니면 액티비티에 추가하는 건가요?

    그것을 먼저 명확히 하셔야 할 것 같습니다. 올려주신 코드를 보면 액티비티에 툴바를 표시하려고 하신 것 같거든요.

    확인해보시고, 다시 질문글 남겨주세요.

    감사합니다.

  27. Blog Icon
    질문있습니다.

    제가 작성자님께서 올려주신 예제를 잘못 이해하고 따라한거 같습니다.. 제대로 다시 따라해서 툴바 추가했어요! 감사합니다.

  28. 잘 해결하셨다니 다행이네요. ^^
    또 다시 궁금한 내용 있으면 질문글 남겨주세요.

    감사합니다.

  29. Blog Icon
    질문이요

    public void onListItemClick (ListView l, View v, int position, long id) {
    // get TextView's Text.
    ListViewItem item = (ListViewItem) l.getItemAtPosition(position) ;

    String titleStr = item.getTitle() ;
    String descStr = item.getDesc() ;
    Drawable iconDrawable = item.getIcon() ;
    String numStr = item.getNum();
    }

    이 이후에 하나를 클릭하면 프레그먼트로 넘어가게 하고 싶습니다. 제가 리스트를 3개를 만들었는데 각각 다른 프레그먼트를 불러와서 페이지 이동을 원하는데 이 이후에 알려주실 수 있을까요? ㅠㅠ
    FragmentManager fm = getFragmentManager();
    fm.beginTransaction().replace(R.id.nav_host_fragment, new noticeDetail1()).commit();

    예를 들어 이렇게 프레그먼트를 한 리스트에 하나씩 위치시켜 해당 리스트를 클릭할 시에 프레그먼트가 교체되게요!

  30. 작업의 핵심은 크게 두 가지로 나누어 볼 수 있는데요.

    첫 번째는 프래그먼트를 어디서 교체할 수 있는가.
    두 번째는 리스트뷰 아이템 클릭 이벤트를 프로그래먼트를 교체하는 곳에다 어떻게 알려줄 것인가.

    첫 번째 질문에 대한 답은, 프래그먼트를 제어할 수 있는 곳을 확인하면 해결됩니다. 만약 액티비티 위에 프래그먼트를 표시했다면, 액티비티에서 프래그먼트를 제어하면 되겠죠.

    그리고 두 번째 질문의 경우, 커스텀 리스너를 구현하거나 Handler를 통해 구현할 수 있습니다. 어쨌거나 핵심은, 리스트뷰 아이템 클릭 이벤트에 따른 프래그먼트 교체 메서드가 프래그먼트를 제어하는 곳(ex. 액티비티)에서 실행되게 만드는 것입니다.

    각각에 대한 내용은 아래 링크를 참고하세요.

    커스텀 리스너에 대한 내용.
    https://recipes4dev.tistory.com/168#6-리사이클러-외부액티비티-프래그먼트-에서-아이템-클릭-이벤트-처리하기

    핸들러에 대한 내용.
    https://recipes4dev.tistory.com/166

    추가적으로 궁금한 내용은 다시 질문글 남겨주세요.

    감사합니다.

  31. Blog Icon
    질문

    안녕하세요. 올려주시는 글들 감사히 잘 참고하고 있습니다.
    혹시 리스트의 위치를 지정할 수는 없을까요?

  32. 리스트의 위치를 어떻게 지정하고 싶은지 구체적으로 알려주실 수 있을까요?

    프래그먼트 안에 리스트뷰가 들어 있는 구조이기 때문에, 액티비티 레이아웃 XML 내에서 프래그먼트의 위치를 조절하면 리스트뷰도 해당 위치에 나오게 될텐데, 혹시 다른 것을 원하는 건지 궁금합니다.

    조금 더 구체적인 요구사항을 질문해주시면 도움드리기 수월할 것 같습니다.

    감사합니다.

  33. Blog Icon
    질문

    프레그먼트에 나타나는 레이아웃 내에 위치를 지정하고 싶습니다.
    한 프래그먼트 내에서 리스트 위에 버튼이나 텍스트 등도 표기할 수 있게요.

  34. 원하시는 것을 구현하려면, 기본적으로 제공되는 ListFragment의 기능을 커스터마이징해야 합니다.

    기본적으로 하나의 리스트뷰만 표시하도록 만들어진 ListFragment를 커스터마이징하려면, ListFragment을 상속받은 클래스의 onCreateView() 메서드에서 프래그먼트에 표시할 뷰를 리턴하면 됩니다.

    단, ListFragment는 내부 코드에서 ListView의 참조를 획득할 때 "R.id.list"라는 고정 ID를 사용하기 때문에, 프래그먼트에 표시할 뷰 레이아웃에도 ListView의 ID는 "@android:id/list"를 사용해야 합니다.

    음... 생각해보면 이런 경우 굳이 ListFragment를 써야 하나... 라는 생각이 드네요. 그냥 Fragment에 ListView를 넣는 게 더 나은 방법인 것 같습니다.

    답변이 되었는지 모르겠네요.
    추가적으로 궁금한 내용이 있으면 질문글 남겨주세요.

    감사합니다.

  35. Blog Icon
    질문

    Fragment에 ListView를 넣는 방식이 잘 되질 않아서 질문을 드렸던 건데, 천천히 다시 해보는 중 Fragment를 상속받아야 하는데 ListFragment를 상속하고 있었다는 것을 알게 되었습니다;; Fragment로 고치고 나니까 잘 되네요. 답변 감사합니다.

  36. 네. 해결하셨군요.
    또 다른 도움 필요하시면 언제든 질문글 남겨주세요.

    감사합니다.

  37. Blog Icon
    질문

    블로그 보면서 작성해본 코드입니다.
    구조는 뷰페이저 위에 ListFragment 덮은 상태입니다.
    다음 코드는 ListPageAdapter 어댑터 생성하고, Monday 클래스에 등록한겁니다. 뭐가 잘못된건지 보이는거 같은데 어떻게 해야할지 모르겠어서 질문남겨봅니다..


    adapterList = new ListPageAdapter();
    final Monday monday = (Monday) getSupportFragmentManager().findFragmentById(R.id.mon_fragment);
    if (monday != null) {
    monday.setListAdapter(adapterList);
    }
    addBtn = (Button) findViewById(R.id.addBtn);
    addBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    if (monday != null) {
    monday.addItem("zz","zz");
    adapterList.notifyDataSetChanged();
    }
    }
    });

  38. 올려주신 코드만으로는 어디가 문제인지 알 수 없네요.

    문제가 되는 상황에서의 에러 메시지를 같이 올려주시면 도움드릴 수 있을 것 같습니다.

    감사합니다.

  39. 좋은 강의 정말 감사합니다.
    질문이 하나 있습니다.

    CustomListFragment.java 부분의 코드중에
    adapter.addItem(ContextCompat.getDrawable(getActivity(), R.drawable.ic_account_box_black_36dp),
    "Box", "Account Box Black 36dp") ;

    라는 코드로 item 을 하나씩 추가하고 있는데

    R.drawable.ic_account_box_black_36dp 이 코드는 기존에 앱에 들어있는 이미지를 가져오는걸로 알고 있습니다.

    이렇게가 아닌 서버나 웹상에서 이미지를 가져오고 싶어서 glide 나 피카소 라이브러리를 쓰려고 하는데
    이렇게 할 경우에는 R.drawable.ic_account_box_black_36dp 대신에 어떻게 작성하면 좋을까요?

    어떻게 시도해보면 좋을지 조언 부탁드립니다 ㅜㅜ

  40. 흠.. 질문하신 내용은,
    제가 주절 주절 답글을 다는 것보다,
    github에 가셔서 샘플 코드를 보시는 게 더 나을 것 같네요.

    https://github.com/bumptech/glide

    일단 간단하게 예제 코드부터 확인해보시고, 차근차근 적용해보세요.

    감사합니다.

  41. Blog Icon
    도움

    안드로이드 개발을 시작하면서 많은 도움을 얻고 있습니다. 혹시 클래스로 만든 리스트에서는 같은 데이터가 있는지 확인하는 contains를 어떻게 사용해야 하는지 궁금합니다

  42. 죄송합니다. 제가 질문의 내용을 정확히 이해하지 못한 것 같아요.

    그냥 사용하시거나, 데이터 구조에 따라 구현하시면 될 것 같은데, 특별한 문제가 있으신건지...

    음.. 질문 내용 만으로는 어떤 문제가 있는지 잘 모르겠네요.

    조금 더 구체적인 내용을 올려주시면 도움드리기가 더 쉬울 것 같습니다.

    감사합니다.

  43. Blog Icon
    질문

    보고 잘 배우고 있는 학생입니다.

    ChatFrag chatFrag = (ChatFrag) getSupportFragmentManager().findFragmentById(R.id.list);
    chatFrag.addItem(ContextCompat.getDrawable(this, R.drawable.ic_profile),
    "me", "me me me me me me me") ;

    여기서 addItem에서 오류가 납니다. 오류 메세지는 Method invocation 'addItem' may produce 'NullPointerException' 입니다. addItem은 그대로 입니다. 왜 오류가 날까요?

  44. findFragmentById()를 통해 얻어온 Fragment 객체가 null이기 때문에 발생하는 문제인데요.

    먼저 R.id.list가 fragment의 id가 맞는지 다시 한번 확인해보세요. 작성하신 코드가 어떻게 되어 있는지 다 확인하지 못해서 확답은 드리기 어려우나, 코드에서 작성하신 fragment의 id가 R.id.list는 아닌 것으로 생각되네요.

    ChatFrag로 제어하려는 fragment의 id를 findFragmentById() 메서드에 사용하면 문제가 해결될 것으로 생각됩니다.

    감사합니다.