안드로이드 3.0이상일경우에만

listview 안에 getview에서

android.R.layout.simple_list_item_activated_1 로 체크해주고

getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  
  getListView().setItemChecked(pos, true);

해주면된다.

만약 customView 일경우

해당 view에  android:background="?android:attr/activatedBackgroundIndicator" 해주면 체크는 가능해진다.

그리고 만약 listview에 높이 맞게 textview 구성을 원할시

 android:minHeight="?android:attr/listPreferredItemHeight"

해주면 된다.

출처 : http://fupeg.blogspot.com/2011/03/honeycomb-drag-and-drop-basics.html

If you are thinking about developing an Android tablet app, one of the most exciting things has got to be the drag and drop capabilities that were added to Android as part of Honeycomb. No wait a minute, aren't there a lot more interesting features than drag and drop in Honeycomb? There are definitely a lot of great features, but drag and drop is one of the most interesting in my opinion. It's a perfect fit for tablets, where you can have this nice big graphical objects to drag and drop. I think it really challenges mobile designers and developers to rethink our interaction models. If you read through that tutorial I just linked to, you will learn about many of the intricacies of drag and drop Android style. I thought I'd provide an even simpler example for those of you who prefer the Cliff Notes versions of things.

First we will start with a simple UI, a 2x2 grid with a cute little image that we will drag and drop around this grid. Here is what it looks like:

DnD UI

I used a simple TableLayout for this, along with some basic border background Drawables. Here's the XML for it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<TableLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <TableRow>
    <LinearLayout android:layout_width="640px"
        android:layout_height="345px"
        android:id="@+id/topLeft"
        android:background="@drawable/border">
        <ImageView android:id="@+id/droid"
            android:src="@drawable/bugdroid"
            android:layout_width="150px"
            android:layout_height="150px"
         />
    </LinearLayout>
    <LinearLayout android:layout_width="640px"
        android:layout_height="345px"
        android:id="@+id/topRight"
        android:background="@drawable/border2"
    />    
  </TableRow>
  <TableRow>
    <LinearLayout android:layout_width="640px"
        android:layout_height="345px"
        android:id="@+id/bottomLeft"
        android:background="@drawable/border2"
    />
    <LinearLayout android:layout_width="640px"
        android:layout_height="345px"
        android:id="@+id/bottomRight"
        android:background="@drawable/border"
    />    
  </TableRow>  
</TableLayout>

Nothing special so far. All of the magic happens in the code. Here's the basics of the Activity that uses this XML.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DndActivity extends Activity {
  
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.grid);
  View droid = findViewById(R.id.droid);
  droid.setOnLongClickListener(new OnLongClickListener(){
   @Override
   public boolean onLongClick(View v) {
    ClipData data = ClipData.newPlainText("foo","bar");
    DragShadowBuilder shadowBuilder = new DragShadowBuilder();
    v.startDrag(data, shadowBuilder, v, 0);
    return true;
   }
  });
  findViewById(R.id.topLeft).setOnDragListener(new BoxDragListener());
  findViewById(R.id.bottomLeft).setOnDragListener(new BoxDragListener());
  findViewById(R.id.topRight).setOnDragListener(new BoxDragListener());
  findViewById(R.id.bottomRight).setOnDragListener(new BoxDragListener());
 }
}

Here we just set the contentView, then we get a handle on the droid image. We set its onLongClickListener. I think the long click will become the de facto way to initiate drag and drop in Android apps. You could use some other event of course, but long click feels right. The key thing we need to do to initiate drag and drop is invoke the startDrag method. To do that, we have to create a ClipData object and DragShadowBuilder. You can use the ClipData to put all kinds of interesting data/metadata that will be passed around to the drop zones (Views) in your app. In this case I didn't need anything fancy, so I put something minimal and arbitrary. For the DragShadowBuilder, you might want to do something fancy to make the dragging look cool. I used a default one and it doesn't do much at all. You also need to set a "localData" object. This can be anything. Since my local data is the image itself, I chose to pass the ImageView that will be passed in to the onLongClick method. Now you can call startDrag. Finally, make sure you return true to let the OS know that drag and drop is a go.

Next in the code you will see that for each of the cells in my table, I have set an OnDragListener object. I've set this to a new instance of BoxDragListener for each of the cells. This is an inner class of DndActivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class BoxDragListener implements OnDragListener{
 boolean insideOfMe = false;
 Drawable border = null;
 Drawable redBorder = getResources().getDrawable(R.drawable.border3);
 @Override
 public boolean onDrag(View self, DragEvent event) {
  if (event.getAction() == DragEvent.ACTION_DRAG_STARTED){
   border = self.getBackground();
   self.setBackgroundDrawable(redBorder);
  } else if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED){ 
   insideOfMe = true;
  } else if (event.getAction() == DragEvent.ACTION_DRAG_EXITED){
   insideOfMe = false;
  } else if (event.getAction() == DragEvent.ACTION_DROP){
   if (insideOfMe){
    View view = (View) event.getLocalState();
    ViewGroup owner = (ViewGroup) view.getParent();
    owner.removeView(view);
    LinearLayout container = (LinearLayout) self;
    container.addView(view);
   }
  } else if (event.getAction() == DragEvent.ACTION_DRAG_ENDED){
   self.setBackgroundDrawable(border);     
  }
  return true;
 }
}

