Home » Android -9. 선택 위젯 및 커스텀 뷰

Android -9. 선택 위젯 및 커스텀 뷰

9.1. Fragment

  • 프래그먼트는 태블릿과 같은 큰 화면을 가지는 단말에서 애플리케이션이 화면을 더 효율적으로 활용할 수 있도록 도와준다. 기존에는 애플리케이션 화면을 구성하는 큰 틀이 액티비티(Activity) 하나였고, 이 안을 여러 뷰로 구성하여 정보를 표시하고, 상호작용을 수행했었다.
  • 그런데, 뷰만을 사용해서 다양한 내용을 보여주기는 매우 어려웠다. 특히 전체적인 UI 틀은 고정되어 있으면서 특정 부분만 변화하며 다른 내용을 표시하도록 하려면 매우 복잡한 구성이 필요했다. 또한, 서로 다른 역할을 하는 코드들이 같은 곳에 있게 되어서 가독성도 떨어지고 유지보수에도 악영향을 미쳤다.
  • 이를 위해 나온 것이 프래그먼트이며 간단하게 표현하자면 ‘뷰(View)처럼 사용할 수 있는 액티비티(Activity)’라 할 수 있다. 즉, 액티비티와 뷰의 특징을 모두 가지고 있다.
  • 프래그먼트는 뷰에게 레이아웃 내에 자유롭게 배치될 수 있는 특징을 물려받았는데, 바로 ‘생명주기’이다.

Fragment의 생명주기


그림 39. Fragment의 생명주기

<fragment_sample.xml>
[php]
<LinearLayout (1)
android:id="@+id/fragmentSampleTabArea"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3" >
[/php]

번호 설명
(1) 화면 전환이 될 레이아웃

<step7/FragmentSample>
[php]
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.fragmentSampleFragmentButton1:
fragment = new FragmentSampleFrag1(); (1)
break;

case R.id.fragmentSampleFragmentButton2:
fragment = new FragmentSampleFrag2();
break;
… 생략

}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); (2)
// 프래그먼트 영역 replace
transaction.replace(R.id.fragmentSample, fragment); (3)
transaction.commit(); (4)
}
[/php]

번호 설명
(1) 분기에 따른 fragment 생성
(2) Fragment 변경 작업을 위한 transaction 객체 생성
(3) Fragment 교체 인자로 바꿀 레이아웃의 id와 프래그먼트를 받는다.
(4) commit메소드를 이용한 작업 실행

<step7/FragmentSampleFrag1>
<step7/FragmentSampleFrag2>
<step7/FragmentSampleFrag3>

  • 각 뷰를 구성한 클래스로 예제에서는 내용이 같으며, 레이아웃의 변동만 있으므로 frag1만 설명
    [php]
    public class FragmentSampleFrag1 extends Fragment { (1)
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_sample_frag1, null); (2)
    Button btn = (Button) rootView.findViewById(R.id.fragmentSampleFrag1_button); (3)
    btn.setText("button1");

    btn.setOnClickListener(new View.OnClickListener() { (4)
    @Override
    public void onClick(View v) {
    ToastUtil.show(getActivity(), "clicked button1");
    }
    });

    return rootView;

    }
    }
    [/php]

    번호 설명
    (1) Fragment를 상속
    (2) 레이아웃 정보 가져와서 객체로 생성
    (3) 버튼 객체 생성
    (4) 버튼 리스너




그림 40. Fragment

9.2. 갤러리

  • Gallery는 가로로 스크롤을 하면서 아이템들을 탐색할 수 있는 “가로형 목록 객체” 라고 이해 하면 될 것이다.

<gallery_sample.xml>
[php]
<Gallery
android:id="@+id/gallerySampleGalleryView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#55999999" (1)
/>
[/php]

번호 설명
(1) Background 설정. 기본 색상표의 # + 숫자6자리로 구성된다.
하지만 예제와 같이 앞에 2자리가 더 붙어 8자리가 된 경우, 앞 2자리는 alpha(투명도) 값으로 적용된다.

