액션바 taps+swipe에서 잠시 언급했던 fragment에 대해 이번엔 정리해 보도록 하겠습니다.

안드로이드 플랫폼 3.0 버전인 허니컴Honeycomb부터 큰 화면의 태블릿을 위해 도입된 대표적인 컴포넌트가 프래그먼트(Fragment)와 액션바(ActionBar)입니다.그 중 액션바는 이전 게시판에서 이야기했으니 오늘은 프래그먼트에 대해서입니다.

 프래그먼트 개요

만약 스마트 폰 앱을 개발할 때 세로모드(Portrait)에서는 단순하게 화면을 구성하지만 가로모드(Landscape) 화면 구성을 좀더 다양한 화면으로 구성 하고 싶다면 어떻게 할지 고민할 수 있습니다. 또는 해상도나 디바이스의 스크린크기, 디바이스 종류에 따라서 보여지는 것들을 다르게 구성하고 싶어할 수도 있습니다.

안드로이드에서는 이러한 요구사항을 만족시켜줄 수 있는 것을 fragment라는 개념으로 추가하게 되었습니다.

안드로이드 3.0이전 버전에서는 한 화면에 보이는 모든 것을 관장하는 것이 Activity라는 개념이였는데 이러한 Activity는 하나의 화면에 여러개 사용할 수 없게 설계가 되어있습니다.

그러나 하나의 액티비티로 상호작용하는 것은 큰 화면의 기기에서 비효율적이며 자원 낭비가 심하고 또한 일부 화면을 위한 자원을 재사용할 수 없고, 다양한 내용을 표시하려면 구성이 매우 복잡했습니다.

그래서 하나의 화면에 Activity와 비슷한 개념을 가지면서도 여러가지 화면을 넣을 수 있는 방법으로 fragment가 등장하게 되었습니다. 

 프래그먼트 특징

1) 프래그먼트는 액티비티 조각 혹은 서브액티비티(subactivity)

2) 하나의 액티비티에서 다수의 프래그먼트를 결합하여 다중 패널multi-pane UI를 생성할 수 있음

3) 여러 액티비티에서 하나의 프래그먼트를 재활용 가능

4) 프래그먼트 자신만의 생명주기 가짐

5) 입력 이벤트를 받아들이며, 액티비티가 실행하는 동안 동적으로 추가 혹은 삭제 가능

6) activity 안에서만 존재할 수 있고 단독으로 존재할 수 없다.

7) activity 안에서 다른 view와 함께 존재 가능

8) back stack을 사용가능

9) 반드시 default 생성자 존재

 프래그먼트 디자인 철학

애플리케이션을 태블릿과 핸드셋에 모두 지원하도록 설계하고자 한다면 화면의 사이즈에 따라 최적의 UX을 맛볼 수 있도록 프래그먼트를 다양한 레이아웃 구성에 재사용할 수 있습니다.

작은 화면사이즈 기기에는 하나의 액티비티에 하나의 프래그먼트를 사용하는 1-패널 UI를 제공하고, 태블릿과 같은 큰 화면사이즈 기기에서는 2개의 프래그먼트를 하나의 액티비티에 조합하여 2-패널UI를 제공하면 됩니다. 

 프래그먼트 생명주기

● 활성resumed 상태 : 실행 중인 액티비티에서 프래그먼트가 보이는 상태

● 중지paused 상태 : 다른 액티비티가 포그라운드 상태이며 포커스를 가지고 있지만 프래그먼트가 거주하는 액티비티가 여전히 보이는 상태

● 정지stopped 상태 : 프래그먼트가 보이지 않는다. 호스트 액티비티가 정지되거나 프래그먼트가 액티비티에서 제거되고 백스택에 추가되어 있는 상태 정지된 프래그먼트는 여전히 살아 있어 모든 상태와 멤버 정보가 시스템에 보관되어 있으나 사용자에게 더 이상 보이지 않고 액티비티가 종료되면 프래그먼트도 같이 종료됨

아래 소스에서 사용한 메소드는 onActivityCreated,onResume,onPause, onCreateView정도여서 http://blog.saltfactory.net/190 의 설명을 인용하도록 하겠습니다.