This guy will get a stream of DragEvents fired at its onDrag method. You can determine what kind of DragEvent it is by calling getAction on the event. The first one you will get is an ACTION_DRAG_STARTED event. For this we want to change the border of our cell to red to let the user know that they can drop the image in this cell. Here's what that looks like:

Enter the red zone

Next we want to keep track if the user's finger has entered into a drop zone. So we look for the ACTION_DRAG_ENTERED and toggle a local boolean. Similarly, we look for ACTION_DRAG_EXITED in case the user changed their mind and picked a different zone to drop the image into. Next we look for the ACTION_DROP. If the object is inside our drop zone, then we update the UI by removing it from its previous parent and adding it to the drop zone. Finally, we listen for ACTION_DRAG_ENDED to know when the drag and dropping is finished. We then restore the borders to their original color. That's it!

Update: The code above will not draw a shadow when you drag the image around the screen. There is a very easy way to get a shadow and that is to pass the View to it the DragShadowBuilder constructor, i.e. new DragShadowBuilder(v); in the above code. That will cause the image in the sample app to be used as the shadow. Be careful using this technique though. If the View that you are dragging is big/complex, then you might take a performance hit (UI becomes sluggish.) In that case you might want to subclass DragShadowBuilder and override its onDragShadow method to draw something simpler for the shadow. I've been playing around with this and StackViews however, and it has worked great with no need to subclass DragShadowBuilder. Be sure to set the android:hardwareAccelerated="true" attribute in your AndroidManifest.xml either on the Activity or the whole Application


출처 : http://androidhuman.tistory.com/entry/액션바의-변신은-무죄-다양한-액션바의-모습들

이전 글 (
2011/05/30 - 액션바(Action bar) 따라잡기 - 액션바가 뭔가요?) 에서 알아보았던 것처럼, 액션바는 메뉴를 대신하는 것 외에도 다양한 역할을 수행할 수 있습니다. 그 중에서 대표적인 기능이 사용자가 애플리케이션 내 흐름을 잘 알 수 있도록 도와주는 것입니다.

애플리케이션 흐름을 알려주는 액션바의 요소들


애플리케이션을 사용하다 보면 화면이 전환되어 홈 화면보다 하위 단계의 화면에 오게 되는 경우가 많은데, 간혹 내가 보고 있는 화면이 홈 화면인지, 하위 단계의 화면인지 혼동되는 경우가 있습니다.

액션바는 위와 같은 사용자들의 혼동을 방지하기 위해, 하위 단계 화면일 경우 이를 사용자가 인지할 수 있도록 액션바 좌측 아이콘에 '업 네비게이션(Up navigation)'이라 하는 작은 삼각형을 표시하도록 제공하고 있습니다. 다음 화면은 일반적인 상태와 업 네비게이션으로 표시될 때의 액션바 아이콘을 보여줍니다.


일반 상태일 때와 업 네비게이션 상태의 액션바 아이콘



액션바 아이콘을 업 네비게이션 형태로 표시하려면 setDisplayHomeAsUpEnabled() 메서드를 사용합니다. 액션바의 인스턴스는 액티비티의 getActionBar() 메서드로 얻을 수 있습니다.

API
public abstract void setDisplayHomeAsUpEnabled (boolean showHomeAsUp)

액션바 아이콘을 업 네비게이션 형태로 표시합니다.



이 뿐만 아니라, 액션바에 탭을 추가하여 여러 화면을 더 깔끔하게 구성하는 것도 가능합니다. 다음은 액션바에 탭이 추가된 모습을 보여줍니다.

액션바에서 탭을 표시하는 모습



액션바에 탭을 추가하려면 액션바의 네비게이션 모드를 탭 모드로 설정한 후, 탭을 추가하면 됩니다.

API
public abstract void setNavigationMode(int mode)

액션바의 네비게이션 모드를 설정합니다.


액션바의 네비게이션 모드는 총 세가지로, 기본 모드인 ActionBar.NAVIGATION_MODE_STANDARD, 탭을 표시하는 모드인 ActionBar.NAVIGATION_MODE_TABS, 리스트를 표시하는 모드인 ActionBar.NAVIGATION_MODE_LIST가 있습니다.

API
public abstract void addTab(ActionBar.Tab)

액션바에 탭을 추가합니다.


Tab 인스턴스는 ActionBar.newTab() 메서드를 통해 얻을 수 있으며, 탭을 추가한 후에는 필히 ActionBar.Tab.setTabListener() 메서드를 사용하여 각 탭이 선택되었을 때 수행할 동작을 처리할 TabListener를 등록해야 합니다.

액션바 모습 변경하기


액션바는 기본적으로 애플리케이션 아이콘과 제목이 표시됩니다. 하지만 원한다면 이를 표시되지 않도록 할 수도 있습니다. 또한, 애플리케이션 아이콘 대신 액션바에 최적화된 그림인 '로고'를 표시하도록 하는 것도 가능합니다.

로고는 매니페스트에서 지정할 수 있으며, 로고를 지정하면 애플리케이션 아이콘 대신 로고가 기본적으로 액션바에 표시됩니다. 로고를 표시하거나 표시하지 않도록 하려면 ActionBar.setDisplayUseLogoEnabled() 메서드를 사용합니다.

