제목과 상세 내용으로 나뉘어진 Fragment 예제. (Titles and Details Fragment Example)

2016. 7. 9. 16:45


1. 다양한 화면 크기 및 화면 모드를 위한 Fragment

앞서 작성한 Fragment 관련 글과 예제를 통해 Fragment의 개념 및 기본 사용법, FragmentManager, ListView를 가지는 ListFragment로의 확장 등에 대해 살펴보았습니다. 또한 Fragment의 장점으로는 모듈 단위의 UI 구성, 이미 만들어 놓은 Fragment의 재사용, 다양한 화면 크기 및 화면 모드에서에서의 유연한 동작 등이 있다는 것도 설명하였습니다.


그런데 여러 장점들 중에서 "다양한 화면 크기 및 화면 모드에서의 유연한 동작"에 대한 내용은 충분히 설명되지 않은 것 같네요. "다양한 화면 크기 및 화면 모드에서의 유연한 동작"이라는 장점을 경험하기 위해서는 어떤 작업을 거쳐야 하는 걸까요?


자, 지금 부터 간단한 앱을 하나 만드는 것을 가정해 보겠습니다. 아주 간단합니다. 제목 리스트를 표시하고, 리스트의 제목 중 하나를 선택하면 그에 해당하는 상세내용을 표시하는 앱입니다.


앱이 모바일 폰에서 동작하는 모습을 구성해보자면, 아래와 같은 구성도를 가지게 될 것입니다.

제목과 상세 내용 프래그먼트

그런데 모바일 폰을 항상 세로 모드로만 사용하는 것은 아니죠. 만약 가로로 놓고 쓴다면 아래와 같이 표시될 것입니다.

제목과 상세 내용 프래그먼트


1.1 태블릿에서의 실행

이제 위에서 만든 앱을 모바일 폰이 아닌, 큰 화면을 가진 태블릿에서 실행해 보겠습니다.

제목과 상세 내용 프래그먼트 태블릿

모바일 폰에서 실행한 것에 비교하여 여백이 상당히 많이 보이는 것을 확인할 수 있습니다. 흠.. 뭐.. 괜찮네요. 이제 태블릿을 가로 모드로 바꿔 보겠습니다.

제목과 상세 내용 프래그먼트 태블릿


흠.. 당연히 세로 모드에 비해 좌우의 공간이 많이 비어 있는 것을 확인할 수 있습니다.


이 시점에서 한 가지 고민을 해봅시다. "저 넓은 태블릿 화면에서, 과연 제목과 상세내용을 따로 표시할 필요가 있을까? 모바일 폰은 화면 크기의 제약 때문에 어쩔 수 없다 하더라도 태블릿의 가로 모드에서는 제목과 상세내용을 한번에 표시하는 게 더 좋지 않을까?" 라고 말이죠. 바로 다음 화면에서 보여지는 내용 처럼 말입니다.

제목과 상세 내용 프래그먼트 사용 예제


2. 제목과 상세내용으로 나뉘어진 Fragment 예제

제목(Titles)과 상세내용(Details)로 나뉘어진 Fragment에 대한 예제는 구글에서 제공하는 안드로이드 예제 소스에 포함되어 있으며, 예제에 대한 설명은 "https://developer.android.com/guide/components/fragments.html?hl=ko#Example"에 잘 나와 있습니다. 이 글의 예제도 안드로이드에서 제공하는 예제를 바탕으로 작성되었기 때문에, 구글의 프래그먼트 예제에 대한 설명을 같이 보시면 훨씬 많은 도움이 될 것입니다.


그럼 지금부터 예제를 만들어 보도록 하겠습니다.

2.1 워크 플로우

제목과 상세 내용 프래그먼트 작성 절차


2.2 Activity에 Fragment 추가.

Activity에 Fragment를 표시하기 위해 Layout 리소스 XML 파일에 다음과 같은 내용을 추가합니다. "android:name"에는 화면에 출력될 Fragment 클래스를 지정하며, 예제의 클래스는 "2.4 Fragment 상속 및 구현" 단계에서 추가될 클래스입니다.


