과제
코드는 총 3개로 되어있다.
- 액티비티의 UI를 정의하는
activity_main.xml
- 메뉴 아이템을 정의하는
menu1.xml
- 액티비티의 동작을 제어하는
MainActivity.java
activity_main.xml
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
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/baseLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@drawable/jeju2"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/edtInput"
android:layout_alignParentLeft="true"
android:text="각도 및 확대/축소 비율 입력"
android:textSize="20dp"/>
<EditText
android:id="@+id/edtInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/textView1"
android:text="0,1.0" />
</RelativeLayout>
코드 해석
RelativeLayout
: 레이아웃의 최상위 뷰 그룹- 레이아웃의 속성으로는
android:id
,android:layout_width
,android:layout_height
,android:orientation
가 설정android:id
는baseLayout
으로 설정되어 있고,android:layout_width
와android:layout_height
는match_parent
로 설정되어 레이아웃이 부모 컨테이너와 동일한 크기를 가짐레이아웃이 부모 컨테이너와 동일한 크기를 갖도록 하는데 부모 컨테이너란 무엇일까?
여기서 “부모 컨테이너”는 현재 레이아웃이 포함된 상위 레이아웃을 가리킨다.
일반적으로 액티비티의 루트 레이아웃이 부모 컨테이너가 돤다.
액티비티의 루트 레이아웃은 액티비티의 XML 레이아웃 파일에서 최상위에 정의된 레이아웃이다.
위의 코드에서
android:layout_height="match_parent"
는<RelativeLayout>
이 포함된 부모 컨테이너(일반적으로 액티비티)의 세로 크기와 동일하게 설정되어 레이아웃이 채워지도록 한다. android:orientation
은 수직 방향(vertical
)으로 설정
- 레이아웃의 속성으로는
ImageView
: 이미지를 표시하는 뷰- 이미지의 속성으로는
android:id
,android:layout_width
,android:layout_height
,android:layout_centerHorizontal
,android:layout_centerVertical
,android:src
가 설정됨 android:id
는imageView1
으로 설정되어 있고,android:layout_width
와android:layout_height
는wrap_content
로 설정되어 이미지의 원본 크기에 맞게 크기가 조정android:layout_centerHorizontal
과android:layout_centerVertical
은 이미지를 수평과 수직으로 중앙에 정렬android:src
는@drawable/jeju14
로 설정되어 해당 이미지를 표시
- 이미지의 속성으로는
TextView
: 텍스트를 표시하는 뷰- 텍스트의 속성으로는
android:id
,android:layout_width
,android:layout_height
,android:layout_alignBottom
,android:layout_alignParentLeft
,android:text
,android:textSize
가 설정됨 android:id
는textView1
으로 설정되어 있고,android:layout_width
와android:layout_height
는wrap_content
로 설정되어 텍스트의 내용에 맞게 크기가 조정android:layout_alignBottom
은edtInput
과 하단을 맞춰줌android:layout_alignParentLeft
는 부모 레이아웃의 왼쪽에 정렬android:text
는 “각도 및 확대/축소 비율 입력”으로 텍스트를 표시android:textSize
는20dp
로 텍스트의 크기를 조정
- 텍스트의 속성으로는
EditText
: 사용자 입력을 받는 텍스트 입력란이 됨- 에디트 텍스트의 속성으로는
android:id
,android:layout_width
,android:layout_height
,android:layout_alignParentTop
,android:layout_toRightOf
,android:text
가 설정 android:id
는edtInput
으로 설정되어 있고,android:layout_width
는match_parent
,android:layout_height
는match_parent
와wrap_content
로 설정layout_alignParentTop
는 true로 설정되어EditText
가 상단에 위치layout_toRightOf
는 현재 뷰를 다른 뷰의 오른쪽에 배치하는 시켜줌@+id/textView1
은 다른 뷰의 ID를 참조하며, 현재 뷰를textView1
뷰의 오른쪽에 배치하도록 지정android:text="0, 1.0"
으로 인해 입력 초기값은 “0,1.0”으로 설정
- 에디트 텍스트의 속성으로는
실행 화면
menu1.xml
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
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/itemRotate"
android:title="그림 회전">
</item>
<item android:id="@+id/itemZoom"
android:title="그림 확대">
</item>
<item android:id="@+id/itemShrink"
android:title="그림 축소">
</item>
<group android:checkableBehavior="single" >
<item
android:id="@+id/item1"
android:checked="true"
android:title="한라산">
</item>
<item
android:id="@+id/item2"
android:title="추자도">
</item>
<item
android:id="@+id/item3"
android:title="범섬">
</item>
</group>
</menu>
코드 해석
onCreate()
메서드: 액티비티가 생성될 때 호출되는 메서드setContentView(R.layout.activity_main)
: 액티비티의 레이아웃을 설정한다.activity_main.xml
파일의 내용이 화면에 표시됨setTitle("제주도 풍경")
: 액티비티의 타이틀을 “제주도 풍경”으로 설정findViewById(R.id.edtInput)
: XML에서 정의한edtInput
ID를 가진EditText
뷰를 찾아줌edtInput.setText("0,1.0")
:edtInput
의 텍스트를 “0,1.0”으로 설정findViewById(R.id.imageView1)
: XML에서 정의한imageView1
ID를 가진ImageView
뷰를 찾아줌
onCreateOptionsMenu()
메서드: 옵션 메뉴를 생성하는 메서드getMenuInflater().inflate(R.menu.menu1, menu)
:menu1.xml
파일을 인플레이트하여 메뉴를 생성
onOptionsItemSelected()
메서드: 옵션 메뉴 아이템을 선택했을 때 호출되는 메서드item.getItemId()
: 선택된 메뉴 아이템의 ID를 가져옴R.id.itemRotate
: “itemRotate” 메뉴 아이템을 선택한 경우edtInput.getText().toString().split(",")[0]
:edtInput
의 텍스트를 가져와서 “,”로 분리한 후 첫 번째 값을 가져옴imageView1.setRotation(angle)
:imageView1
의 회전을angle
값으로 설정
R.id.itemZoom
: “itemZoom” 메뉴 아이템을 선택한 경우edtInput.getText().toString().split(",")[1]
:edtInput
의 텍스트를 가져와서 “,”로 분리한 후 두 번째 값을 가져옴checkScaleBounds(scaleValue)
:scaleValue
값이 화면 스케일 범위 내에 있는지 확인imageView1.setScaleX(scaleValue)
:imageView1
의 X 축 스케일을scaleValue
로 설정imageView1.setScaleY(scaleValue)
:imageView1
의 Y 축 스케일을scaleValue
로 설정
R.id.itemShrink
: “itemShrink” 메뉴 아이템을 선택한 경우shrinkValue
:edtInput
의 텍스트를 가져와서 “,”로 분리한 후 두 번째 값을 가져옵니다. 이 값을 절반으로 조정checkScaleBounds(scaleValue)
:scaleValue
값이 화면 스케일 범위 내에 있는지 확인
R.id.item1
: “item1” 메뉴 아이템을 선택한 경우imageView1.setImageResource(R.drawable.jeju2)
:imageView1
의 이미지를jeju2
로 설정
R.id.item2
: “item2” 메뉴 아이템을 선택한 경우imageView1.setImageResource(R.drawable.jeju14)
:imageView1
의 이미지를jeju14
로 설정
R.id.item3
: “item3” 메뉴 아이템을 선택한 경우imageView1.setImageResource(R.drawable.jeju6)
:imageView1
의 이미지를jeju6
으로 설정
- 기타: 선택한 메뉴 아이템이 없는 경우, 부모 클래스인
super.onOptionsItemSelected(item)
을 호출
checkScaleBounds()
메서드: 화면 스케일 범위를 확인하는 메서드imageView1.getWidth()
:imageView1
의 너비를 가져옴imageView1.getHeight()
:imageView1
의 높이를 가져옴imageView1.getDrawable().getIntrinsicWidth()
:imageView1
에 설정된 이미지의 원본 너비를 가져옴imageView1.getDrawable().getIntrinsicHeight()
:imageView1
에 설정된 이미지의 원본 높이를 가져옴maxScaleX
: 뷰의 너비와 이미지의 너비를 비교하여 X 축 스케일의 최대값을 계산maxScaleY
: 뷰의 높이와 이미지의 높이를 비교하여 Y 축 스케일의 최대값을 계산return scaleValue <= maxScaleX && scaleValue <= maxScaleY
: 스케일 값이 X 축과 Y 축의 최대값보다 작거나 같으면true
를 반환하고, 그렇지 않으면false
를 반환
실행 화면
범선으로 그림을 변경한 모습
MainActivity.java
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.cookandroid.project7_1;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
EditText edtInput;
ImageView imageView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("제주도 풍경");
edtInput = findViewById(R.id.edtInput);
edtInput.setText("0,1.0"); // 입력 초기값 설정
imageView1 = findViewById(R.id.imageView1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater mInflater = getMenuInflater();
mInflater.inflate(R.menu.menu1, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.itemRotate:
float angle = Float.parseFloat(edtInput.getText().toString().split(",")[0]);
imageView1.setRotation(angle);
return true;
case R.id.itemZoom:
float scaleValue = Float.parseFloat(edtInput.getText().toString().split(",")[1]);
if (checkScaleBounds(scaleValue)) {
imageView1.setScaleX(scaleValue);
imageView1.setScaleY(scaleValue);
} else {
Toast.makeText(this, "화면보다 확대를 크게 할 수 없습니다.", Toast.LENGTH_SHORT).show();
}
return true;
case R.id.itemShrink:
float shrinkValue = Float.parseFloat(edtInput.getText().toString().split(",")[1]) / 2;
if (checkScaleBounds(shrinkValue)) {
imageView1.setScaleX(shrinkValue);
imageView1.setScaleY(shrinkValue);
} else {
Toast.makeText(this, "화면보다 축소를 작게 할 수 없습니다.", Toast.LENGTH_SHORT).show();
}
return true;
case R.id.item1:
imageView1.setImageResource(R.drawable.jeju2);
return true;
case R.id.item2:
imageView1.setImageResource(R.drawable.jeju14);
return true;
case R.id.item3:
imageView1.setImageResource(R.drawable.jeju6);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private boolean checkScaleBounds(float scaleValue) {
int viewWidth = imageView1.getWidth();
int viewHeight = imageView1.getHeight();
int imageWidth = imageView1.getDrawable().getIntrinsicWidth();
int imageHeight = imageView1.getDrawable().getIntrinsicHeight();
float maxScaleX = (float) viewWidth / imageWidth;
float maxScaleY = (float) viewHeight / imageHeight;
return scaleValue <= maxScaleX && scaleValue <= maxScaleY;
}
}
코드 해석
onCreate()
메서드- 액티비티가 생성될 때 호출되는 메서드
setContentView(R.layout.activity_main)
: activity_main.xml 레이아웃을 액티비티에 설정setTitle("제주도 풍경")
: 액티비티의 타이틀을 “제주도 풍경”으로 설정findViewById(R.id.edtInput)
: edtInput EditText를 찾아 변수에 할당edtInput.setText("0,1.0")
: edtInput EditText의 텍스트를 “0,1.0”으로 설정findViewById(R.id.imageView1)
: imageView1 ImageView를 찾아 변수에 할당
onCreateOptionsMenu()
메서드- 옵션 메뉴를 생성하기 위해 호출되는 메서드
getMenuInflater().inflate(R.menu.menu1, menu)
: menu1.xml 파일을 가져와서 메뉴를 생성
onOptionsItemSelected()
메서드- 옵션 메뉴에서 선택한 아이템에 대한 동작을 처리하기 위해 호출되는 메서드
item.getItemId()
를 사용하여 선택한 아이템의 ID를 확인- 선택한 아이템에 따라 다음 동작을 수행
- “itemRotate”:
edtInput
의 텍스트를 가져와서 “,”로 분리한 후 첫 번째 값을 회전 각도로 설정 - “itemZoom”:
edtInput
의 텍스트를 가져와서 “,”로 분리한 후 두 번째 값을 확대/축소 비율로 설정 설정한 값이 화면 스케일 범위 내에 있는지 확인하고, 그에 따라 이미지의 X 및 Y 축 스케일을 설정 - “itemShrink”:
edtInput
의 텍스트를 가져와서 “,”로 분리한 후 두 번째 값을 절반으로 조정하여 축소 비율로 설정 설정한 값이 화면 스케일 범위 내에 있는지 확인하고, 그에 따라 이미지의 X 및 Y 축 스케일을 설정 - “item1”, “item2”, “item3”: 각각의 아이템에 대해
imageView1
의 이미지를 변경
- “itemRotate”:
- 선택한 아이템에 대한 동작을 수행한 후
true
를 반환
checkScaleBounds()
메서드- 주어진 스케일 값이 화면 스케일 범위 내에 있는지 확인하는 메서드
imageView1.getWidth()
및imageView1.getHeight()
:imageView1
의 너비와 높이를 가져옴imageView1.getDrawable().getIntrinsicWidth()
및imageView1.getDrawable().getIntrinsicHeight()
:imageView1
에 설정된 Drawable의 원본 너비와 높이를 가져옴maxScaleX
와maxScaleY
: 이미지 뷰의 너비와 높이를 기준으로 최대 스케일 값을 계산합니다. 이미지가 화면에 맞게 스케일되지 않도록 최대 스케일 값을 제한scaleValue
와shrinkValue
가 최대 스케일 값을 초과하지 않으면true
를 반환하고, 그렇지 않으면false
를 반환
실행 화면
45도 회전한 모습
화면보다 그림이 크게 확대되어 화면 밖으로 짤릴경우 경고가 토스트로 출력되는 모습
그림을 축소한 모습
그림을 다시 확대한 모습
그림을 회전과 축소를 같이 한 모습
댓글남기기