API
public abstract void setDisplayUseLogoEnabled(boolean enabled)

액션바의 로고 표시유무를 설정합니다.


로고와 마찬가지로 액션바에 표시되는 제목도 ActionBar.setDisplayShowTItleEnabled() 메서드를 사용하여 쉽게 표시 여부를 설정할 수 있습니다.

API
public abstract void setDisplayShowTitleEnabled(boolean enabled)

액션바에 표시되는 제목의 표시유무를 설정합니다.

출처 : http://androidhuman.tistory.com/472

안드로이드 3.0, 허니컴(Honeycomb)에서는 태블릿 단말에 최적화된 컴포넌트들이 많이 추가되었는데요, 프래그먼트(Fragment)와 함께 허니컴에서 추가된 대표적인 UI 컴포넌트 중 하나가 바로 액션바(Action bar)입니다.

액션바는 기존 안드로이드 애플리케이션의 타이틀바(Title bar)를 대체하는 컴포넌트로, 기존 타이틀바가 단순히 제목만을 표시하거나 간단한 정보만을 표시할 수 있던 것에 반해 액션바는 제목 표시 뿐만 아니라 메뉴 제공, 액션 아이템(Action item)을 통한 단축 메뉴 제공, 탭 지원 등 다양한 기능을 포함하고 있습니다. 다음은 전형적인 액션바의 모습을 보여줍니다.

액션바(Action bar)의 모습



액션바를 표시하려면?


안드로이드 3.0 이하 버전으로 작성된 애플리케이션 (매니페스트에 Min SDK Version / Target SDK Version이 11 이하인 경우)을 안드로이드 3.0이 탑재된 단말기에서 실행하면 액션바 대신 기존의 타이틀바가 그대로 표시됩니다. 때문에, 기존 플랫폼 버전을 지원함과 동시에 액션바도 표시되도록 하려면 매니페스트의 Target SDK Version을 11 이상으로 수정해야 합니다.

기존 버전과 3.0버전 이상을 동시에 지원하기 위한 매니페스트 설정



액션바와 메뉴(OptionsMenu), 그리고 액션 아이템(Action item)


액션바와 메뉴는 뗼레야 뗄 수 없는 관계입니다. 안드로이드 3.0 이상이 탑재된 단말기를 처음 접하게 되면 메뉴키가 없어 당황하는 분들이 꽤 있습니다. 물론 기존 플랫폼 버전을 타깃으로 작성된 애플리케이션을 실행했을 때는 메뉴를 호출할 수 있도록 하단에 메뉴키가 생기지만, 안드로이드 3.0을 타깃으로 작성된 애플리케이션에서는 메뉴키를 찾을 수 없을 것입니다.


기존 버전을 타깃으로 작성된 애플리케이션을 안드로이드 3.0 이상에서 실행했을 때 메뉴 표시모습



그렇다면, 메뉴는 과연 어디에 표시되는 것일까요? 답은 의외로 가까운 곳에 있습니다. 바로 액션바입니다. 액션바는 여러 역할을 하지만, 가장 주된 역할은 메뉴를 표시 기능입니다. 기존 버전을 타깃으로 작성된 애플리케이션의 타깃을 안드로이드 3.0 이상으로 변경한 후 다시 실행해보면 다음과 같이 액션바 우측 구석에 메뉴 버튼이 표시되는 것을 확인할 수 있습니다.



안드로이드 3.0부터는 메뉴가 액션바의 오버플로우 메뉴(눌렀을 때 리스트로 나타나는 메뉴)에 표시되며, 메뉴 항목 중 빈번한 사용이 예상되는 항목은 액션바에 바로 항목이 노출되도록 할 수 있습니다. 이렇게 오버플로우 메뉴가 아닌 액션바에 바로 노출되는 항목을 액션 아이템(Action item)이라 합니다.

일반 메뉴 항목을 액션 아이템으로 표시되도록 하는 방법은 크게 두 가지입니다. 하나는 메뉴 XML을 사용하는 방법, 다른 하나는 코드로 구현하는 방법입니다. 두 가지 방법 모두 동일한 결과를 내는 데는 문제가 없지만, 코드를 통해 구현하는 경우 SDK 버전에 맞추어 별도로 코드를 작성해야 하므로 XML을 통해 설정하는 방법을 권장합니다.

XML에서 메뉴 항목이 액션 아이템으로 표시되도록 설정하려면 다음과 같이 android:showAsAction 속성을 지정하면 됩니다. 다음 예제는 메뉴 항목에 대해 공간이 있을 경우 메뉴 항목의 텍스트를 동시에 표시하도록 구현하고 있습니다.

1.<?xml version="1.0" encoding="utf-8"?>
3.    <item android:id="@+id/menu_add"
4.          android:icon="@drawable/ic_menu_save"
5.          android:title="@string/menu_save"
6.          android:showAsAction="ifRoom|withText" />
7.</menu>

android:showAsAction 속성으로 지정할 수 있는 항목은 다음과 같습니다.

  •  ifRoom : 액션 아이템을 표시할 수 있는 공간이 있다면 액션 아이템을 표시합니다. 
  •  never : 항상 액션 아이템으로 표시하지 않습니다. (기본값)
  •  withText : 메뉴 항목의 아이콘과 메뉴 항목의 텍스트를 함께 액션 아이템으로 표시합니다.
  •  always : 항상 액션 아이템으로 표시합니다.