앱이 실행될 때 최초에는 제목 리스트만 표시될 것입니다. 그래서 아래의 Layout 리소스 XML 내용에는 TitlesFragment만 기술되어 있습니다.

[STEP-1.1] "content_main.xml" - MainActivity에 TitlesFragment 추가.
<?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.fragmenttitlesdetailsexample1.MainActivity"
    tools:showIn="@layout/activity_main">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/titles_fragment"
        android:name="com.recipes4dev.examples.fragmenttitlesdetailsexample1.TitlesFragment"
        tools:layout="@layout/activity_main" />

</RelativeLayout>

다른 예제들에선 하나의 Layout 리소스 XML만 작성하고 다음 단계로 넘어갔지만, 여기서는 추가적인 작업을 해줘야 합니다. 왜냐하면 "1.1 태블릿에서의 실행"에서 다양한 화면 크기 및 화면 모드에 대한 요구가 언급되었기 때문이죠. 이를 위해 "태블릿 화면 크기에서의 가로 모드"를 위한 Layout 리소스 XML을 별도의 파일로 정의해야 하며, 제목이 표시되는 Fragment와 상세내용이 표시되는 Fragment 모두 하나의 Layout 리소스 XML에 배치되어야 합니다.


태블릿(7인치 이상)에서의 가로 모드를 위한 Layout 리소스 XML을 추가하기 위해서는 "/res/layout-large-land/"에 XML 파일을 추가하면 됩니다.

    "/res/layout-large-land/content_main.xml" 파일 추가.

large layout 추가


추가된 파일은 "/res/layout" 아래에 다음과 같이 표시됩니다. "layout-"이라는 prefix는 생략되고 "large-land"만 남아 있는 것을 주의하세요.

    프로젝트 파일 리스트에서 "/res/layout-large-land/content_main.xml"의 표시 내용.

large-land 리소스


다양한 화면 크기를 지원하기 위한 방법에 대한 설명은 많은 내용이 포함되기 때문에, 별도의 주제로 다루겠습니다. 그리고 구글 Training 문서("https://developer.android.com/training/multiscreen/screensizes.html")에 친절하게 설명되어 있으므로 읽어보시길 바랍니다.

새로 추가한 "/res/layout-large-land/content_main.xml" 파일에 아래의 내용을 작성합니다.

[STEP-1.2] "/res/layout-large-land/content_main.xml" - TitlesFragment와 DetailsFragment를 위한 Layout 리소스 정의.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="horizontal"
    tools:context="com.recipes4dev.examples.fragmenttitlesdetailsexample1.MainActivity"
    tools:showIn="@layout/activity_main">

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/titles_fragment"
        android:name="com.recipes4dev.examples.fragmenttitlesdetailsexample1.TitlesFragment" />

    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:id="@+id/details_container" />

</LinearLayout>

2.3 Fragment를 위한 Layout 리소스 XML 정의

제목 리스트가 표시될 Fragment는 [ListView를 가지는 Fragment 만들기]에서 살펴본 ListFragment로부터 상속받아 사용하기 때문에 별도의 Layout 리소스 XML을 정의하지 않습니다. 하지만 상세내용이 표시되는 Fragment는 예제 구성도에 따라 두 개의 TextView를 가지는 Layout 리소스 XML을 정의해야 합니다.


상세내용을 위한 Fragment의 Layout 리소스 XML은 "/res/layout/details_fragment.xml"이라는 이름으로 추가하겠습니다.

[STEP-2] "/res/layout/details_fragment.xml" - DetailsFragment를 위한 Layout 리소스 XML.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/selectedTitle"
        android:textSize="32sp"
        android:background="#2ECC71"
        android:layout_weight="1"
        android:gravity="center"
        android:text="TextView for TITLE"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/detailsText"
        android:textSize="20sp"
        android:background="#F1C40F"
        android:layout_weight="9"
        android:text="TextView for DETAILS" />

</LinearLayout>

2.4 Fragment 상속 및 구현