<step7/GallerySample>
[php]
// 갤러리에 담을 이미지
private Integer[] IMAGES = { (1)
R.drawable.g1,
R.drawable.g2,
R.drawable.g3,
R.drawable.g4,
R.drawable.g5,
R.drawable.g6,
R.drawable.g7
};
… 생략
imgView = (ImageView) findViewById(R.id.gallerySampleImgView);
Gallery galleryImage = (Gallery) findViewById(R.id.gallerySampleGalleryView);
// 어댑터 연결
galleryImage.setAdapter(new GalleryAdapter(GallerySample.this, IMAGES)); (2)
galleryImage.setOnItemClickListener(new OnItemClickListener() { (3)
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
imgView.setImageResource(IMAGES[position]); (4)
}
});
[/php]

번호 설명
(1) Imgae의 Resource id를 가지고 있는 배열
(2) 어댑터 생성. 인자로 context와 image 배열을 넘겨준다.
(3) 리스너 등록. 해당 아이템을 선택하면 아이템 정보를 받아온다.
(4) 아이템의 position 값을 이용하여 선택한 이미지로 바꾸어준다.

<step7/GalleryAdapter>
[php]
public class GalleryAdapter extends BaseAdapter { (1)
private Context mContext;
Integer[] galleryList;
int background;
// 생성자
public GalleryAdapter(Context c, Integer[] galleryList) {
mContext = c;
this.galleryList = galleryList;
// background 테마 가져오기
TypedArray arr = mContext.obtainStyledAttributes(R.styleable.Theme); (2)
background =
arr.getResourceId(R.styleable.Theme_android_galleryItemBackground, 0);
arr.recycle();
}

@Override
public int getCount() { (3)
return galleryList.length;
}

@Override
public Object getItem(int position) {
return position;
}

@Override
public long getItemId(int position) {
return position;
}

// 실제 눈에 보여지는 작업을 하는 메소드
@Override
public View getView(int position, View convertView, ViewGroup parent) { (4)
ImageView imageView;
imageView = new ImageView(mContext);
imageView.setImageResource(galleryList[position]);
imageView.setScaleType(ImageView.ScaleType.FIT_XY); (5)
imageView.setLayoutParams(new Gallery.LayoutParams(300,250)); (6)
imageView.setBackgroundResource(background); (7)

return imageView;
}

}
[/php]

번호 설명
(1) BaseAdapter 상속
(2) Background 테마 설정, 하지 않을 경우 그림이 겹쳐 보이는 등 문제가 발생한다.
(3) 아이템의 총 개수. 여기 설정한 만큼 getView가 호출 된다.
(4) 실제 뷰를 만들어주는 메소드
(5) 이미지의 스케일 설정
(6) 이미지의 크기 설정
(7) 이미지의 뷰에 이미지 설정


그림 41. 갤러리

9.3. View Pager를 이용한 갤러리 구현

  • 갤러리는 상위 버전부터 deprecated 되었기 때문에 상위버전부터는 여러가지 방법을 사용하여 갤러리와 같은 모습을 구현하는데, 그 중 많이 쓰이는 것이 View Pager이다.
  • ViewPager란 수평으로 View를 좌/우 로 스크롤 할때 사용 할때 사용하는 클래스이다.
  • 안드로이드 기본으로 지원하는 클래스는 아니지만 안드로이드 제공하는 ‘Compatibility Package Revison 3’ Support 라이브러리에 포함 되어 안드로이드 1.6 이후 버젼 이후에는 사용이 가능하다.

<viewpager_gallery_sample.xml>
[php]
<android.support.v4.view.ViewPager
android:id="@+id/viewpagerSample"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:paddingLeft="24dp"
android:paddingRight="24dp" />
[/php]
<step7/ViewPagerGallerySample>
[php]
mPager = (ViewPager) findViewById(R.id.viewpagerSample);
mAdapter = new ViewPagerGalleryAdapter(this, images); (1)
mPager.setAdapter(mAdapter); (2)
// 이전, 다음의 이미지를 보여주기 위함
mPager.setClipToPadding(false); (3)
mPager.setPageMargin(12); (4)
[/php]

번호 설명
(1) 어댑터 생성, context 객체와 이미지 배열을 넘겨준다.
(2) View pager에 어댑터 연결
(3) 이전 다음 이미지를 프리뷰 하기 위한 설정
(4) 이전 다음 이미지를 프리뷰 하기 위한 설정