메뉴 항목이나 액션 항목을 선택했을 때 수행할 작업 구현은 기존과 동일하게 onOptionsItemSelected() 메서드에서 처리합니다. 안드로이드 3.0부터는 액션바 좌측에 애플리케이션 아이콘이 기본적으로 표시되는데, 이 아이콘을 눌렀을 때 처리할 동작 또한 동일한 메서드에서 처리할 수 있습니다. 애플리케이션 아이콘의 id는 android.R.id.home으로, onOptionsItemSelected()에서 이 id를 사용하여 적절한 작업을 수행하도록 구현할 수 있습니다.


액션바 감 잡아보기


지금까지 액션바에 대해 간단히 알아보았으니 예제를 통해 확실히 사용법을 알아보도록 하겠습니다.

[어플리케이션 정보]

액티비티
  • Main (Main.java)

메뉴 리소스
  • menu.xml

API Level
  • Android 3.0 (API Level 11)



프로젝트를 생성한 후, 다음과 같이 리소스 추가 마법사를 사용하여 메뉴 리소스를 추가합니다.

메뉴 리소스를 추가합니다.


다음, 메뉴 항목을 추가합니다. 예제에서는 총 3개의 메뉴 항목을 추가합니다. 다음과 같이 메뉴 리소스를 작성합니다.

[menu.xml]
01.<?xml version="1.0" encoding="utf-8"?>
03.    <item
04.        android:showAsAction="ifRoom|withText"
05.        android:id="@+id/item1"
06.        android:icon="@android:drawable/ic_menu_add"
07.        android:title="Add"
08.    />
09.    <item
10.        android:id="@+id/item2"
11.        android:showAsAction="always"
12.        android:title="Action item with icon"
13.        android:icon="@android:drawable/ic_menu_search"
14.    />
15.    <item
16.        android:title="Normal item"
17.        android:id="@+id/item3"
18.    />
19.</menu>

각 메뉴 항목을 보면 각각 다른 속성을 적용한 것을 알 수 있습니다. 첫 번째 항목과 두 번째 항목은 모두 액션 아이템으로 표시하도록 설정하였지만, 첫 번째 항목은 액션바에 빈 공간이 있을 때만 표시하며 아이콘과 텍스트 모두 표시되도록 설정하였습니다. 두 번째 항목은 항상 액션 아이템으로 표시되도록 하였으며, 세 번째 항목은 일반적인 메뉴 항목으로써 오버플로우 메뉴에 표시되도록 설정하였습니다.

다음, 메뉴 항목을 선택했을 때 실행할 동작을 구현해 보도록 하겠습니다. 우선 onCreateOptionsMenu()에서 메뉴 리소스 파일을 불러 메뉴에 적용한 후, onOptionsItemSelected() 메서드에서 각 메뉴 항목을 선택했을 때 수행할 동작을 구현합니다.

[Main.java]
01.@Override
02.public boolean onCreateOptionsMenu(Menu menu) {
03.    getMenuInflater().inflate(R.menu.menu, menu);
04.    return true;
05.}
06.@Override
07.public boolean onOptionsItemSelected(MenuItem item) {
08.    String text = null;
09.      
10.    switch(item.getItemId()){
11.    case android.R.id.home:
12.        text = "Application icon";
13.        break;
14.          
15.    case R.id.item1:
16.        text = "Action item, with text, displayed if room exists";
17.        break;
18.          
19.    case R.id.item2:
20.        text = "Action item, icon only, always displayed";
21.        break;
22.          
23.    case R.id.item3:
24.        text = "Normal menu item";
25.        break;
26.          
27.    default:
28.        return false;
29.    }
30.    Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
31.    return true;
32.}

각 항목을 선택할 때마다 해당 항목의 속성을 토스트로 표시하도록 구현했습니다. 앞에서 알아본 것과 같이 액션바에 표시되는 애플리케이션 아이콘(android.R.id.home)을 선택한 것 또한 감지하여 토스트로 표시하도록 구현했습니다.

예제를 실행하면 다음과 같이 액션바가 표시되는것을 확인할 수 있습니다. 메뉴 리소스에서 설정한 것과 같이 첫번째와 두번째 항목이 액션 아이템으로 표시되고, 나머지 한 항목은 오버플로우 메뉴에 표시됩니다. 

액션바가 표시되는 모습



각 항목을 선택하면 앞에서 구현한 것과 같이 각 항목에 해당하는 토스트가 표시됩니다. 애플리케이션 아이콘을 눌렀을 때 토스트가 표시되는 것 또한 확인할 수 있습니다.


애플리케이션 아이콘을 선택했을 때의 모습



이것으로 액션바에 대해 간단히 알아보았습니다. 이 정도면 어느 정도 액션바가 무엇인지에 대해 감을 잡을 수 있었을 것이라 생각합니다. 다음 포스트에서는 액션바의 조금 더 다양한 내용에 대해 알아보도록 하겠습니다.


[번역] The Android 3.0 Fragments API 안드로이드

2011/02/08 22:58

복사 http://huewu.blog.me/110102599243