앞선 단계들에서 Layout 리소스 XML에 대한 작성은 완료하였으므로 이제 각 Fragment들에 대한 클래스를 정의해야 합니다. 잠시 언급한대로 제목 리스트가 표시되는 Fragment는 ListFragment로부터 상속받습니다.

[STEP-3.1] "TitlesFragment.java" - TitlesFragment 추가.
public class TitlesFragment extends ListFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        {
            return super.onCreateView(inflater, container, savedInstanceState) ;
        }
    }
}

그리고 상세 내용이 표시되는 DetailsFragment는 Fragment로부터 상속받습니다.

[STEP-3.2] "DetailsFragment.java" - DetailsFragment 추가.
public class DetailsFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.details_fragment, container, false);
        super.onCreateView(inflater, container, savedInstanceState);

        // TODO : TextView에 제목 및 상세 내용 표시.

        return view ;
    }
}

DetailsFragment의 onCreateView() 함수의 내용을 보면 "R.layout.details_fragment"를 inflate한 다음, 리턴되는 view에 대한 참조를 따로 유지하고 있습니다. 이는 이후 과정에서 view를 사용하여 Fragment 내부의 TextView 참조를 획득한 다음, 제목 및 상세 내용을 표시하기 때문입니다.

2.5 DetailsActivity 구현

예제에서 7인치 이상의 태블릿 가로 모드에서는 두 종류의 Fragment를 나란히 표시하지만, 그 외의 실행환경에서는 상세 내용을 출력하기 위해 Activity를 사용하도록 구현하겠습니다. 이를 위해 DetailsActivity라는 이름으로 Activity를 추가합니다. (반드시 Activity를 사용해야만 하는 이유가 있는 것은 아닙니다. 단지, 예제 코드 작성의 편의성 및 다양성을 위해 Activity를 사용한 것일 뿐입니다.)

[STEP-4] "DetailsActivity.java" - DetailsActivity 추가.
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;

public class DetailsActivity extends AppCompatActivity {

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

        setContentView(R.layout.details_fragment);

        Intent intent = getIntent() ;

        TextView titleTextView = (TextView) findViewById(R.id.selectedTitle) ;
        titleTextView.setText(intent.getExtras().getString("title")) ;

        TextView detailsTextView = (TextView) findViewById(R.id.detailsText) ;
        detailsTextView.setText(intent.getExtras().getString("details")) ;
    }
}

새로운 DetailsActivity 클래스를 추가하긴 했지만 별도의 Layout 리소스 XML을 만들진 않았습니다. 대신, 이전 단계에서 만들어 놓은 "details_fragment.xml"을 setContentView() 함수에 지정하여 DetailsFragment의 Layout 리소스 XML을 재사용하고 있습니다. 또한 선택된 제목 및 상세 내용은 Intent로 전달받아 각 TextView에 표시하고 있습니다.

2.6 TitlesFragment의 ListView에 항목 추가

TitlesFragment의 내부에서 Adapter를 생성할 수도 있지만, 일단 제목 및 상세내용 정보가 Activity에서 관리되도록 만들겠습니다. 단, 예제 코드의 복잡성을 높이지 않기 위해 제목과 상세내용은 배열로 저장하고, 그 중 제목들만 Adapter에 추가하겠습니다.

[STEP-5] "MainActivity.java" - onCreate() 함수에서 TitlesFragment에 Adapter 적용.
public class MainActivity extends AppCompatActivity {}
    final String[][] contents = new String[3][2] ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 코드 계속 ...

        contents[0][0] = "TITLE-1" ;
        contents[0][1] = "This is Details of TITLE-1." ;
        contents[1][0] = "TITLE-2" ;
        contents[1][1] = "This is Details of TITLE-2." ;
        contents[2][0] = "TITLE-3" ;
        contents[2][1] = "This is Details of TITLE-3." ;

        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, new ArrayList()) ;
        adapter.add(contents[0][0]) ;   // add "TITLE-1"
        adapter.add(contents[1][0]) ;   // add "TITLE-2"
        adapter.add(contents[2][0]) ;   // add "TITLE-3"

        TitlesFragment titlesFragment = (TitlesFragment) getSupportFragmentManager().findFragmentById(R.id.titles_fragment);
        titlesFragment.setListAdapter(adapter) ;

        // ... 코드 계속
    }
}