● onAttach()

onAttach() 콜백 메소드는 fargment가 activity에 추가되고 나면 호출된다. 이때 Activity가 파라미터로 전달되게 된다. Fragment는 Activity가 아니다. 다시말해서 Activity는 Context를 상속받아서 Context가 가지고 있는 많이 있지만 Fragment는 android.app 의 Object 클래스를 상속받아서 만들어진 클래스이다. 그래서 Fragment에서는 Context의 기능을 바로 사용할 수 없다. 뿐만 아니라 Fragment는  Activity 안에서만 존재하기 때문에 Activity를 onAttach() 콜백으로 받아올 수 있다.

 public void onAttach(Activity activity) {
      super.onAttach(activity);
 }



onCreate()

프래그먼트가 생성될 때 호출된다. 프래그먼트가 중지 혹은 정지된 후 재개될 때 보유하기 원하는 프래그먼트의 필수 컴포넌트을 초기화한다.

Fragment의 onCreate() 메소드는 Activity의 onCreate() 메소드와 비슷하지만 Bundle을 받아오기 때문에 bundle에 대한 속성을 사용할 수 있다.

 	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}


onCreateView()

Fragment의 onCreateView()는 Fragment에 실제 사용할 뷰를 만드는 작업을 하는 메소드이다. LayoutInflater를 인자로 받아서 layout으로 설정한 XML을 연결하거나 bundle에 의한 작업을 하는 메소드이다. 

	public View onCreateView(LayoutInflater inflater, ViewGroup container,
	                Bundle savedInstanceState)
	{
	        view = inflater.inflate(R.layout.table, container, false);
	        return view;
	}


onActivityCreated()

onActivityCreate() 메소드는 Activity에서 Fragment를 모두 생성하고 난 다음에 (Activity의 onCreate()가 마치고 난 다음)에 생성되는 메소드이다. 이 메소드가 호출될 때에서는 Activity의 모든 View가 만들어지고 난 다음이기 때문에 View를 변경하는 등의 작업을 할 수 있다.그러나, fragment에서 생성된 view의 size를 알려고 하니 이 메소드에서는 getWidth가 0으로 나와 따로 메소

    public void onActivityCreated(Bundle savedInstanceState) {
    	//Log.d(TAG,"onActivityCreated");
	    super.onActivityCreated(savedInstanceState);
    }


onStart()

이 메소드가 호출되면 화면의 모든 UI가 만들어진 지고 호출이 된다.

    public void onStart(){
    	super.onStart();
    }


onResume()

이 메소드가 호출되고 난 다음에 사용자와 Fragment와 상호작용이 가능하다. 다시 말해서 이 곳에서 사용자가 버튼을 누르거나 하는 이벤트를 받을 수 있게 된다.

	public void onResume() {
		super.onResume();

		⁄⁄mStatus.setText("현재 상태 : 서비스 시작");
		cellwidth = new int[5];
		desity = this.getResources().getDisplayMetrics().density;
		appendHeader();
		appendRowBlank();
		datainfo = selectData();
		appendRow(datainfo);	
		ViewTreeObserver vto = headerView[0].getViewTreeObserver();
		vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
		  @SuppressWarnings("deprecation")
		@Override
		  public void onGlobalLayout() {
		    ⁄⁄Log.d(TAG, "Height = " + headerView[0].getWidth() + " Width = " + tv[0][0].getWidth());
		    ViewTreeObserver obs = headerView[0].getViewTreeObserver();
		    resizeRow();
		    obs.removeGlobalOnLayoutListener(this);
		  }
		});		
		
	}	


onPause()

이 메소드는 Fragment가 다시 돌아갈 때 (Back) 처음으로 불려지는 콜백 메소드이다. 

    
   @Override
    public void onPause() {
        super.onPause();
    }


onSaveInstanceState()

이 메소드에서는 Activity와 동일하게 Fragment가 사라질때 현재의 상태를 저장하고 나중에 Fragment가 돌아오면 다시 저장한 내용을 사용할 수 있게해주는 메소드이다.

    
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putInt("text", "savedText");
    }


● onStop()