The Android 3.0 Fragments API

[이 포스트는 안드로이드 개발에 핵심적인 역할을 담당하는, 소프트웨어 엔지니어 Dianne Hackborn 에 의해 작성되었습니다. - Tim Bray]

원문: http://android-developers.blogspot.com/2011/02/android-30-fragments-api.html


 안드로이드 3.0 의 주요 목표 중 하나는, 개발자들이 보다 쉽게 다양한 해상도를 지원하는 어플리케이션을 작성할 수 있도록 만들어 주는 것 입니다. 다양한 해상도를 지원하는 어플리케이션을 개발 할 수 있도록, 지금까지 안드로이드 플랫폼에서는 다음과 같은 기능을 지원해 주고 있었습니다.

  • 애초부터, 안드로이드 UI 프레임워크는 레이아웃 매니저 계층을 사용하도록 구성되었습니다. 레이아웃 매니저를 통해, UI 구성 요소들은 현재 활용 가능한 화면 크기에 맞추어 적절하게 자신의 크기와 위치를 조절할 수 있습니다. 한 가지 예로 ListView 의 높이는 디바이스의 화면 크기가 QVGA, HVGA, WVGA 이건 무관하게, 또한 화면이 가로 세로 비율이 어떻든간에 화면에 꼭 맞추어 알아서 크기가 조절됩니다. 
  • 안드로이드 1.6 에서는 Screen Density (DIP) 라는 새로운 개념이 소개되었습니다. 이를 통해, 개발자들은 실재 물리적인 화면 크기가 유사한 경우, 화면 해상도에대하여 별다른 고민없이 다양한 해상도의 휴대 단말을 지원하는  UI 를 구성할 수 있게 되었습니다. 덕분에, 1.6 발표 이 후 출시된 모토로이 드로이드와 같은 다양한 고해상도 휴대폰등에서 별다른 호환성 문제 없이 기존의 어플리케이션이 동작할 수 있었습니다.
  • 또한, 안드로이드 1.6 에서는 화면의 크기를 몇 가지 카테고리로 나누어 분류 하였습니다. QVGA 급 화면은 "small" , HVGA 나 WVGA 급 화면은 "normal", 그 이상의 크기를 갖는 경우에는 "large" 로 말이지요. 어플리케이션 개발자는 안드로이드에서 제공하는 리소스 시스템을 활용하여, 자신의 어플에서 사용되는 리소스와 화면 레이아웃을 서로 다른 해상도에 맞추어 구분하여 구성할 수 있게 되었습니다.

 레이아웃 메니저와 화면 크기에 따라 유연하게 동작하는 리소스 선택 시스템은 '개발자들이 다양한 크기의 안드로이드 디바이스에서 자연스럽게 동작하는 어플리케이션을 만들 수 있게 하자' 는 원대한 목표하에서 구현되었습니다. 그리고 결과적으로, 다행스럽게도 기존에 개발된 많은 어플리케이션이 최근 새롭게 발표된 10인치 크기의 타블렛용 OS 인 허니콤 상에서 별다른 문제 없이 '잘' 동작하는 것으로 확인되고 있습니다. 하지만, 허니콤이 공개되고 타블렛을 위해 UI 요소에 많은 변화가 생김에 따라, 안드로이드 개발팀은 '만일 기존 어플리케이션이 10인치의 타블렛을 위해 고안된 새로운 UI 요소들을 손 쉽게 활용 할 수 있다면 여러가지 추가적인 장점을 누릴 수 있지 않을까' 고민하게 되었습니다. 그 결과, Fragment 클래스가 추가되었습니다.