2.7 Activity에서 Fragment 초기화

모바일 폰에서 앱이 처음 실행되면 Activity의 Layout 리소스 XML의 "titles_fragment"에 지정된 TitlesFragment가 표시되므로 별다른 초기화 작업이 필요하지 않습니다. 하지만 7인치 이상의 태블릿의 가로 모드에서 앱이 실행되면 TitlesFragment 뿐만 아니라, "details_container"에 DetailsFragment도 표시해야 하기 때문에 추가적인 초기화 과정이 수행되어야 합니다.


Fragment에 대한 초기화 과정은 [안드로이드 프래그먼트 기본 사용법]에서 설명했듯이 FragmentManager를 사용하여 수행할 수 있습니다.


단, "7인치 이상의 화면 크기에서 가로 모드"라는 조건인 경우에만 DetailsFragment의 초기화 과정을 실행하도록 코드를 작성합니다.

[STEP-6] "MainActivity.java" - onCreate() 함수에서 DetailsFragment 초기화.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // 코드 계속 ...

        if (getResources().getConfiguration().isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE) &&
                getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
            DetailsFragment fr = new DetailsFragment();
            Bundle args = new Bundle() ;

            args.putString("title", contents[0][0]);
            args.putString("details", contents[0][1]);
            fr.setArguments(args) ;

            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fm.beginTransaction();
            fragmentTransaction.replace(R.id.details_container, fr);
            fragmentTransaction.commit();
        }
    }
}

현재 기기 상태에 대한 정보를 확인하기 위해 getResource().getConfiguration() 함수를 사용하였습니다. 여기서 리턴되는 Configuration 클래스에는 현재 기기의 모든 설정 정보가 저장되어 있으며, 멤버 함수 또는 변수를 통해 설정 정보를 조회할 수 있습니다.


Configuration.isLayoutSizeAtLeast() 함수는 현재 기기가 제공하는 최소 화면 크기를 검사할 때 사용될 수 있습니다. 그리고 Configuration.orientation 변수는 세로 모드(PORTRAIT)인지 가로 모드(LANDSCAPE)인지 정보를 가지고 있습니다.


예제의 if 조건에 기술된 내용은 기기가 최소 7인치 이상(SCREENLAYOUT_SIZE_LARGE)이고, 가로 모드(ORIENTATION_LANDSCAPE)인지 검사하는 코드입니다. 만약 그렇다면 앱이 실행됨과 동시에 DetailsFragment의 인스턴스를 생성하여 화면(details_container)에 표시합니다.

2.8 TitlesFragment의 제목 클릭 시, DetailsFragment 변경.

이제 예제 작성의 마지막 단계로 TitlesFragment의 ListView에 표시된 제목을 선택하면, 선택된 제목에 대한 상세 내용이 DetailsFragment에 표시되도록 만들겠습니다. 이 때, ListView의 제목을 선택했을 때의 이벤트 처리는 TitlesFragment에서 onListItemClick() 함수를 override하여 처리합니다.

[STEP-7.1] "TitlesFragment.java" - onListItemClick() 함수 override.
public class TitlesFragment extends ListFragment {

    // 코드 계속 ...

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails(position) ;
    }

    // ... 코드 계속.
}

다음으로 선택된 제목에 해당하는 상세 내용을 표시하기 위해 MainActivity에서 position을 전달받아 DetailsFragment를 표시하는 과정을 구현하겠습니다. 이를 위해 TitlesFragment에 이벤트 콜백을 위한 인터페이스를 정의한 다음, MainActivity에서 해당 인터페이스를 구현해야 합니다. 이는 Fragment에서 Activity로 이벤트를 전달하는 일반적인 방법입니다.


먼저 TitlesFragment에 이벤트를 위한 인터페이스를 정의합니다.

[STEP-7.2] "TitlesFragment.java" - 이벤트 콜백 인터페이스 정의
public class TitlesFragment extends ListFragment {