<step7/ViewPagerGalleryAdapter>
[php]
public class ViewPagerGalleryAdapter extends PagerAdapter{ (1)
Context context;
int[] images;
LayoutInflater inflater;
// 이전, 다음 이미지를 보여주기 위해 각 페이지의 가로 길이 지정
@Override
public float getPageWidth(int position) { (2)
return 0.93f;
}
public ViewPagerGalleryAdapter(Context ctx, int[] imgs) {
context = ctx;
images = imgs;
inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return images.length;
}

@Override
public boolean isViewFromObject(View view, Object object) { (3)
return view == ((LinearLayout) object);
}
// ViewPager의 getCount()에서 얻어온 Count의 Position별로 Pager에 등록할 item을 처리 할 수 있는 메서드, ViewPager에서 사용할 뷰객체를 생성하는 작업
@Override
public Object instantiateItem(ViewGroup container, int position) { (4)
View itemView = inflater.inflate(R.layout.viewpager_sample_item, container, false);
ImageView imgView =
(ImageView) itemView.findViewById(R.id.viewPagerSampleImgView);
imgView.setImageResource(images[position]);
((ViewPager) container).addView(itemView);
return itemView;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) { (5)
// Remove viewpager_item.xml from ViewPager
((ViewPager) container).removeView((LinearLayout) object);

}

}
[/php]

번호 설명
(1) PagerAdapter 상속
(2) 이전, 다음 페이지의 미리보기를 위한 가로길이 설정
(3) instantiateItem메소드에서 생성한 객체를 이용할 것인지 여부를 반환 한다.
(4) ViewPager에서 사용할 뷰객체 생성 및 등록 한다.
(5) View 객체를 삭제 한다.


그림 42. ViewPager를 이용한 Gallery

9.4. 커스텀 그리드 뷰

<custom_gridview_sample.xml>
[php]
<GridView
android:id="@+id/customGridViewSample"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:columnWidth="65dp"
android:gravity="center"
android:numColumns="auto_fit" (1)
android:stretchMode="columnWidth" >
</GridView>
[/php]

번호 설명
(1) GridView의 행에 나열할 컬럼의 개수(1~n개)를 지정한다. 'auto_fit'으로 설정하면 사용 가능한 공간에 따라 자동으로 컬럼의 개수가 정해진다.

<step7/CustomGridViewSample>
[php]
// 어댑터 생성 및 연결
adapter =
new CustomGridViewSampleAdapter(this, R.layout.custom_gridview_sample_rowgrid, gridArray);(1)
gridView.setAdapter(adapter);
[/php]

번호 설명
(1) 어댑터 생성, context 객체, 표현할 layout, 데이터를 보낸다.

<step7/CustomGridViewSampleAdapter>
[php]
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView; (1)

if(row == null) { (2)
// inflater로 xml 리소스를 가져와서 뷰 객체를 만드는 역할을 한다.
row = inflater.inflate(layoutResource, null);
}
GridViewItem item = data.get(position);

TextView title = (TextView) row.findViewById(R.id.customGridViewSampleTextView);
ImageView icon = (ImageView) row.findViewById(R.id.customGridViewSampleImgView);

title.setText(item.getTitle());
icon.setImageResource(item.getImageResourceId());
return row;
}
[/php]

번호 설명
(1) convertView는 뷰를 재사용할 수 있도록 도와주는 객체
(2) 만들어진 뷰가 없으면 뷰를 생성


그림 43. 커스텀 그리드 뷰

9.5. 커스텀 리스트 뷰

<custom_listview_sample.xml>
[php]
<ListView
android:id="@+id/customListViewSample"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
[/php]
<step7/CustomListViewSample>
[php]
// 어댑터 생성 및 연결
adapter = new CustomListViewSampleAdapter(CustomListViewSample.this,
R.layout.custom_listview_sample_item, data);
listView.setAdapter(adapter);

<step7/CustomListViewSampleAdapter>
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(view == null) {
// xml에 있는 정보 가져와서 view객체로 만들어준다.
view = inflater.inflate(resource, null);
}

ImageView img = (ImageView) view.findViewById(R.id.customListViewSampleImgView);
TextView text = (TextView) view.findViewById(R.id.customListViewSampleTextView);

img.setImageResource(item.get(position).getImageResource());
text.setText(item.get(position).getContents());

return view;
}
[/php]

그림 44. 커스텀 리스트 뷰