Introducing the Fragment
 어플리케이션 개발자는 안드로이드 3.0 에서 새롭게 추가된 Fragment 클래스를 이용하여 어플리케이션의 UI 를 보다 상황에 따라 변경하거나 조절할 수 있습니다. Fragment 는 별도의 UI 와 생명주기를 갖는 독립된 어플리케이션 컴포넌트입니다. 따라서, 여러분은 특정 디바이스 혹은 스크린 해상도에 따라서, 여러 개의 Fragment 를 조합하거나 재배치 하는 방식으로 원하는 UI 를 손쉽게 구성할 수 있습니다.

 다시말해, Fragment 는 미니 엑티비티라고 생각하실 수 있습니다. 일반적인 엑티비티와는 다르게, 반드시 실재 엑티비티에 포함되어 동작해야 되긴 하지만 말이지요. 안드로이드 개발팀은 Fragment 관련 기능을 추가하면서, 어플리케이션 개발자들이 엑티비티를 사용할 때 겪게 되는 일반적인 불편함에 대해 다시 살펴보고 이를 해결할 수 있는 방안을 검토했으며, 그 결과 개발된 Fragment API 는 그저 다양한 해상도나 화면 모드에 따라 UI 를 변경하는 기능 이상의 역할을 수행 할 수 있습니다.

  • 이전 버전의 안드로이드 프레임워크 상에서도, 엑티비티 그룹(ActivityGroup)을 이용하여 복 수의 엑티비티를 하나의 엑티비티에 담을 수 있습니다. (주> 대표적으로 TabActivity) 하지만, 기본적으로 엑티비티는 독립적으로 동작하도록 설계되었기 때문에, 엑티비티 그룹을 적절하게 활용하는 것은 결코 쉬운일이 아니였습니다. Fragment API 를 활용하면 이런 일을 훨씬 더 잘 처리할 수 있으며, 따라서 안드로이드 개발팀은 Fragment 컴포넌트를 이용하면 기존의 임베디드 엑티비티를 완전히 대체할 수 있지 않을까 생각하고 있습니다.
  •  지금까지 화면이 회전되거나 설정 값이 변경됨에 현재 엑티비티가 Destroy 되고 뒤이어 새로운 엑티비티가 생성되는 경우, 두 엑티비티 인스턴스간에 데이타를 유지하기 위해서는, Activity.onRetainNonConfigurationInstance() 메서드를 활용해야 했습니다. (주> 많은 분들이 단순하게 아예 엑티비티가 새로 생성되지 않도록 막는 경우가 있지만, 화면 모드에 따라 전혀 다른 UI 를 제공하고자 한다면 별 수 없이 새로운 엑티비티 인스턴스가 생성되어야 합니다.) 뭐, 크게 어려운 일은 아닐지라도 좀 투박하고 불명확한 솔루션이었지요. Fragment API 를 활용하면, 특정 Fragment 인스턴스에 적절한 플래그만 설정해 주시면, 프레임워크 내에서 해당 인스턴스를 보존해 줍니다.
  • DialogFragment 클래스가 제공됩니다. 이 새로운 클래스를 활용하면, 엑티비티 라이프 사이클에 잘 부합되도록 다이얼로그 박스를 표시하는게 훨씬 수월해 줍니다. (주> 예를 들어, 화면이 회전되는 등의 이유로 엑티비티가 종료되고 재 시작될 때, 기존 다이얼로그 박스에서 표시된 정보를 똑같이 복원하기 위해서는 꽤나 골치아픈 작업이 필요했지요....) 엑티비티가 기존에 엑티비티에서 사용되던 "managed dialog API" 를 대채할 수 있습니다.
  • 또 한가지 ListFragment 클래스가 제공됩니다. 이 클래스를 활용하면, 데이타 목록을 손쉽게 표시할 수 있습니다. 몇 가지 기능이 추가된 점을 제외하면, 기존 ListActivity 와 유사하지만, Fragment 특성 상, 임의의 엑티비티에 손쉽게 포함될 수 있기 때문에, 흔한 질문 중 하나인, '데이타 목록과 동시에 여러가지 추가 정보를 표시하려면 어떻게 하나요?' 같은 문제를 손쉽게 해결 할 수 있게 도와줄 것 입니다.
  • 현재 실행중인 엑티비티에 포함되어 있는 모든 Fragment 인스턴스 정보는 해당 엑티비티의 'saved instance state' 로 자동으로 저장되며, 엑티비티가 잠시 종료되었다가 재 시작될 때 자동으로 복원됩니다. 따라서 여러분이 UI의 상태 정보를 저장, 관리하기 위해 들이는 귀찮은 작업을 대폭 감소시킬 수 있습니다.
  • 안드로이드 프레임워크는 Fragment  인스턴스의 'back-stack'(이전 상태를 저장해 두는 스택) 을 관리합니다. 따라서 여러분은 엑티비티 내부에 뒤로 가기 버튼등의 기능을 손쉽게 구현 할 수 있습니다.

Getting started
 Fragment 에 대한 여러분의 궁금증을 달래기 위해, 이를 활용하여 화면 모드에 따라서 UI 가 완전히 변화되는 어플리케이션을 만들기 위한 샘플 코드를 설명해 볼까 합니다. 우선, 화면이 랜드스케이프(가로) 모드일 때, 화면 왼쪽에 아이템 목록이 표시되고, 오른편에는 선택된 아이템의 상세 정보가 표시되는 레이아웃을 디자인 해 보도록 하겠습니다.

이런 엑티비티를 위한 코드는 별 다를게 없습니다. 그저, onCreate() 시점에 setContentView() 메서드를 호출하여, 아래의 레이아웃 코드를 설정해주면 됩니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       
android:orientation="horizontal"
       
android:layout_width="match_parent"
       
android:layout_height="match_parent">

   
<fragment class="com.example.android.apis.app.TitlesFragment"
           
android:id="@+id/titles" android:layout_weight="1"
           
android:layout_width="0px"
           
android:layout_height="match_parent" />

   
<FrameLayout android:id="@+id/details" android:layout_weight="1"
           
android:layout_width="0px"
           
android:layout_height="match_parent" />
   
</LinearLayout>
 레이아웃 코드를 살펴 보시면, 새롭게 추가된 <fragment> 태그를 확인 할 수 있습니다. 이 태그를 사용하면 프레임워크에서 자동적으로 Fragment 인스턴스를 생성하고, 생성된 Fragment 인스턴스를 엑티비티 View 계층 구조 안에 삽입합니다. 여기서 사용된 Fragment 는 ListFragment 를 상속받아 작성되었으며 아이템 목록을 관리하고 화면상에 출력합니다. 