    public interface OnTitleSelectedListener {
        public void onTitleSelected(int position) ;
    }

    OnTitleSelectedListener titleSelectedListener;

    // 코드 계속 ...

    // onListItemClick() 함수에서 호출하는 showDetails() 함수.
    public void showDetails(int position) {
        titleSelectedListener.onTitleSelected(position) ;
    }

    // 코드 계속 ...
}

다음 TitlesFragment가 MainActivity에 Attach될 때, MainActivity에 구현된 인터페이스 리스너를 참조할 수 있도록 만듭니다.

[STEP-7.3] "TitlesFragment.java" - TitlesFragment의 onAttach()에서 콜백 참조 저장.
public class TitlesFragment extends ListFragment {

    // 코드 계속 ...

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            if (context instanceof Activity) {
                titleSelectedListener = (OnTitleSelectedListener) context;
            }
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnTitleSelectedListener");
        }
    }

    // ... 코드 계속
}

이제 MainActivity에서 TitlesFragment의 OnTitleSelectedListener를 implements합니다. 또한 onTitleSelected() 함수에서 DetailsFragment의 내용을 표시합니다. 이 때 화면 크기 및 모드 조건에 따라 Activity를 실행할 지, Fragment를 만들지 결정합니다.

[STEP-7.4] "MainActivity.java" - 이벤트 콜백 인터페이스 구현.
public class MainActivity extends AppCompatActivity implements TitlesFragment.OnTitleSelectedListener {

    // 코드 계속 ...

    @Override
    public void onTitleSelected(int position)
    {
        if (getResources().getConfiguration().isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE) &&
                getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            DetailsFragment fr = new DetailsFragment();
            Bundle args = new Bundle() ;

            args.putString("title", contents[position][0]);
            args.putString("details", contents[position][1]);
            fr.setArguments(args) ;

            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fm.beginTransaction();
            fragmentTransaction.replace(R.id.details_container, fr);
            fragmentTransaction.commit();
        } else {
            Intent intent = new Intent();
            intent.setClass(this, DetailsActivity.class);

            intent.putExtra("title", contents[position][0]);
            intent.putExtra("details", contents[position][1]);

            startActivity(intent);
        }
    }

    // ... 코드 계속
}

위의 예제 코드를 보면 Fragment에 데이터를 전달할 때 Bundle의 인스턴스를 생성하여 데이터를 채운 다음, setArguments() 함수를 통해 인스턴스를 전달하는 것을 확인할 수 있습니다. Intent를 사용하여 Activity에 데이터를 전달하는 방법과 비슷하죠. Fragment의 setArguments() 함수로 전달한 데이터는 getArguments() 함수를 사용하여 가져올 수 있습니다.


아래 코드는 DetailsFragment 내에서 getArguments() 함수를 사용하여 TextView에 데이터를 표시하는 코드입니다.

[STEP-7.5] "DetailsFragment.java" - DetailsFragment에서 제목 및 상세 내용 표시.
public class DetailsFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.details_fragment, container, false);
        super.onCreateView(inflater, container, savedInstanceState);

        Bundle args = getArguments() ;

        TextView titleTextView = (TextView) view.findViewById(R.id.selectedTitle) ;
        titleTextView.setText(args.getString("title")) ;

        TextView detailsTextView = (TextView) view.findViewById(R.id.detailsText) ;
        detailsTextView.setText(args.getString("details")) ;

        return view ;
    }
}

3. Fragment 예제 실행 화면

아래는 모바일 폰(갤럭시 노트 2)에서 세로 모드로 실행한 화면입니다.

제목과 상세 내용 프래그먼트 예제 실행 화면


다음은 모바일 폰(갤럭시 노트 2)에서 가로 모드로 실행한 화면입니다.

제목과 상세 내용 프래그먼트 예제 실행 화면


이제 7인치 크기를 가진 넥서스 7 2세대에서의 실행 결과를 보겠습니다. 세로 모드에서의 실행화면입니다.

제목과 상세 내용 프래그먼트 예제 실행 화면