Fragment의 onStop() 메소드는 Activity의 onStop()메소드와 비슷하다. 이 콜백 메소드가 호출되면 Fragment가 더이상 보이지 않는 상태이고 더이상 Activity에서 Fragment에게 오퍼레이션을 할 수 없게 된다.

    
    @Override
    public void onStop() {
        super.onStop();
    }


onDestroyView()

Fragment의 View가 모두 소멸될 때 호출되는 콜백 메소드이다. 이때 View에 관련된 모든 자원들이 사라지게 된다.

    
    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }


onDestroy()

Fragment를 더이상 사용하지 않을 때 호출되는 콜백 메소드이다.  하지만 Activity와의 연결은 아직 끊어진 상태는 아니다.

    @Override
    public void onDestroy() {
        super.onDestroy();
    }


onDetach()

Fragment가 더이상 Activity와 관계가 없을 때 두 사이의 연결을 끊으며 Fragment에 관련된 모든 자원들이 사라지게 된다.

   @Override
    public void onDetach() {
       super.onDetach();
       activity = null;
    }

 전체소스

PublicWifi_0409.zip

참고사이트

http://developer.android.com/guide/components/fragments.html

http://blog.saltfactory.net/190

swipe기능이 뭔지 어색할지 모르지만 스마트폰 사용자라면 지금도 많이 사용하고 있는 기능입니다. 특히 안 가르쳐줘도 아이들은 정말 잘 사용하는 기능이죠. 손가락으로 클릭후 오른쪽/왼쪽으로 움직하면 화면이 변경되는 기능입니다.

Android 3.0 부터 제공되는 Swipe Views와 Tabs + Swipe를 편리하게 생성하고 사용할 수 있습니다. 

프로젝트 생성시 다른 프로젝트와 조금 다르게 설정만 해주면 됩니다.

그럼 작성도 안 했는데 많은 소스의 내용이 생깁니다. 왠지 뿌듯하지만 작동로직을 제대로 이해하지 못하면 남이 짠 소스이니 구현에 힘이 들 수 있습니다.

이제부터 자동생성 소스 중 변경부분을 위주로 설명하도록 하겠습니다.

 

 변경전 소스

		@Override
		public Fragment getItem(int position) {
			⁄⁄ getItem is called to instantiate the fragment for the given page.
			⁄⁄ Return a DummySectionFragment (defined as a static inner class
			⁄⁄ below) with the page number as its lone argument.
			Fragment fragment = new DummySectionFragment();
			Bundle args = new Bundle();
			args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
			fragment.setArguments(args);
			return fragment;
		}

 변경후 소스

		@Override
		public Fragment getItem(int position) {
			Fragment fragment = null;
			
			⁄⁄ getItem is called to instantiate the fragment for the given page.
			⁄⁄ Return a DummySectionFragment (defined as a static inner class
			⁄⁄ below) with the page number as its lone argument.
			switch (position) {
			case 0:			
				fragment = new TabOne();
				break;
			case 1:			
				fragment = new TabTwo();
				break;
			}
			return fragment;
		}

메인activity는 이렇게 만 변경하고 물론 tab이 기본이 3개인데 2개 바꾸는 기본적인 변경의 내용은 그냥 생략했습니다. 보면 직관적으로 알 수 있기 때문에 전체 소스를 참고하시면 될 듯 합니다.

public class TabOne extends Fragment implements MapView.OpenAPIKeyAuthenticationResultListener, 
MapView.MapViewEventListener,
MapView.CurrentLocationEventListener,
MapView.POIItemEventListener {

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

	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
	                Bundle savedInstanceState)
	{
	        view = inflater.inflate(R.layout.map, container, false);
	        linearLayout = (LinearLayout)view.findViewById(R.id.maplayout);
	}	

}

