Transcript Document

Do It! 안드로이드 앱 프로그래밍
둘째 마당 - Chapter 04
선택위젯의 사용과 커스텀뷰 만들기
Jun. 2013
이지스퍼블리싱(주) 제공 강의 교안
저자 : 정재곤
이번 장에서는 무엇을 다룰까요?
아이콘이 들어간 리스트가 포함된 화면을 만들 때는 어떻게 하나요?
• 나인패치 이미지에 대해 알아볼까요?
• 이미지로 보여지는 비트맵 버튼을 직접 만들어 볼까요?
• 리스트뷰가 포함된 화면을 만들어 볼까요?
• 콤보박스처럼 사용되는 스피너가 포함된 화면을 만들어 볼까요?
• 사진을 선택할 때 사용할 수 있는 갤러리를 사용해 볼까요?
• 테이블 모양으로 보여줄 수 있는 그리드뷰를 사용해 볼까요?
• 여러 개의 위젯이 들어있는 복합 위젯을 직접 만들어 볼까요?
• 달력 모양의 월별 캘린더를 만들어 볼까요?
• 두 손가락으로 이미지를 다루는 멀티 터치 이미지 뷰어를 만들어 볼까요?
-3-
이번 장에서는 무엇을 다룰까요?
-4-
강의 주제 및 목차
강의 주제
대표적인 선택 위젯의 이해와 실습
목 차
1
나인패치 이미지
6
그리드뷰 사용하기
2
비트맵 버튼 만들기
7
복합위젯 만들기
3
리스트뷰 사용하기
8
월별 캘린더 만들기
4
스피너 사용하기
9
멀티터치 이미지뷰어 만들기
5
갤러리 사용하기
-5-
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
1.
나인패치 이미지
나인패치(Nine Patch) 이미지란?
• 이미지가 늘어나거나 줄어들 때 생기는 이미지 왜곡을 해결하는 방법을 정의한 것
• 서로 다른 해상도를 가진 여러 단말에 dp 단위로 뷰의 크기를 맞추다 보면 이미지
크기가 자동 조절되면서 왜곡되는 현상 발생  나인패치 이미지로 해결
[이미지의 크기에 따른 원본 이미지의 왜곡]
1. 나인패치 이미지
[이미지의 크기를 늘릴 때 왜곡되는 영역]
-7-
나인패치 이미지 예제
나인패치 이미지 예제
-일반 이미지와 나인패치 이미지 비교
-레이아웃에 여러 개의 버튼 추가
메인 액티비티의
XML 레이아웃
-일반 버튼 추가
1. 나인패치 이미지
메인 액티비티의
XML 레이아웃
-나인패치 버튼 추가
-8-
XML 레이아웃 조정
• 일반 이미지를 배경으로 사용하는 버튼과 나인패치 이미지를 배경으로 사용하는 버튼 추가
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Small"
1 일반 이미지를 배경으로 만든
작은 버튼 정의
android:textColor="#ffffffff"
android:background="@drawable/button_image_01"
/>
Continued..
1. 나인패치 이미지
-9-
XML 레이아웃 조정 (계속)
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
2 일반 이미지를 배경으로 만든
중간 버튼 정의
android:text="MediumMediumMedium"
android:textColor="#ffffffff"
android:background="@drawable/button_image_01"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LongLongLongLongLongLongLongLongLong"
android:textColor="#ffffffff"
android:background="@drawable/button_image_01"
/>
…
1. 나인패치 이미지
- 10 -
3 일반 이미지를 배경으로 만든
큰 버튼 정의
실행 화면
• 일반 이미지를 배경으로 사용하는 버튼은 모서리 부분의 이미지 깨짐 현상 발생
• 나인패치 이미지를 배경으로 사용하는 버튼은 크게 이상하게 보이지 않음
[Reference]
void setBackgroundColor (int color)
void setBackgroundDrawable (Drawable d)
void setBackgroundResource (int resid)
[일반 이미지와 나인패치 이미지를 적용한 버튼 모양 비교]
1. 나인패치 이미지
- 11 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
2.
비트맵 버튼 만들기
비트맵 버튼 만들기
• 비트맵 버튼을 직접 만들어 normal일 경우와 clicked일 경우의 이미지를 표시
• 나인패치 이미지를 적용하는 대표적인 경우가 바로 버튼인데 이렇게 배경 부분을 이미지로
지정하여 만든 버튼은 아무리 눌러도 이미지의 변화가 없어 사용자가 버튼을 눌렀는지 안 눌렀는지
알 수 없다는 단점이 있음
• 비트맵 이미지를 이용해 버튼의 상태를 표시하려면 버튼이 눌렸을 때와 떼어졌을 때를 이벤트로
구분하여 처리함
[Reference]
public void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
public void onDraw(Canvas canvas)
[Reference]
void setMeasuredDimension (int measuredWidth, int measuredHeight)
2. 비트맵 버튼 만들기
- 13 -
뷰 위에 그래픽을 그리는 과정
• 뷰에 그래픽이 그려질 때 onDraw() 메소드 호출됨
• 다시 그리기는 invalidate() 메소드 사용
2. 비트맵 버튼 만들기
- 14 -
비트맵 버튼 만들기 예제
비트맵 버튼 만들기 예제
-이미지를 보여주는 비트맵 버튼
버튼을 상속한
새로운 클래스 정의
-새로운 버튼 클래스 정의
XML 레이아웃에 추가
-새로운 버튼 태그를 XML 레이
아웃에 추가
메인 액티비티 코드 작성
-메인 액티비티 코드에서 참조하여 사용
2. 비트맵 버튼 만들기
- 15 -
새로운 버튼 클래스 정의
• 터치 이벤트에 따라 배경 이미지를 바꾸어주는 버튼 클래스 정의
public TitleButton(Context context, AttributeSet atts) {
super(context, atts);
this.context = context;
init();
}
public void init() {
setBackgroundResource(R.drawable.title_button);
paint = new Paint();
paint.setColor(defaultColor);
1 초기화 메소드를 정의하는 코드로 배경 이미지
설정과 텍스트를 위한 페인트 객체 생성
paint.setAntiAlias(true);
paint.setTextScaleX(defaultScaleX);
paint.setTextSize(defaultSize);
paint.setTypeface(defaultTypeface);
}
Continued..
2. 비트맵 버튼 만들기
- 16 -
새로운 버튼 클래스 정의 (계속)
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_DOWN:
Toast.makeText(context, titleText, Toast.LENGTH_SHORT).show();
2 터치 이벤트를 처리하여 버튼이 눌렸을 때
토스트 메시지 표시
break;
}
invalidate();
return true;
}
Continued..
2. 비트맵 버튼 만들기
- 17 -
새로운 버튼 클래스 정의 (계속)
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
int curWidth = getWidth();
int curHeight = getHeight();
if (paintChanged) {
4
paint.setColor(defaultColor);
뷰가 화면에 그려질 때 호출되는
onDraw() 메소드 다시 정의
paint.setTextScaleX(defaultScaleX);
paint.setTextSize(defaultSize);
paint.setTypeface(defaultTypeface);
}
Rect bounds = new Rect();
paint.getTextBounds(titleText, 0, titleText.length(), bounds);
float textWidth = ((float)curWidth - bounds.width())/2.0F;
5
텍스트의 크기 계산
float textHeight = ((float)(curHeight-4) + bounds.height())/2.0F-1.0F;
Continued..
2. 비트맵 버튼 만들기
- 18 -
새로운 버튼 클래스 정의 (계속)
6 캔버스 객체를 이용해 텍스트
그리기
canvas.drawText(titleText, textWidth, textHeight, paint);
}
...
public void setDefaultColor(int defaultColor) {
this.defaultColor = defaultColor;
7
paintChanged = true;
}
...
}
2. 비트맵 버튼 만들기
- 19 -
색상 값 설정 메소드 정의
버튼 중앙에 텍스트를 그리는 계산
{(버튼의 폭 + 높이값) - (텍스트의 폭 + 높이값)}/2
• 새로 정의한 뷰 자체의 크기는 getWidth()와 getHeight() 메소드를 이용해 가져올 수 있으므로
버튼의 중앙에 텍스트를 그려주기 위해 버튼의 폭과 높이값에서 텍스트의 폭과 높이값을 뺀 후
절반으로 나누는 방식으로 텍스트의 위치를 조정함
2. 비트맵 버튼 만들기
- 20 -
레이아웃 만들기
<org.androidtown.ui.TitleButton
android:id="@+id/titleBtn"
android:layout_width="match_parent"
1 타이틀 영역을 보여주는 버튼 추가
android:layout_height=“22dp"
android:layout_marginBottom=“4dp"
/>
<org.androidtown.ui.TitleBitmapButton
android:id="@+id/arrowLeftBtn"
android:layout_width=“20dp"
android:layout_height=“40dp"
2
android:layout_alignParentLeft="true"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
/>
2. 비트맵 버튼 만들기
- 21 -
타이틀 영역 안에 작은 버튼 추가
메인 액티비티 코드 만들기
package org.androidtown.ui.bitmap.widget;
...
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
TitleBitmapButton arrowLeftBtn = (TitleBitmapButton)findViewById(R.id.arrowLeftBtn);
1 화면의 기본 타이틀
없애기
Continued..
2. 비트맵 버튼 만들기
- 22 -
메인 액티비티 코드 만들기 (계속)
arrowLeftBtn.setBitmapResource(R.drawable.arrow_left, R.drawable.arrow_left_clicked);
arrowLeftBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
2 [비트맵] 버튼에서 사용
되는 이미지 설정
}
});
String titleText = "비트맵 Title";
TitleButton titleBtn = (TitleButton)findViewById(R.id.titleBtn);
titleBtn.setTitleText(titleText);
titleBtn.setDefaultSize(18F);
}
3 [비트맵] 버튼에 표시되는
텍스트 설정
}
[[비트맵] 버튼을 이용한 상단 타이틀 표시]
2. 비트맵 버튼 만들기
- 23 -
Selector를 이용하는 방법
• Selector를 이용해 버튼의 상태에 따라 이미지를 바꾸어 주는 방법도 적용 가능
• normal 상태와 selected 상태를 구분하여 XML로 정의 후 background 속성으로 적용
<Button
<?xml version="1.0" encoding="UTF-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:state_pressed="false"
android:drawable="@drawable/arrow_left_clicked"
/>
<item
android:state_focused="true"
android:state_pressed="true"
android:drawable="@drawable/arrow_left_clicked"
/>
<item
android:state_focused="false"
android:state_pressed="true"
android:drawable="@drawable/arrow_left_clicked"
/>
<item
android:drawable="@drawable/arrow_left_normal"
/>
</selector>
android:id="@+id/arrowLeftBtn"
android:layout_width="46dp"
android:layout_height="74dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:background="@drawable/button_selector"
/>
2. 비트맵 버튼 만들기
- 24 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
3.
리스트뷰 사용하기
왜 굳이 선택위젯이라는 이름으로 구분할까?
• 안드로이드에서는 여러 아이템 중의 하나를 선택하는 선택위젯은 별도의 패턴을 사용함
• 여러 개의 아이템 중에서 하나를 선택하는 방식의 선택 위젯은 어댑터를 사용하여야 함
• 이 어댑터에서 데이터를 관리하도록 해야 할 뿐만 아니라 화면에 보여지는 뷰도 어댑터의 getView()
메소드에서 결정함
• 선택위젯의 가장 큰 특징은 원본 데이터를 위젯에 직접 설정하지 않고 어댑터라는 클래스를
사용하도록 되어 있다는 점으로 이 패턴을 잘 기억해 두어야 함
[선택 위젯과 어댑터]
3. 리스트뷰 사용하기
- 26 -
대표적인 선택 위젯
• 안드로이드에서 제공하는 대표적인 선택 위젯들은 그 사용방법을 잘 알아두어야 함
3. 리스트뷰 사용하기
- 27 -
리스트뷰로 보여줄 때 해야 할 일들
(1) 아이템을 위한 XML 레이아웃 정의하기
- 리스트뷰에 들어갈 각 아이템의 레이아웃을 XML로 정의함
(2) 아이템을 위한 뷰 정의하기
- 리스트뷰에 들어갈 각 아이템을 하나의 뷰로 정의. 이 뷰는 여러 개의 뷰를 담고 있는 뷰그룹이어야 함
(3) 어댑터 정의하기
- 데이터 관리 역할을 하는 어댑터 클래스를 만들고 그 안에 각 아이템으로 표시할 뷰를 리턴하는 getView()
메소드를 정의함
(4) 리스트뷰 정의하기
- 화면에 보여줄 리스트뷰를 만들고 그 안에 데이터가 선택되었을 때 호출될 리스너 객체를 정의함
3. 리스트뷰 사용하기
- 28 -
리스트뷰 사용하기 예제
리스트뷰 사용하기 예제
-아이콘이 들어 있는 리스트뷰의 아이템 정의
-리스트뷰의 한 아이템을 선택했을 때 토스트 표시
아이템의
XML 레이아웃
한 아이템의 데이터를
넣을 클래스
-한 아이템으로 보여질 뷰의
-한 아이템의 데이터를 넣을 클
XML 레이아웃 정의
래스 정의
리스트뷰를 사용하는
메인 액티비티 코드 작성
리스트뷰 선택 이벤트
처리 코드 작성
-리스트뷰를 보여주는 역할을 하
-리스트뷰를 사용하는 메인 액티
-한 아이템을 선택했을 때의 이
는 어댑터 클래스 정의
비티 코드 작성
벤트 처리
어댑터 클래스 정의
3. 리스트뷰 사용하기
- 29 -
한 아이템으로 보여줄 XML 레이아웃 정의
• 선택위젯에서 각각의 아이템은 동일한 레이아웃을 가진 뷰가 반복적으로 보여짐
• 각각의 아이템을 위한 XML 레이아웃이 필요함
[리스트뷰의 한 아이템이 갖는 레이아웃의 예]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
Continued..
3. 리스트뷰 사용하기
- 30 -
한 아이템으로 보여줄 XML 레이아웃 정의 (계속)
<ImageView
android:id="@+id/iconItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dip"
android:layout_gravity="center_vertical"
/>
<LinearLayout
1 왼쪽에 보이는 이미지뷰
정의
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentLeft="true"
>
Continued..
3. 리스트뷰 사용하기
- 31 -
한 아이템으로 보여줄 XML 레이아웃 정의 (계속)
<TextView
android:id="@+id/dataItem01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
2 첫 번째 줄의 텍스트뷰
정의
android:textSize=“12dp"
android:padding=“4dp"
/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding=“4dp"
>
3 두 번째 줄을 표시할 상대
레이아웃 정의
Continued..
3. 리스트뷰 사용하기
- 32 -
한 아이템으로 보여줄 XML 레이아웃 정의 (계속)
<TextView
android:id="@+id/dataItem02"
android:layout_width="wrap_content"
4
텍스트뷰 정의
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/dataItem03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textColor="#ccf88107"
android:textSize=“14dp"
5 두 번째 줄의 오른쪽에 있는
텍스트뷰 정의
android:textStyle="bold"
android:paddingRight="4dp"
/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
3. 리스트뷰 사용하기
- 33 -
한 아이템으로 보여줄 뷰 정의
• 어댑터의 getView() 메소드에서 리턴해 줄 뷰를 별도의 클래스로 정의하여 사용
public class IconTextView extends LinearLayout {
…
LayoutInflater inflater = (LayoutInflater)
1
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.listitem, this, true);
mIcon = (ImageView) findViewById(R.id.iconItem);
아이템의 모양을 구성한
XML 레이아웃을 인플레이
터를 이용해 메모리 객체화
2 레이아웃에 정의된 이미지뷰
객체 참조
mIcon.setImageDrawable(aItem.getIcon());
mText01 = (TextView) findViewById(R.id.dataItem01);
3 레이아웃에 정의된 텍스트뷰
객체 중에 첫 번째 것 참조
mText01.setText(aItem.getData(0));
mText02 = (TextView) findViewById(R.id.dataItem02);
mText02.setText(aItem.getData(1));
mText03 = (TextView) findViewById(R.id.dataItem03);
mText03.setText(aItem.getData(2));
}
3. 리스트뷰 사용하기
- 34 -
한 아이템으로 보여줄 데이터 클래스 정의
• 각각의 아이템을 위한 데이터도 별도의 클래스로 정의하여 사용
public class IconTextItem {
private Drawable mIcon;
private String[] mData;
1 리스트뷰의 한 아이템에 표시할 데이터를
담고 있을 클래스 정의
2 Drawable 타입의 변수와 문자열
타입의 배열 변수 선언
public IconTextItem(Drawable icon, String[] obj) {
3 Drawable 객체와 문자열 타입의 배열을
파라미터로 전달받는 생성자
mIcon = icon;
mData = obj;
}
3. 리스트뷰 사용하기
- 35 -
새로운 어댑터 클래스 정의
public class IconTextListAdapter extends BaseAdapter {
private Context mContext;
1 BaseAdapter를 상속하여 새로운
어댑터 클래스 정의
private List<IconTextItem> mItems = new ArrayList<IconTextItem>();
2
public IconTextListAdapter(Context context) {
각 아이템의 데이터를 담고 있는
IconTextItem 객체를 저장할
ArrayList 객체 생성
mContext = context;
}
…
public int getCount() {
3 전체 아이템의 개수를 리턴하는
메소드 정의
return mItems.size();
}
…
public Object getItem(int position) {
return mItems.get(position);
}
…
Continued..
3. 리스트뷰 사용하기
- 36 -
새로운 어댑터 클래스 정의 (계속)
public View getView(int position, View convertView, ViewGroup parent) {
IconTextView itemView;
if (convertView == null) {
itemView = new IconTextView(mContext);
} else {
itemView = (IconTextView) convertView;
}
4 아이템에 표시할 뷰 리턴하는
메소드 정의
itemView.setIcon(mItems.get(position).getIcon());
itemView.setText(0, mItems.get(position).getData(0));
itemView.setText(1, mItems.get(position).getData(1));
itemView.setText(2, mItems.get(position).getData(2));
return itemView;
}
}
3. 리스트뷰 사용하기
- 37 -
getView() 메소드
[Reference]
public View getView (int position, View convertView, ViewGroup parent)
• 첫 번째 파라미터 - 아이템의 인덱스를 의미하는 것으로 리스트뷰에서 보일 아이템의 위치 정보라 할 수 있음.
0부터 시작하여 아이템의 개수만큼 파라미터로 전달됨
• 두 번째 파라미터 - 현재 인덱스에 해당하는 뷰 객체를 의미하는데 안드로이드에서는 선택 위젯이 데이터가 많아
스크롤될 때 뷰를 재활용하는 메커니즘을 가지고 있어 한 번 만들어진 뷰가 화면 상에 그대로
다시 보일 수 있도록 되어 있음. 따라서 이 뷰가 널값이 아니면 재활용 가능함
• 세 번째 파라미터 - 부모 컨테이너 객체임
3. 리스트뷰 사용하기
- 38 -
메인 액티비티 코드 만들기
• 데이터를 어댑터에 추가하고 아이템 선택 시의 이벤트 처리
package org.androidtown.ui.listview;
...
public class SampleListViewActivity extends Activity {
DataListView list;
IconTextListAdapter adapter;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
list = new DataListView(this);
adapter = new IconTextListAdapter(this);
Resources res = getResources();
3. 리스트뷰 사용하기
2
리스트뷰 객체 생성
3
어댑터 객체 생성
1 자바 코드에서 만든 리스트뷰
객체를 부모 레이아웃에 추가할
때 필요한 파라미터
Continued..
- 39 -
메인 액티비티 코드 만들기 (계속)
adapter.addItem(new IconTextItem(res.getDrawable(R.drawable.icon05),
"추억의 테트리스", "30,000 다운로드", "900 원"));
list.setAdapter(adapter);
4 어댑터에 각 아이템의
데이터 추가
5 리스트뷰에 어댑터
객체 설정
list.setOnDataSelectionListener(new OnDataSelectionListener() {
public void onDataSelected(AdapterView parent,View v,int position, long id) {
IconTextItem curItem = (IconTextItem) adapter.getItem(position);
6 아이템을 클릭했을 때
토스트 메시지를 보여주
도록 리스너 설정
String[] curData = curItem.getData();
Toast.makeText(getApplicationContext(), "Selected : " + curData[0], Toast.LENGTH.SHORT).show();
}
});
setContentView(list, params);
}
7 액티비티 화면을 리스트뷰로
설정
}
3. 리스트뷰 사용하기
- 40 -
실행 화면
3. 리스트뷰 사용하기
- 41 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
4.
스피너 사용하기
스피너 사용하기 예제
스피너 사용하기 예제
-콤보박스처럼 선택할 수 있는 스피너 사용
-XML 레이아웃에 정의한 스피너 참조
메인 액티비티의
XML 레이아웃
-스피너를 포함하는 메인 화면의
메인 액티비티 코드 작성
-스피너 객체를 참조하여 설정
XML 레이아웃 정의
4. 스피너 사용하기
- 43 -
XML 레이아웃 만들기
• <Spinner> 태그를 사용하여 레이아웃에 추가
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
1
android:layout_height="wrap_content"
/>
4. 스피너 사용하기
- 44 -
스피너 정의
메인 액티비티 코드 만들기
• 안드로이드에서 미리 만들어 제공하는 어댑터를 사용할 수 있음
package org.androidtown.ui.spinner;
...
public class MainActivity extends Activity implements AdapterView.OnItemSelectedListener {
TextView selection;
String[] items = { "mike", "angel", "crow", "john", "ginnie", "sally",
"cohen", "rice" };
1 스피너에 표시될 아이템들의
데이터를 배열로 정의
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
selection = (TextView) findViewById(R.id.selection);
Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
Continued..
4. 스피너 사용하기
- 45 -
메인 액티비티 코드 만들기 (계속)
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(
2 ArrayAdapter를 이용해 어댑터
객체 생성
android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
3
스피너에 어댑터 설정
}
public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
selection.setText(items[position]);
}
public void onNothingSelected(AdapterView<?> parent) {
selection.setText("");
}
}
4. 스피너 사용하기
- 46 -
4
스피너의 아이템이
선택되었을 때 처리하는
메소드 정의
ArrayAdapter의 사용
[Reference]
public ArrayAdapter (Context context, int textViewResourceId, T[] objects)
• 첫 번째 파라미터는 Context 객체이므로 액티비티인 this를 전달하면 됨
• 두 번째 파라미터는 뷰를 초기화할 때 사용되는 XML 레이아웃의 리소스 ID 값으로 이 코드에서는
android.R.layout.simple_spinner_item을 전달하였음
• 세 번째 파라미터는 아이템으로 보일 문자열 데이터들의 배열임
[스피너를 이용한 아이템 선택]
4. 스피너 사용하기
- 47 -
하나의 문자열을 보여주는 간단한 리스트뷰 사용
• 리스트뷰의 경우에도 안드로이드에서 미리 제공하는 어댑터 사용 가능
package org.androidtown.ui.spinner;
...
public class MainActivity extends ListActivity {
TextView selection;
String[] items = { "mike", "angel", "crow", "john",
1 리스트에 표시될 아이템들의
데이터를 배열로 정의
"ginnie", "sally", "cohen", "rice" };
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Continued..
4. 스피너 사용하기
- 48 -
하나의 문자열을 보여주는 간단한 리스트뷰 사용 (계속)
setListAdapter(new ArrayAdapter<String>(
this,
2 ArrayAdapter를 이용해
어댑터 객체를 생성한 후
리스트에 설정
android.R.layout.simple_list_item_1,
items));
selection = (TextView)findViewById(R.id.selection);
}
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
String text = " position:" + position + " " + items[position];
selection.setText(text);
}
3 리스트의 아이템이
선택되었을 때 처리
하는 메소드 정의
}
[리스트뷰에 단순 문자열만 표시한 경우]
4. 스피너 사용하기
- 49 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
5.
갤러리 사용하기
갤러리 사용하기 예제
갤러리 사용하기 예제
-콤보박스처럼 선택할 수 있는 스피너 사용
-XML 레이아웃에 정의한 스피너 참조
메인 액티비티의
XML 레이아웃
-갤러리를 포함하는 메인 화면의
메인 액티비티 코드 작성
-갤러리 객체를 참조하여 설정
XML 레이아웃 정의
어댑터 클래스 정의
이미지뷰를 위한 액티비티
정의
-갤러리를 위한 어댑터 클래스 정의 -갤러리의 아이템 선택 시 보여
5. 갤러리 사용하기
줄 액티비티 정의
- 51 -
레이아웃 만들기
• 갤러리(Gallery)는 사진을 나열해서 볼 수 있도록 하는 간단한 위젯
• 갤러리는 사진을 일렬로 보여주고 이렇게 나열된 사진을 스크롤하거나 선택할 수 있도록 만들어 줌
• 갤러리는 <Gallery> 태그를 이용해 XML 레이아웃에 추가할 수 있음
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Gallery
android:id="@+id/gallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:verticalSpacing="10dp"
1
갤러리 정의
android:horizontalSpacing="10dp"
/>
</LinearLayout>
5. 갤러리 사용하기
- 52 -
메인 액티비티 코드 만들기
• 앨범에 있는 사진을 갤러리로 보여주기 위해 내용 제공자 사용
private void init() {
String[] img = { MediaStore.Images.Thumbnails._ID };
imageCursor = managedQuery(
MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
img, null, null, MediaStore.Images.Thumbnails.IMAGE_ID + "");
imageColumnIndex = imageCursor.getColumnIndexOrThrow(
1 앨범 사진들에 대한 썸네일 이미지를
쿼리하여 Cursor 객체로 받음
MediaStore.Images.Thumbnails._ID);
count = imageCursor.getCount();
gallery = (Gallery) findViewById(R.id.gallery);
gallery.setAdapter(new ImageAdapter(getApplicationContext()));
2 갤러리 객체에 ImageAdapter
객체 설정
gallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent,View v,int position, long id) {
System.gc();
3 갤러리의 아이템을 클릭했을 때
의 리스너 설정
String[] proj = { MediaStore.Images.Media.DATA };
5. 갤러리 사용하기
Continued..
- 53 -
메인 액티비티 코드 만들기 (계속)
actualImageCursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj,
null, null, null);
actualImageColumnIndex = actualImageCursor.getColumnIndexOrThrow(
MediaStore.Images.Media.DATA);
4
앨범에 있는 원본 이미지를
쿼리하여 Cursor 객체로 받음
actualImageCursor.moveToPosition(position);
String filename = actualImageCursor.getString(actualImageColumnIndex);
System.gc();
Intent intent = new Intent(getApplicationContext(), OriginalPictureView.class);
intent.putExtra("filename", filename);
5
startActivity(intent);
}
이미지를 보기 위한
액티비티 띄우기
});
}
Continued..
5. 갤러리 사용하기
- 54 -
메인 액티비티 코드 만들기 (계속)
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
6 ImageAdapter 클래스 정의
mContext = c;
}
public int getCount() {
return count;
}
public Object getItem(int position) {
return position;
7 어댑터에서 리턴할 전체 아이템의 개수는
썸네일 이미지의 개수로 함
}
public long getItemId(int position) {
return position;
}
Continued..
5. 갤러리 사용하기
- 55 -
메인 액티비티 코드 만들기 (계속)
public View getView(int position,View convertView,ViewGroup parent) {
System.gc();
ImageView imgView = new ImageView(mContext.getApplicationContext());
if (convertView == null) {
8 아이템으로 보일 뷰 객체를
리턴하는 getView() 메소드
정의
imageCursor.moveToPosition(position);
int id = imageCursor.getInt(imageColumnIndex);
imgView.setImageURI(Uri.withAppendedPath(
MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, ""+id));
imgView.setScaleType(ImageView.ScaleType.FIT_XY);
imgView.setLayoutParams(new Gallery.LayoutParams(136, 88));
} else {
imgView = (ImageView) convertView;
}
return imgView;
}
}
}
5. 갤러리 사용하기
- 56 -
9 썸네일 이미지를 이미지뷰에
설정
이미지를 보기 위한 액티비티의 레이아웃
• 갤러리에서 선택한 이미지를 보기 위한 액티비티의 레이아웃 정의
[Reference]
MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
1
이미지뷰 정의
/>
</LinearLayout>
5. 갤러리 사용하기
Continued..
- 57 -
이미지를 보기 위한 액티비티 코드
• 메인 액티비티로부터 이미지에 대한 정보를 전달받아 비트맵 객체로 로딩 후 설정
private void processIntent() {
System.gc();
1 메인 액티비티로부터
전달받은 인텐트 처리
Intent intent = getIntent();
Bundle extras = intent.getExtras();
filename = extras.getString("filename");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
3 이미지 파일을 읽어 들여
비트맵 객체로 만듦
4 이미지뷰에 비트맵
객체 설정
imgView.setImageBitmap(bitmap);
}
}
5. 갤러리 사용하기
2 이미지를 메모리로 로딩할 때의
축소 비율을 1/2로 지정
- 58 -
실행 화면
5. 갤러리 사용하기
- 59 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
6.
그리드뷰 사용하기
그리드뷰 사용하기 예제
그리드뷰 사용하기 예제
-테이블 형태로 보여주는 데이터를 관리할 어댑터 정의
-테이블 모양의 뷰 생성
어댑터 클래스 정의
메인 액티비티 코드 작성
-그리드뷰를 위한 어댑터 클래스
- 그리드뷰와 어댑터를 이용해
정의
화면에 표시
6. 그리드뷰 사용하기
- 61 -
어댑터 클래스 정의
• 테이블 모양으로 데이터를 보여주기 위해 그리드뷰 위젯을 사용
• 이전의 윈도우 모바일 기반 PDA나 PC 또는 웹에서 많이 사용하는 것임
• 테이블(Table) 형태로 데이터를 보여주는 위젯
package org.androidtown.ui.gridview;
...
public class DataAdapter extends BaseAdapter {
Context mContext;
Continued..
6. 그리드뷰 사용하기
- 62 -
어댑터 클래스 정의 (계속)
private MTable mTable;
...
public DataAdapter(Context context) {
super();
mContext = context;
1 가상 테이블 객체 선언
}
public DataAdapter(Context context, MTable table) {
super();
mContext = context;
setTable(table);
}
public int getNumColumns() {
if (mTable == null) {
return 0;
2 테이블의 칼럼 개수
정보 확인
}
return mTable.countColumn;
}
Continued..
6. 그리드뷰 사용하기
- 63 -
어댑터 클래스 정의 (계속)
public int getCount() {
if (mTable == null) {
return 0;
3
}
return (mTable.count()+1) * mTable.countColumn;
전체 아이템의 개수 확인
(칼럼 수 * 레코드 수)
}
public View getView(int position, View view, ViewGroup group) {
Log.d("DataAdapter", "getView(" + position + ") called.");
4 어댑터에서 사용되는
getView() 메소드 정의
if (mTable == null) {
return null;
}
Continued..
6. 그리드뷰 사용하기
- 64 -
어댑터 클래스 정의 (계속)
int rowIndex = position / mTable.countColumn;
int columnIndex = position % mTable.countColumn;
Log.d("DataAdapter", "Index : " + rowIndex + ", " + columnIndex);
...
5 position 값을 이용해 행과 열의
인덱스 값 계산
if (position < mTable.countColumn) {
itemView.setText(mTable.columns[columnIndex].name);
} else {
Object item = "";
try {
item = mTable.getCell(rowIndex-1, columnIndex);
6 position 값에 해당하는
셀의 값 확인
} catch(Exception ex) {
ex.printStackTrace();
}
itemView.setText(item.toString());
}
...
return container;
}
Continued..
6. 그리드뷰 사용하기
- 65 -
전체 아이템 개수 계산
getCount() 리턴 값 = 칼럼 개수 * 레코드의 개수
public void onItemClick(AdapterView parent,View v,int position, long id) {
if (selectionListener == null) {
return;
}
int countColumn = 0;
if (adapter != null) {
if (adapter.getTable() != null) {
countColumn = adapter.getTable().countColumn;
}
}
int rowIndex = -1;
int columnIndex = -1;
if (countColumn > 0) {
rowIndex = position / countColumn;
4 행과 열의 인덱스를 계산하여
새로 정의한 리스너 호출
columnIndex = position % countColumn;
}
selectionListener.onDataSelected(parent, v, rowIndex, columnIndex, id);
6. 그리드뷰 사용하기
- 66 -
메인 액티비티 코드 만들기
grid = new DataGridView(this);
grid.setColumnWidth(100);
1
adapter = new DataAdapter(this);
새로 정의한
DataGridView 객체
생성
MTable table = null;
try {
table = createSampleTable();
} catch(MTableException ex) {
ex.printStackTrace();
2
새로 정의한
DataAdapter 객체
생성
}
adapter.setTable(table);
grid.setAdapter(adapter);
3 어댑터에 가상 테이블
객체 설정
Continued..
6. 그리드뷰 사용하기
- 67 -
메인 액티비티 코드 만들기
grid.setOnDataSelectionListener(new OnDataSelectionListener() {
public void onDataSelected(AdapterView parent,View v,int row,
int column, long id) {
Toast.makeText(getApplicationContext(), "Selected : " + row + ", " + column,
Toast.LENGTH_LONG).show();
}
});
setContentView(grid, params);
}
...
}
[그리드뷰로 테이블 데이터를 보여준 경우]
6. 그리드뷰 사용하기
- 68 -
4 DataGridView 객체에
리스너 설정
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
7.
복합위젯 만들기
복합위젯 만들기 예제
복합위젯 만들기 예제
-여러 개의 뷰를 포함하는 복합 위젯
새로운 클래스를 위한
XML 레이아웃 정의
레이아웃을 상속하여
새로운 클래스 정의
-여러 개의 뷰를 포함하는 새로
-여러 개의 뷰를 포함하는 새로
운 클래스를 위한 레이아웃 정의
운 클래스 정의
메인 액티비티를 위한
XML 레이아웃 정의
-새로운 클래스 태그 추가
7. 복합위젯 만들기
메인 액티비티 코드 작성
-메인 액티비티 코드에서 참조하
여 사용
- 70 -
복합위젯 만들기
• 복합 위젯은 여러 개의 뷰를 조합하여 만든 새로운 위젯
- 뷰그룹에 여러 개의 뷰를 넣은 후 이벤트 처리
- 실무에 많이 사용하는 위젯 만들기 방법
• 날짜와 시간을 함께 선택할 수 있는 DateTimePicker 예제
- DatePicker와 TimePicker를 같이 포함한 복합 위젯
7. 복합위젯 만들기
- 71 -
날짜/시간 선택기능 위젯
• 날짜와 시간을 설정 위젯
- DatePicker와 TimePicker가 제공되며 대화상자로는 DatePickerDialog 와 TimePickerDialog가 제공됨
- DatePicker 와 DatePickerDialog 를 사용할 때는 시작 일자를 지정할 수 있으며, 그 포맷은 연, 월, 일
(year, month, day) 이 됨.
- 월 단위 값은 0부터 11까지 이며 각각 1월부터 12월까지를 표시함
(January –December)
• 리스너 설정
- 각각의 위젯을 사용자가 선택하면 OnDateChangedListener 또는 OnDateSetListener 콜백 객체가
호출됨.
• TimePicker 와 TimePickerDialog
- 초기 시간을 지정할 수 있으며 그 형태는 0부터 23까지의 시간 (hour :0 - 23) 과 0부터 59까지의
3분(minute :0 - 59)이 됨.
- 선택할 수 있는 값들이 12 시간 모드(AM/PM 토글)인지 또는 24시간 모드인지 지정할 수 있음.
- 사용자가 새로운 시간을 선택하면 OnTimeChangedListener 또는OnTimeSetListener 콜백 객체가
호출됨.
7. 복합위젯 만들기
- 72 -
DatePicker와 TimePicker 사용하기
7. 복합위젯 만들기
- 73 -
레이아웃 만들기
<DatePicker
android:id="@+id/datePicker"
1 날짜 선택 위젯 정의
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<CheckBox
android:id="@+id/enableTimeCheckBox"
android:layout_width="wrap_content"
2 시간 선택 위젯을 보일지 여부를
결정하는 체크박스 정의
android:layout_height="wrap_content"
android:text="시간 보이기"
/>
<TimePicker
android:id="@+id/timePicker"
android:layout_width="wrap_content"
3 시간 선택 위젯 정의
android:layout_height="wrap_content"
/>
7. 복합위젯 만들기
- 74 -
레이아웃을 상속한 새로운 클래스 정의
public class DateTimePicker extends LinearLayout {
1 리니어 레이아웃을 상속하여
새로운 클래스 정의
public DateTimePicker(Context context, AttributeSet attrs){
super(context, attrs);
LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.datetimepicker, this, true);
…
2 새로 정의한 클래스를
위한 레이아웃 인플레이
션
datePicker.init(_currentYear, _currentMonth, _currentDay,
new OnDateChangedListener() {
public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
if(onDateTimeChangedListener != null) {
onDateTimeChangedListener.onDateTimeChanged(
DateTimePicker.this, year, monthOfYear, dayOfMonth,
timePicker.getCurrentHour(), timePicker.getCurrentMinute());
}
}
3 날짜가 변경되는 이벤트가 발생했을 때
새로운 리스너의 메소드 호출
});
7. 복합위젯 만들기
Continued..
- 75 -
레이아웃을 상속한 새로운 클래스 정의 (계속)
enableTimeCheckBox = (CheckBox)findViewById(R.id.enableTimeCheckBox);
enableTimeCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
timePicker.setEnabled(isChecked);
timePicker.setVisibility(
(enableTimeCheckBox).isChecked()?View.VISIBLE:View.INVISIBLE);
}
});
timePicker = (TimePicker)findViewById(R.id.timePicker);
timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
if(onDateTimeChangedListener != null) {
onDateTimeChangedListener.onDateTimeChanged(
DateTimePicker.this, datePicker.getYear(),
datePicker.getMonth(), datePicker.getDayOfMonth(), hourOfDay, minute);
}
}
});
7. 복합위젯 만들기
4
시간이 변경되는 이벤트가
발생했을 때 새로운 리스너의
메소드 호출
Continued..
- 76 -
레이아웃을 상속한 새로운 클래스 정의 (계속)
timePicker.setCurrentHour(_currentHour);
timePicker.setCurrentMinute(_currentMinute);
timePicker.setEnabled(enableTimeCheckBox.isChecked());
timePicker.setVisibility((enableTimeCheckBox.isChecked()?View.VISIBLE:View.INVISIBLE));
}
public void setOnDateTimeChangedListener(OnDateTimeChangedListener
onDateTimeChangedListener) {
this.onDateTimeChangedListener = onDateTimeChangedListener;
}
public void updateDateTime(int year, int monthOfYear, int dayOfMonth,
int currentHour, int currentMinute){
datePicker.updateDate(year, monthOfYear, dayOfMonth);
timePicker.setCurrentHour(currentHour);
timePicker.setCurrentMinute(currentMinute);
}
public void updateDate(int year, int monthOfYear, int dayOfMonth){
datePicker.updateDate(year, monthOfYear, dayOfMonth);
}
...
}
7. 복합위젯 만들기
- 77 -
5 새로운 리스너 객체 설정
메소드 정의
XML 레이아웃 만들기
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
>
<TextView
android:id="@+id/text01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
1 변경된 날짜와 시간 값을
표시하는 텍스트뷰 추가
/>
<org.androidtown.ui.composite.DateTimePicker
android:id="@+id/dateTimePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
2 새로 정의한 DateTimePicker 뷰
사용을 위해 추가
/>
</LinearLayout>
7. 복합위젯 만들기
- 78 -
메인 액티비티 코드 만들기
package org.androidtown.ui.composite;
...
public class MainActivity extends Activity {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy년 MM월 dd일 HH시 mm분");
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text01 = (TextView)findViewById(R.id.text01);
final DateTimePicker dateTimePicker = (DateTimePicker)findViewById(R.id.dateTimePicker);
Continued..
7. 복합위젯 만들기
- 79 -
메인 액티비티 코드 만들기 (계속)
dateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year,
int monthOfYear, int dayOfYear, int hourOfDay, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, monthOfYear, dayOfYear, hourOfDay, minute);
text01.setText(dateFormat.format(calendar.getTime()));
1 DateTimePicker 객체
에 리스너 설정하여 날
짜나 시간이 변경될 때
마다 텍스트에 표시
}
});
Calendar calendar = Calendar.getInstance();
calendar.set(dateTimePicker.getYear(), dateTimePicker.getMonth(),
dateTimePicker.getDayOfMonth(), dateTimePicker.getCurrentHour(),
dateTimePicker.getCurrentMinute());
text01.setText(dateFormat.format(calendar.getTime()));
2 텍스트에 현재 날짜와
시간 정보 표시
}
}
7. 복합위젯 만들기
- 80 -
실행 화면
7. 복합위젯 만들기
- 81 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
8.
월별 캘린더 만들기
월별 캘린더 만들기
• 월별 캘린더는 격자 형태로 그리드뷰를 사용하는 가장 대표적인 뷰
• 선택 위젯이므로 어댑터를 이용해 7X6 형태의 격자 모양 구성
• Date와 Calendar 클래스를 이용해 선택한 월의 정보 확인
• 1일이 시작되는 요일을 확인하고 끝나는 일수를 확인하는 등의 계산 작업 필요
8. 월별 캘린더 만들기
- 83 -
월별 캘린더 만들기 예제
월별 캘린더 만들기 예제
-달력 형태로 보여주는 화면
어댑터 클래스 정의
-그리드뷰를 위한 어댑터 클래스
메인 액티비티를 위한
XML 레이아웃 정의
-그리드뷰 추가
정의
메인 액티비티 코드 작성
-메인 액티비티 코드에서 참조하
여 사용
8. 월별 캘린더 만들기
- 84 -
레이아웃 만들기
• 화면 위쪽에 월 이동 버튼이 있고 중앙에 캘린더 화면이 있는 레이아웃 정의
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffffff"
>
<Button
android:id="@+id/monthPrevious"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:background="@drawable/backward"
android:gravity="center_horizontal"
1 이전 월로 이동하기 위한
버튼 추가
android:layout_alignParentLeft="true"
/>
8. 월별 캘린더 만들기
Continued..
- 85 -
레이아웃 만들기 (계속)
<TextView
android:id="@+id/monthText"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="MonthView"
2 현재 월을 표시하기 위한
텍스트뷰 추가
android:gravity="center_horizontal"
android:layout_centerInParent="true"
/>
<Button
android:id="@+id/monthNext"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:background="@drawable/forward"
3 이후 월로 이동하기
위한 버튼 추가
android:gravity="center_horizontal"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
8. 월별 캘린더 만들기
Continued..
- 86 -
레이아웃 만들기 (계속)
<org.androidtown.calendar.month.CalendarMonthView
android:id="@+id/monthView"
4
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
8. 월별 캘린더 만들기
- 87 -
새로 정의한 월별 캘린더 뷰 추가
그리드뷰를 상속한 새로운 클래스 정의
package org.androidtown.calendar.month;
public class CalendarMonthView extends GridView {
private OnDataSelectionListener selectionListener;
CalendarMonthAdapter adapter;
public CalendarMonthView(Context context) {
super(context);
1 그리드뷰를 상속하여
클래스 정의
init();
}
public CalendarMonthView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
Continued..
8. 월별 캘린더 만들기
- 88 -
그리드뷰를 상속한 새로운 클래스 정의 (계속)
private void init() {
setBackgroundColor(Color.GRAY);
setVerticalSpacing(1);
setHorizontalSpacing(1);
2 뷰를 초기화하는 메소드를 만들고
그 안에서 칼럼의 개수 설정
setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
setNumColumns(7);
setOnItemClickListener(new OnItemClickAdapter());
}
...
}
8. 월별 캘린더 만들기
- 89 -
어댑터 클래스 정의
package org.androidtown.calendar.month;
...
public class CalendarMonthAdapter extends BaseAdapter {
...
public CalendarMonthAdapter(Context context) {
super();
mContext = context;
init();
1 BaseAdapter를 상속하여
새로운 어댑터 정의
}
public CalendarMonthAdapter(Context context, AttributeSet attrs) {
super();
mContext = context;
init();
}
Continued..
8. 월별 캘린더 만들기
- 90 -
어댑터 클래스 정의 (계속)
private void init() {
2 한 개 월의 일별 데이터를
담고 있을 수 있는
MonthItem의 배열 객체
생성
items = new MonthItem[7 * 6];
mCalendar = Calendar.getInstance();
recalculate();
resetDayNumbers();
}
public void recalculate() {
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
int dayOfWeek = mCalendar.get(Calendar.DAY_OF_WEEK);
firstDay = getFirstDay(dayOfWeek);
Log.d(TAG, "firstDay : " + firstDay);
mStartDay = mCalendar.getFirstDayOfWeek();
curYear = mCalendar.get(Calendar.YEAR);
curMonth = mCalendar.get(Calendar.MONTH);
lastDay = getMonthLastDay(curYear, curMonth);
int diff = mStartDay - Calendar.SUNDAY - 1;
startDay = getFirstDayOfWeek();
}
8. 월별 캘린더 만들기
Continued..
- 91 -
어댑터 클래스 정의 (계속)
public void setPreviousMonth() {
mCalendar.add(Calendar.MONTH, -1);
recalculate();
resetDayNumbers();
3 이전 월로 이동 시 일별 데이터
새로 계산
selectedPosition = -1;
}
public void setNextMonth() {
mCalendar.add(Calendar.MONTH, 1);
recalculate();
resetDayNumbers();
4 다음 월로 이동 시 일별 데이터
새로 계산
selectedPosition = -1;
}
Continued..
8. 월별 캘린더 만들기
- 92 -
어댑터 클래스 정의 (계속)
public void resetDayNumbers() {
for (int i = 0; i < 42; i++) {
int dayNumber = (i+1) - firstDay;
if (dayNumber < 1 || dayNumber > lastDay) {
dayNumber = 0;
5 지정한 월의 일별 데이터를
새로 계산하는 메소드 정의
}
items[i] = new MonthItem(dayNumber);
}
}
private int getFirstDay(int dayOfWeek) {
int result = 0;
if (dayOfWeek == Calendar.SUNDAY) {
result = 0;
} else if (dayOfWeek == Calendar.MONDAY) {
result = 1;
} else if (dayOfWeek == Calendar.TUESDAY) {
result = 2;
Continued..
8. 월별 캘린더 만들기
- 93 -
어댑터 클래스 정의 (계속)
public View getView(int position, View convertView, ViewGroup parent) {
MonthItemView itemView;
if (convertView == null) {
itemView = new MonthItemView(mContext);
} else {
itemView = (MonthItemView) convertView;
}
GridView.LayoutParams params = new GridView.LayoutParams(
GridView.LayoutParams.MATCH_PARENT,
70);
int rowIndex = position / countColumn;
int columnIndex = position % countColumn;
Continued..
8. 월별 캘린더 만들기
- 94 -
어댑터 클래스 정의 (계속)
itemView.setItem(items[position]);
itemView.setLayoutParams(params);
itemView.setPadding(2, 2, 2, 2);
itemView.setGravity(Gravity.LEFT);
if (columnIndex == 0) {
itemView.setTextColor(Color.RED);
} else {
itemView.setTextColor(Color.BLACK);
}
6
if (position == getSelectedPosition()) {
itemView.setBackgroundColor(Color.YELLOW);
} else {
itemView.setBackgroundColor(Color.WHITE);
}
return itemView;
}
...
}
8. 월별 캘린더 만들기
- 95 -
getView() 메소드에서
리턴하는 뷰의 속성 설정
메인 액티비티 코드 만들기
monthView = (CalendarMonthView) findViewById(R.id.monthView);
monthViewAdapter = new CalendarMonthAdapter(this);
monthView.setAdapter(monthViewAdapter);
1 레이아웃에 정의된 월별 캘린더 뷰
객체 참조
2 어댑터 객체 생성 후 월별 캘린더 뷰
객체에 설정
monthView.setOnDataSelectionListener(new OnDataSelectionListener() {
public void onDataSelected(AdapterView parent, View v, int position, long id) {
MonthItem curItem = (MonthItem) monthViewAdapter.getItem(position);
int day = curItem.getDay();
3 월별 캘린더 뷰에
리스너 설정
}
});
monthText = (TextView) findViewById(R.id.monthText);
setMonthText();
Continued..
8. 월별 캘린더 만들기
- 96 -
메인 액티비티 코드 만들기 (계속)
Button monthPrevious = (Button) findViewById(R.id.monthPrevious);
monthPrevious.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
4 이전 월 [이동] 버튼 클릭 시 일별
데이터를 다시 계산하는 메소드
호출하고 화면 갱신
monthViewAdapter.setPreviousMonth();
monthViewAdapter.notifyDataSetChanged();
setMonthText();
}
});
Button monthNext = (Button) findViewById(R.id.monthNext);
monthNext.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
5 다음 월 [이동] 버튼 클릭 시
일별 데이터를 다시 계산하는
메소드 호출하고 화면 갱신
monthViewAdapter.setNextMonth();
monthViewAdapter.notifyDataSetChanged();
setMonthText();
}
});
}
8. 월별 캘린더 만들기
Continued..
- 97 -
메인 액티비티 코드 만들기 (계속)
private void setMonthText() {
curYear = monthViewAdapter.getCurYear();
curMonth = monthViewAdapter.getCurMonth();
monthText.setText(curYear + "년 " + (curMonth+1) + "월");
}
}
[월별 캘린더와 날짜 선택]
8. 월별 캘린더 만들기
- 98 -
둘째 마당 – CH4. 선택 위젯의 사용과 커스텀뷰 만들기
9.
멀티터치 이미지뷰어 만들기
멀티터치 이벤트 처리하기 – X, Y 좌표
[API]
public final int getPointerCount ()
public final float getX(int pointerIndex)
public final float getY(int pointerIndex)
• 첫 번째 메소드인 getPointerCount()는 몇 개의 손가락이 터치되었는지를 알 수 있도록 해주는 것
• 이벤트 처리에 자주 사용되는 getX()와 getY() 메소드는 손가락이 하나일 때 X와 Y의 좌표값을 가져오지만
getX(int pointerIndex)와 getY(int pointerIndex) 메소드는 여러 개의 손가락이 터치되었을 때 각각의 손가락이
가지는 인덱스의 값을 이용해 좌표값을 확인할 수 있도록 함
9. 멀티터치 이미지뷰어 만들기
- 100 -
멀티터치 이미지뷰어 예제
멀티터치 이미지뷰어
만들기 예제
-멀티터치로 이미지를 조작하는 기능
새로운 뷰 클래스 정의
메인 액티비티 코드 작성
-멀티터치 이벤트를 처리하는 새
-메인 액티비티 코드에서 참조하
로운 뷰 클래스 정의
여 사용
9. 멀티터치 이미지뷰어 만들기
- 101 -
새로운 뷰 클래스 정의
public class ImageDisplayView extends View implements OnTouchListener {
…
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1
뷰를 상속하면서
OnTouchListener
인터페이스를 구현
하는 클래스 정의
if (w > 0 && h > 0) {
2 뷰가 초기화되고 나서 화면에 보이기
전 크기가 정해지면 호출되는 메소드
안에서 메모리 상에 새로운 비트맵
객체 생성
newImage(w, h);
redraw();
}
}
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
3 뷰가 화면에 그려지는 메소드 안에서
메모리 상의 비트맵 객체 그리기
}
}
...
4 뷰를 터치할 때 호출되는
메소드 다시 정의
public boolean onTouch(View v, MotionEvent ev) {
final int action = ev.getAction();
Continued..
9. 멀티터치 이미지뷰어 만들기
- 102 -
새로운 뷰 클래스 정의 (계속)
int pointerCount = ev.getPointerCount();
Log.d(TAG, "Pointer Count : " + pointerCount);
5 터치했을 때 몇 개의 손가락으로
터치하는지 개수 확인
switch (action) {
case MotionEvent.ACTION_DOWN:
if (pointerCount == 1) {
float curX = ev.getX();
float curY = ev.getY();
startX = curX;
6 손가락으로 눌렀을 때의
기능 추가
startY = curY;
} else if (pointerCount == 2) {
oldDistance = 0.0F;
isScrolling = true;
}
return true;
Continued..
9. 멀티터치 이미지뷰어 만들기
- 103 -
새로운 뷰 클래스 정의 (계속)
case MotionEvent.ACTION_MOVE:
if (pointerCount == 1) {
if (isScrolling) {
return true;
}
float curX = ev.getX();
float curY = ev.getY();
if (startX == 0.0F) {
7 손가락으로 움직일 때의
기능 추가
startX = curX;
startY = curY;
return true;
}
float offsetX = startX - curX;
float offsetY = startY - curY;
if (oldPointerCount == 2) {
} else {
Log.d(TAG, "ACTION_MOVE : " + offsetX + ", " + offsetY);
if (totalScaleRatio > 1.0F) {
9. 멀티터치 이미지뷰어 만들기
Continued..
- 104 -
새로운 뷰 클래스 정의 (계속)
moveImage(-offsetX, -offsetY);
8
}
한 손가락으로 움직이고 있을 때는
moveImage() 메소드 호출
startX = curX;
startY = curY;
}
} else if (pointerCount == 2) {
float x1 = ev.getX(0);
float y1 = ev.getY(0);
float x2 = ev.getX(1);
float y2 = ev.getY(1);
float dx = x1 - x2;
float dy = y1 - y2;
float distance = FloatMath.sqrt(dx * dx + dy * dy);
float outScaleRatio = 0.0F;
if (oldDistance == 0.0F) {
oldDistance = distance;
break;
}
9. 멀티터치 이미지뷰어 만들기
Continued..
- 105 -
새로운 뷰 클래스 정의 (계속)
scaleImage(outScaleRatio);
9
}
두 손가락으로 움직이고 있을 때는
scaleImage() 메소드 호출
oldDistance = distance;
}
oldPointerCount = pointerCount;
break;
case MotionEvent.ACTION_UP:
if (pointerCount == 1) {
float curX = ev.getX();
float curY = ev.getY();
float offsetX = startX - curX;
10
손가락을 떼었을 때의 기능 추가
float offsetY = startY - curY;
if (oldPointerCount == 2) {
} else {
moveImage(-offsetX, -offsetY);
}
Continued..
9. 멀티터치 이미지뷰어 만들기
- 106 -
새로운 뷰 클래스 정의 (계속)
} else {
isScrolling = false;
}
return true;
}
return true;
}
private void scaleImage(float inScaleRatio) {
Log.d(TAG, "scaleImage() called : " + inScaleRatio);
mMatrix.postScale(inScaleRatio, inScaleRatio, bitmapCenterX, bitmapCenterY);
mMatrix.postRotate(0);
11
매트릭스 객체를 이용
해 이미지 크기 변경
totalScaleRatio = totalScaleRatio * inScaleRatio;
redraw();
}
Continued..
9. 멀티터치 이미지뷰어 만들기
- 107 -
새로운 뷰 클래스 정의 (계속)
private void moveImage(float offsetX, float offsetY) {
Log.d(TAG, "moveImage() called : " + offsetX + ", " + offsetY);
12
mMatrix.postTranslate(offsetX, offsetY);
redraw();
}
}
9. 멀티터치 이미지뷰어 만들기
- 108 -
매트릭스 객체를 이용해
이미지 이동
postScale() 메소드
[API]
public boolean postScale (float sx, float sy, float px, float py)
public boolean postTranslate (float dx, float dy)
public boolean postRotate (float degrees)
• postScale() 메소드를 이용하면 비트맵 이미지를 확대 또는 축소할 수 있으며, 첫 번째 파라미터는 X축을
기준으로 확대하는 비율, 두 번째 파라미터는 Y축을 기준으로 확대하는 비율을 의미함
• 세 번째와 네 번째 파라미터는 확대 또는 축소할 때 기준이 되는 위치가 되는데 일반적으로는 비트맵 이미지의
중심점을 지정함
• postTranslate() 메소드는 비트맵 이미지를 이동시킬 때 사용함
• postRotate() 메소드는 비트맵 이미지를 회전시킬 때 사용함
9. 멀티터치 이미지뷰어 만들기
- 109 -
메인 액티비티 코드 만들기
package org.androidtown.events.multitouch;
...
public class MainActivity extends Activity {
LinearLayout viewerContainer;
ImageDisplayView displayView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
viewerContainer = (LinearLayout) findViewById(R.id.viewerContainer);
Bitmap sourceBitmap = loadImage();
1
XML 레이아웃에 정의한 리니어
레이아웃 객체 참조
if (sourceBitmap != null) {
Continued..
9. 멀티터치 이미지뷰어 만들기
- 110 -
메인 액티비티 코드 만들기 (계속)
displayView = new ImageDisplayView(this);
2
새로 정의한
ImageDisplayView 객체 생성
displayView.setImageData(sourceBitmap);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
viewerContainer.addView(displayView, params);
3 addView()를 이용해 리니어
레이아웃 객체에
ImageDisplayView 객체 추가
}
}
private Bitmap loadImage() {
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.beach);
return bitmap;
}
}
9. 멀티터치 이미지뷰어 만들기
- 111 -
실행 화면
9. 멀티터치 이미지뷰어 만들기
- 112 -
디버깅 메시지 확인하기
[두 손가락으로 터치할 때의 좌표값 디버깅]
9. 멀티터치 이미지뷰어 만들기
- 113 -
참고 문헌
[ References]
• 기본 서적
2013, 정재곤, “Do it! 안드로이드 앱 프로그래밍(개정판)”, 이지스퍼블리싱(주)
• Android Website
http://www.android.com/
• Google Developer’s Conference
http://code.google.com/events/io/
• Android SDK Documentation
References
- 114 -