마지막으로 7인치 크기의 넥서스 7 2세대에서 가로 모드로 실행한 화면입니다. TitlesFragment와 DetailsFragment가 한 화면에 동시에 표시되는 것을 확인할 수 있습니다.

제목과 상세 내용 프래그먼트 예제 실행 화면


4. 참고.

.END.


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

  1. Blog Icon
    Exist

    [STEP-7.1]

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
    showDetails(position);
    }

    이부분에서 showDetails(position);는 어디죠??

    디버깅해서 화면에 Title리스트를 클릭하니 onListItemClick 이쪽으로 타기는 하는데

    이게 onTitleseected로 가서 MainActivity에 interface 구현부분으로 가도록 설계하신거죠??

    그런데 onListitemClick에서 어떻게 넘어가야하나요?? ㅠㅠ

  2. 예제를 작성하고 글을 올리면서 showDetails() 함수의 body가 누락되었네요. ㅜ_ㅜ.

    showDetails() 함수의 내용은 [STEP-7.2]에 추가해놓았습니다.
    예제 작성과 실행에 혼란을 드려 죄송합니다.

  3. Blog Icon
    Exist

    빠른답변 감사드려요 ^^

  4. 좀 더 빨리 답변을 드려야 하는데...
    회사일이 바빠서 쉽지가 않네요. ^^;
    다른 질문 사항 있으면 댓글 남겨주세요.
    감사합니다.

  5. Blog Icon
    user1

    정말 많은 도움이 되고 있습니다.
    감사합니다.

  6. 아직 내용이 많이 부족하여, 더 많은 도움 못 드려서 죄송하네요.
    방문해 주셔서 감사합니다.

  7. Blog Icon
    1

    이클립스로 따라해보고 있는데
    TitlesFragment titlesFragment = (TitlesFragment) getSupportFragmentManager().findFragmentById(R.id.titles_fragment); 라인에서 casting 할 수 없다고 나온에요 ㅠㅠㅠ 왜그럴까요 ㅠㅠ

  8. casting 할 수 없다고 나오는 에러 메시지를 그대로 복사해서 올려주시면 문제 해결 방법을 좀 더 빨리 찾아볼 수 있을 것 같습니다.

    음, 먼저 아래의 내용을 한번 체크해보세요.

    1. TitlesFragment를 ListFragment로부터 제대로 상속하였는지.
    public class TitlesFragment extends ListFragment

    2. android.app.ListFragment 패키지와 android.support.v4.app.ListFragment 패키지를 혼용하여 쓰진 않았는지.
    import android.support.v4.app.ListFragment;
    또는
    import android.app.ListFragment;

    위의 내용을 확인해봐도 문제점의 원인을 못 찾으면, 에러 메시지를 그대로 복사하여 질문글에 올려주세요.

    감사합니다.

  9. Blog Icon
    goopy

    정말 좋은 정보 감사히 잘보고 배우고 있습니다.
    가독성도 좋고 이해도 잘되고 예제도 다양하고 자세한 설명까지 해주시고 꽤 많은 노력이 들어간것 같아서 새삼 놀랍습니다.
    앞으로도 좋은 정보 잘 부탁드립니다. 노고에 감사합니다.

  10. 칭찬글 남겨주셔서 감사드립니다.
    나름 시간과 노력을 들여서 좋을 글을 쓰기 위해 노력하고는 있지만, 뒤돌아보면 늘 부족한 것들 뿐이네요.
    앞으로 좀 더 좋은 내용들 담을 수 있도록 노력하겠습니다.

    감사합니다.

  11. Blog Icon
    학습자

    안녕하세요 알려주신 부분 감사히 잘 보고 배우고 있습니다!!근데 저 예제를 구글에서 어떻게 설명하는지 찾아보려고 링크를 타고 들어갔는데 어떤 이름으로 검색을 해야하는 지 모르겠네요. 혹시 어떤 이름으로 검색해야 저 예제를 확인 할 수 있을 지 알 수 있을까요?

  12. 먼저, 본문에 달려 있는 링크로 이동해보면 프래그먼트에 대한 전체적인 설명이 있는 페이지로 이동합니다.

    https://developer.android.com/guide/components/fragments.html?hl=ko#Example%22에

    해당 페이지의 전체 내용을 이해할 수 있다면, 프래그먼트에 대한 내용을 완벽히 파악했다고 할 수 있죠.

    그리고 해당 페이지의 오른쪽 "참고 항목"을 보면 "프래그먼트로 동적 UI 구축" 이라는 항목이 있는데요. 이 페이지에서 프래그먼트롤 사용하여 다이나믹한 UI를 구성하는 방법에 대해 학습할 수 있습니다.

    https://developer.android.com/training/basics/fragments/index.html?hl=ko

    그리고 해당 페이지의 오른쪽, "DOWNLOAD THE SAMPLE"을 선택하면 zip 파일로 압축된 샘플 코드를 다운로드 할 수 있습니다.

    http://developer.android.com/shareables/training/FragmentBasics.zip?hl=ko

    찾으시는 내용이 맞는지 모르겠네요.
    추가적인 질문 사항이 있으면 다시 글 남겨주세요.

    감사합니다.

  13. Blog Icon
    go 2 sun

    안녕하세요. 전체 빌드는 성공했는데 Run apk 실행시에 아래의 에러가 발생하네요.
    stackover 를 찾아봐도 dependencies 는 괜찮은거 같은데요. 혹시 해결책 아실까요?

    # Error
    Program type already present: android.support.v4.app.BackStackRecord$Op
    Message{kind=ERROR, text=Program type already present: android.support.v4.app.BackStackRecord$Op, sources=[Unknown source file], tool name=Optional.of(D8)}

    # app/build.gradle
    dependencies {
    //implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:24.1.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    }

  14. 보통 안드로이드에서 Support Library는 프로젝트 내에서 같은 버전으로 사용하는 게 맞습니다.
    주로 Support Library는 v7 appcompat 패키지와 Design 패키지를 사용하게 되는데, Dependencies에서 같은 버전으로 사용하면 되는거죠.

  15. Blog Icon
    go 2 sun

    새로 프로젝트를 만들면서 베이직 액티비티 선택해서 dependencies 를 동일하게 하니
    또 같은 에러가 나네요.
    Program type already present: android.support.v4.app.BackStackRecord$Op

    그래서 아래와 같이 두 버전을 동일하게 맞추니 빌드 성공! 했어요.
    implementation 'com.android.support:appcompat-v7:27.0.1'
    implementation 'com.android.support:design:27.0.1'

  16. Support Library에서 문제가 발생되면, 일단 버전부터 확인해보는 게 좋습니다.
    스스로 찾으신 답글에 있는 것처럼 버전만 같게 맞춰줘도 해결이 되는 경우가 많습니다.

    답글 늦게 달아서 죄송하고, 먼저 해결책을 찾으셔서 다행이네요.

    감사합니다.

  17. 안녕하세요. 항상 블로그 보면서 도움 많이 받고 있습니다!
    DetailsActivity에서 반드시 Activity를 사용하지 않아도 된다 했는데,
    글 읽어보면서 제가 이해한 바로는
    DetailsActivity.java는 세로 화면일 때 detail.fragmnet.xml을 보여주고,
    DetailsFragment.java는 가로 화면일 때 detail.fragment.xml을 보여주는 것이라 이해했습니다.
    그러면 DetailsFragment.java에서 조건문으로 인텐트 조건을 걸어서 사용하면 DetailsActivity.java를 사용하지 않아도 될거라 생각됩니다.
    제가 말한 의미에서 굳이 DetailsActivity.java를 사용하지 않아도 된다고 하신건가요??

  18. 음.. 너무 복잡하게 생각하실 건 아니고,
    그냥 예제에서 Activity를 사용해서 만들었으나, 굳이 Activity를 만들지 않고 다른 방법을 사용해도 된다는 의미였습니다.

    초보자분들이 블로그의 글을 읽을 때 예제의 내용을 보시고나서, "반드시 예제대로" 만들어야 한다고 생각하시는 경우가 많아서 부연 설명을 적어놓은 것입니다.

    감사합니다.