public class TabTwo extends Fragment {

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

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

이렇게 2개의 fragment를 생성해주면 됩니다. 프래그먼트에 대해서는 다음에 한번 이론적으로 접근해보고 지금은 이렇게 activity와 같은 식으로 추가해주면 아래와 같은 기능을 가진 앱이 만들어집니다.

그래도 이 기능은 기본 소스를 만들어주어서 참 편하게 구현할 수 있는 기능입니다.

 

 

 

PublicWifi_0409.zip

아직 생각한 모든 기능을 구현한 것은 아니지만 이 주제는 다 구현했으니 전체 소스도 첨부합니다.

액션바에 아래와 같이 검색창을 넣을 수 있는데 이를 위해서는 3가지 작업이 필요하다.

1) menu xml에 SearchView item추가

    <item
        android:id="@+id⁄action_search"
 
        android:showAsAction="ifRoom"
        android:actionViewClass="android.widget.SearchView"
        android:title="@string⁄search"
       android:icon="@drawable⁄search"        
    ><⁄item>

2) onCreateOptionsMenu 에 search view 항목추가 및 Listener 설정

@Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.activity_lost, menu); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { searchItem = menu.findItem(R.id.action_search); searchView = (SearchView) searchItem.getActionView(); searchView.setQueryHint("물품명 또는 분류"); searchView.setOnQueryTextListener(queryTextListener); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); if(null!=searchManager ) { searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); } searchView.setIconifiedByDefault(true); } return true; }

 

개발참고사이트 : http://developer.android.com/reference/android/widget/SearchView.html 

 setQueryHint(CharSequence)

 쿼리 필드가 비어있을 때 나타나는 문장 설정

 setOnQueryTextListener(SearchView.OnQueryTextListener listener)

 쿼리 변경시 사용자 액션의 listener 설정

setSearchableInfo(SearchableInfo searchable)  SearchableInfo 설정
 setIconifiedByDefault(boolean iconified)  검색창의 기본상태 설정
true : 필드가 보임
false : 아이콘으로 보임

3) 리스너에 처리로직 구현

	private OnQueryTextListener queryTextListener = new OnQueryTextListener() {
		@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
		@Override
		public boolean onQueryTextSubmit(String query) {
			⁄⁄ TODO Auto-generated method stub
			⁄⁄searchItem.collapseActionView();
			⁄⁄Log.d(TAG,"onQueryTextSubmit");
			Cursor mCount= db.rawQuery("select count(*) from datainfo where GetName like '%" + query+"%' or cate like '%"+ query+"%'", null);
			mCount.moveToFirst();
			count= mCount.getInt(0);
			mCount.close();
			if(count <=0){
				Toast.makeText(LostActivity.con, "검색된 데이타가 없습니다.", Toast.LENGTH_LONG).show();
				return false;
			}
			datainfo = new DataInfo[count];
			Cursor result = db.rawQuery("SELECT * from datainfo where GetName like '%" + query+"%' or cate like '%"+ query+"%' order by GetDate desc", null);
			result.moveToFirst();	
			int i =0;
			while (i<count){
				datainfo[i] = new DataInfo();
				datainfo[i].SetLostId(result.getString(1));
				datainfo[i].SetLostName(result.getString(2));
				datainfo[i].SetLostURL(result.getString(3));
				datainfo[i].SetLostDate(result.getString(5));
				datainfo[i].SetLostTitle(result.getString(4));
				datainfo[i].SetTakePlace(result.getString(6));
				datainfo[i].SetTakeContact(result.getString(7));
				datainfo[i].SetLostPos(result.getString(9));
				datainfo[i].SetLostPlace(result.getString(10));
				datainfo[i].SetLostThing(result.getString(11));
				datainfo[i].SetLostStatus(result.getString(12));
				datainfo[i].SetLostImageUrl(result.getString(14));
				i++;
				result.moveToNext();
			}
			result.close();	
			appendRow(datainfo);
			resizeRow(count);
			⁄⁄Log.d(TAG,"onQueryTextSubmit"+count);
			imm= (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
			imm.hideSoftInputFromWindow(searchView.getWindowToken(), 0);
			searchView.setQuery("", false);
			searchView.setIconified(true);
			⁄⁄Toast.makeText(LostActivity.con, "onQueryTextSubmit:["+count+"]", Toast.LENGTH_LONG).show();
			return false;
		}
		@Override
		public boolean onQueryTextChange(String newText) {
			// TODO Auto-generated method stub
			return false;
		}
	};

 

 

Lost_completed.zip

 

 

 

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

기존 안드로이드 APP의 TitleBar가 단순한 제목의 표시나 간단한 정보만을 표시했다면 액션바는 아래와 같은 다양한 기능을 제공하며 4.0부터는 메뉴버튼까지 대체합니다.

 

 액션바의 주요기능

1) 타이틀바의 기능인 제목 표시 및 간단한 정보 표현