public static class TitlesFragment extends ListFragment {
   
boolean mDualPane;
   
int mCurCheckPosition = 0;

   
@Override
   
public void onActivityCreated(Bundle savedState) {
       
super.onActivityCreated(savedState);

       
// Populate list with our static array of titles.
        setListAdapter
(new ArrayAdapter<String>(getActivity(),
                R
.layout.simple_list_item_checkable_1,
               
Shakespeare.TITLES));

       
// Check to see if we have a frame in which to embed the details
       
// fragment directly in the containing UI.
       
View detailsFrame = getActivity().findViewById(R.id.details);
        mDualPane
= detailsFrame != null
               
&& detailsFrame.getVisibility() == View.VISIBLE;

       
if (savedState != null) {
           
// Restore last state for checked position.
            mCurCheckPosition
= savedState.getInt("curChoice", 0);
       
}

       
if (mDualPane) {
           
// In dual-pane mode, list view highlights selected item.
            getListView
().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
           
// Make sure our UI is in the correct state.
            showDetails
(mCurCheckPosition);
       
}
   
}

   
@Override
   
public void onSaveInstanceState(Bundle outState) {
       
super.onSaveInstanceState(outState);
        outState
.putInt("curChoice", mCurCheckPosition);
   
}

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

   
/**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */

   
void showDetails(int index) {
        mCurCheckPosition
= index;

       
if (mDualPane) {
           
// We can display everything in-place with fragments.
           
// Have the list highlight this item and show the data.
            getListView
().setItemChecked(index, true);

           
// Check what fragment is shown, replace if needed.
           
DetailsFragment details = (DetailsFragment)
                    getFragmentManager
().findFragmentById(R.id.details);
           
if (details == null || details.getShownIndex() != index) {
               
// Make new fragment to show this selection.
                details
= DetailsFragment.newInstance(index);

               
// Execute a transaction, replacing any existing
               
// fragment with this one inside the frame.
               
FragmentTransaction ft
                       
= getFragmentManager().beginTransaction();

                ft
.replace(R.id.details, details);
                ft
.setTransition(
                       
FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft
.commit();
           
}

       
} else {
           
// Otherwise we need to launch a new activity to display
           
// the dialog fragment with selected text.
           
Intent intent = new Intent();
            intent
.setClass(getActivity(), DetailsActivity.class);
            intent
.putExtra("index", index);
            startActivity
(intent);
       
}
   
}
}
 구현 내용을 살펴보시면, UI 레이아웃 상에, 선택된 아이템의 상세 정보를 표시할 수 있는 'details' 라는 ID 의 View 가 존재하는지 여부에 따라, 상세 정보를 현재 엑티비티 내에서 모두 표현할지, 아니면 별도의 엑티비티를 호출하여 표현할지 구분되어 구현되어있음을 확인 하실 수 있습니다. 또, Fragment 인스턴스의 정보는 프레임워크 내에서 자동으로 저장 관리되기 때문에, newInstance() 메서드를 통해 생성된 DetailsFragment 인스턴스를 별다른 관리 없이 FragmentManager 의 findFragmentById 메서드를 통해 재활용해 사용하고 있습니다.

 선택된 아이템 상세 정보를 표현하기 위한 Details첫 화면을 위해서 DetailsFragment 는 주어진 문자열을 TextView 로 출력하는 역할을 수행합니다. 다음과 같이 간단하게 구현할 수 있습니다.

public static class DetailsFragment extends Fragment {
   
/**
     * Create a new instance of DetailsFragment, initialized to
     * show the text at 'index'.
     */

   
public static DetailsFragment newInstance(int index) {
       
DetailsFragment f = new DetailsFragment();

       
// Supply index input as an argument.
       
Bundle args = new Bundle();
        args
.putInt("index", index);
        f
.setArguments(args);

       
return f;
   
}

   
public int getShownIndex() {
       
return getArguments().getInt("index", 0);
   
}
   
   
@Override
   
public View onCreateView(LayoutInflater inflater,
           
ViewGroup container, Bundle savedInstanceState) {
       
if (container == null) {
           
// Currently in a layout without a container, so no
           
// reason to create our view.
           
return null;
       
}

       
ScrollView scroller = new ScrollView(getActivity());
       
TextView text = new TextView(getActivity());
       
int padding = (int)TypedValue.applyDimension(
               
TypedValue.COMPLEX_UNIT_DIP,
               
4, getActivity().getResources().getDisplayMetrics());
        text
.setPadding(padding, padding, padding, padding);
        scroller
.addView(text);
        text
.setText(Shakespeare.DIALOGUE[getShownIndex()]);
       
return scroller;
   
}
}
 이제, 추가로 화면이 포트레이트 (세로) 모드일 때를 위한 새로운 UI 를 구성해 보겠습니다. 새로 모드의 경우, 두 개의 Fragment 를 나란히 배치할 공간이 부족하니, 이 경우에는 오직 아이템 목록만을 보여주고, 사용자가 아이템을 클릭하면 별도의 엑티비티에서 상세 정보를 보여주고자 합니다.