2) 사용자의 현재 위치 식별

3) 각종메뉴-탐색메뉴,액션항목을 통한 단축메뉴-제공

4) Navigation 지원

 

 액션바의 주요목적

1) APP의 이름,정보 와 사용자 위치 식별하기 위한 전용공간 제공

2) 다른 APP사의 일관된 네비게이션과 뷰의 세분화 제공

3) 검색,생성 공유와 같은 주요 액션에 대한 용이한 젭근 및 예측 가능 접근

 

 액션바의 주요UI

 

1. 앱 아이콘

: 애플리케이션 아이콘은 애플리케이션에 아이덴티티를 부여합니다. 원할 경우 다른 로고나 브랜드로 교체할 수도 있습니다. 중요: 만약 애플리케이션이 현재 최상위 화면을 표시하고 있지 않다면, 사용자가 상위 계층으로 이동할 수 있도록 애플리케이션 왼쪽에 상위 기호(Up caret)를 표시해야 합니다.

2. view control

: 만약 애플리케이션이 여러가지 뷰에서 데이터를 표시하고 있다면, 액션 바의 이 부분은 사용자가 뷰 간을 전활할 수 있도록 해줍니다. 뷰 전환 컨트롤에는 드롭다운 메뉴나 탭 컨트롤 등이 있습니다. 만약 애플리케이션이 여러가지 뷰를 지원하지 않는다면, 이 영역을 애플리케이션 타이틀이나 더 긴 브랜드 정보 등을 표시하는데 사용할 수도 있습니다.

 3. Action button

: 액션 섹션에서 애플리케이션의 중요한 액션들을 표시합니다. 액션 바에 들어갈 공간이 없는 액션들은 자동으로 액션 오버플로우 속으로 이동합니다.

4. Action overflow

: 덜 자주 사용하는 액션은 액션 오버플로우에 배치합니다.

 

 액션바의 사용시 주의사항

ActionBar, 액션바를 사용하기 위해서는 targetSdkVersion에 11 이상을 지정하셔야 합니다.

1
<uses-sdk android:targetSdkVersion="11" />



minSdkVersion은 하위 버전에 대한 호환을 고려하지 않을경우, 11이상으로 해두시면 됩니다.

이렇게 targetSdkVersion을 API 11이상으로 설정하시면, 자동적으로 액션바가 사용이 됩니다.

 

 Split action bar

안드로이드 4.0부터 split action bar라고 불리는 추가적인 모드를 사용할 수 있다. 분할 액션바를 사용하면 화면 하단에 별도의 bar가 보이며, 좁은 화면에서 액티비티가 실행되더라도 모든 액션 항목을 보여준다. 액션바를분할하여 액션 항목을 분리하는 것은 상단에 내비게이션과 타이틀 요소를 남겨둔 채로 좁은 화면에서 합리적인 공간을 사용하여 모든 액션 항목을 표시한다. 그리고 다양한 기기의 화면 크기와 화면 회전에 고려한 APP을 만들 때 중요한 UI입니다.

분할 액션바를 활성화하려면 uiOptions="splitActionBarWhenNarrow"를 매니페스트 파일의 <activity> 혹은 <application> 엘리먼트에 추가하면 됩니다.

 

1. 메인 액션바

2. 상단바

3. 하단바

사용자가 빠르게 애플리케이션에서 제공하는 뷰 간을 전환할 수 있도록, 상단 바에 탭이나 스피너를 배치하고 액션과 액션 오버 플로우를 표시하려면 하단 바를 배치하는 방법이 유용한 UI가 될 수 있습니다.

 

아직 split action bar는 사용을 해보지 못해서 이론적인 부분만 추가해 두었습니다.

+ Recent posts