 사실 이 작업은, 지금까지 작성해둔 코드를 활용하면, 포트레이트 모드를 위하여 아래와 같은 레이아웃을 선언하면 끝입니다.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       
android:layout_width="match_parent"
       
android:layout_height="match_parent">
   
<fragment class="com.example.android.apis.app.TitlesFragment"
           
android:id="@+id/titles"
           
android:layout_width="match_parent"
           
android:layout_height="match_parent" />
</FrameLayout>
위 구현내용을 살펴보시면 알 수 있듯이, TitleFragment 는 아이템 상세 정보를 표시하기 위한 'details' ID 를 갖는 View 가 없는 경우 단지 아이템 목록만을 표시하고, 여러분 하나의 아이템을 선택할 때, 해당 아이템의 상세 정보를 담은 새로운 엑티비티를 표시하게 될 것 입니다.

 이미 상세 정보를 출력하기 위해 DetailsFragment 를 구현하였기 때문에, 새로운 엑티비티도 손쉽게 작성할 수 있습니다.
public static class DetailsActivity extends FragmentActivity {

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

       
if (getResources().getConfiguration().orientation
               
== Configuration.ORIENTATION_LANDSCAPE) {

           
// If the screen is now in landscape mode, we can show the
           
// dialog in-line so we don't need this activity.
            finish
();
           
return;
       
}

       
if (savedInstanceState == null) {
           
// During initial setup, plug in the details fragment.
           
DetailsFragment details = new DetailsFragment();
            details
.setArguments(getIntent().getExtras());
            getSupportFragmentManager
().beginTransaction().add(
                    android
.R.id.content, details).commit();
       
}
   
}
}
 작업이 완료되었습니다. 우리는 새로운 Fragment API 를 이용하여, 화면 모드가 변함에 따라 UI 플로우가 완전히 변경되는 온전한 어플리케이션 예제를 만들었습니다.

 지금까지 예제를 통해 Fragment 를 활용하여 UI 를 변경할 수 있는 한 가지 방법을 설명했습니다. 여러분의 어플리케이션 디자인에 따라서, 다양한 방식으로 이를 활용할 수도 있습니다. 예를 들어, 여러분은 전체 어플리케이션을 하나의 엑티비티에 담고, 엑티비티의 상태가 변함에 따라 내부 Fragment 의 구조만을 변경하는 방식으로 어플리케이션을 작성할 수도 있을 것 입니다. 이런경우, Fragment 가 자체적으로 'back-stack' 을 유지하고 있다는 사실이 큰 도움이 될 것 입니다.
 
 Fragment 와 FragmentManager API 에 관한 보다 상세한 정보는 안드로이드 3.0 SDK 문서에서 살펴보시거나, 리소스 항목 아래에 있는 ApiDemos 를 참조해 보셔도 좋습니다. 안드로이드 개발팀은 자유롭게 변환되는 UI 플로우, Fragment 다이얼로그, 리스트, 메뉴 생성, 엑티비티 인스턴스간에 Fragment 인스턴스를 유지하기, 'back-stack' 등등 다양한 예제 코드를 마련해 두었습니다.

Fragmentation for all!
 안드로이드 3.0 기반의 태블릿 용 어플리케이션 개발자들은 새로운 Fragment API 를 이용하여, 큰 화면에 따른 다양한 UI 설계 문제를 보다 잘 처리하실 수 있을 것 입니다. 거기에 덧붙여 만일에, Fragment 를 잘 활용한다면, 향후 등장할 새로운 안드로이드 디바이스 - 휴대폰, TV 등등 에 맞추어 여러분의 어플리케이션 UI 를 쉽게 변경할 수 있을 것 입니다.
 
 하지만, 바로 오늘 이 순간에는 많은 개발자들이 현재 존재하는 휴대폰 용 어플리케이션을 작성하고 있으며, 해당  어플리케이션이 타블렛 상에서는 보다 향상된 UI 요소를 활용할 수 있으면 얼마나 좋을까... 하고 생각하고 계실 것 입니다. 현재 Fragment API 가 오직 안드로이드 3.0 에서만 지원되고 있다는 사실을 생각해보면, 이는 참으로 안타까운 일 입니다.

 이런 문제를 해결하기 위하여, 안드로이드 개발팀은 이 포스트에서 이야기한 Fragment API 를 기존의 안드로이드 플랫폼에서도 활용 할 수 있도록 Static 라이브러리 형태로 제공할 예정입니다. 현재 계획으로는 1.6 이후의 모든 플랫폼을 지원하고자 하며, 사실 여러분이 이 포스트에서 사용된 코드는 바로 그러한 Static 라이브러리를 활용하여 작성되었습니다. 눈치가 빠른 분들이라면 스크린샷으로도 확인하실 수 있었겠지요. 현재, 우리의 목표는 Static 라이브러리에서 제공하는 API 와 안드로이드 3.0 플랫폼에서 제공하는 API 를 최대한 동일하게 만드는 것이며, 여러분이 지금 즉시 Fragment API 를 활용하고, 향후 안드로이드 3.0 을 사용하실 때도, 기존 어플리케이션 코드를 거의 변경하지 않고 적용할 수 있도록 만들고자 합니다.

 Fragment API 를 위한 Static 라이브러리를 제공하는 날짜가 아직 확정되지는 않았지만, 생각보다 이른 시간내에 공개할 것이라고는 말씀드릴 수 있습니다. 그 사이에 여러분은 안드로이드 3.0 SDK 를 이용하여 Fragment API 를 어떤식으로 활용할 수 있는지 테스트해보실 수 있으며, 그 노력은 결코 헛되지 않을 것 입니다.

+ Recent posts