컨스트레인트레이아웃 - 원형 위치 지정 (ConstraintLayout - Circular Positioning)
1. 뷰 위젯 상대 위치 지정.
이전 글 [컨스트레인트레이아웃 - 상대 위치 지정(ConstraintLayout - Relative Positioning)]에서, 컨스트레인트레이아웃의 상대 위치 지정(Relative Positioning) 방법에 대해 살펴보았습니다. 대상 뷰 또는 부모 레이아웃을 기준으로, 수평 방향(Left, Right)과 수직 방향(Top, Bottom)으로 뷰가 배치될 상대 위치를 지정하는 방법에 대해 설명했었죠.
그런데 컨스트레인트레이아웃이 제공하는 상대 위치 지정(Relative Positioning)은, 네 방향(4-way, 상/하/좌/우)에 대한 상대 위치 지정에만 한정되지 않고, 훨씬 다양한 배치를 가능하게 하는 방법을 제공합니다. 대상 뷰를 기준으로 "360도 방향"으로 상대 위치를 지정할 수 있게 말이죠.
2. 원형 위치 지정(Circular Positioning)
컨스트레인트레이아웃은 대상 뷰를 기준으로, 원하는 "각도(angle)"에, 지정된 "반경(radius)"만큼 떨어진 위치에 뷰를 배치하는 제약을 제공하는데요, 이를 원형 위치 지정(Circular Positioning)이라고 부릅니다.
위 그림을 통해 알수 있듯이 원형 위치 지정(Circular Positioning) 방법은 그 이름답게, 대상 뷰 위젯의 중심을 기준으로, 주어진 반지름(radius)만큼 떨어진 원(Circle) 둘레의 한 지점에 뷰를 배치할 수 있게 해 줍니다. 원 둘레에서 뷰가 배치될 정확한 지점은 0~360 사이의 각도(angle) 값을 지정하여 결정하는데, 대상 뷰 위젯의 중심에서 정확히 위(top)쪽 방향 반지름만큼 떨어진 위치가 0도의 기준이 됩니다.
자, 위 설명을 보면, 컨스트레인트레이아웃의 원형 위치 지정(Circular Positioning)에 사용되는 속성이 어떤 것들이 있는지 감이 오죠? 바로 "대상 뷰 위젯", "반지름", "각도" 이렇게 세 개의 속성이 제공됩니다.
* layout_constraintCircle - 대상 뷰 위젯 지정.
> 속성 값에는 대상 뷰의 ID 지정.
* layout_constraintCircleRadius - 뷰 위젯과 대상 뷰 위젯 중심 사이의 거리.
> 치수(dimension) 값 지정 가능.
* layout_constraintCircleAngle - 원 둘레에서 뷰 위젯이 배치될 각도.
> 기본적으로 0~360 사이의 정수 값 사용.
> 음수(-) 값 지정 시, 절대 값으로 적용.
> 360 이상 값 지정 시, 360을 나눈 나머지 값 적용.
아래는 몇 가지 원형 위치 지정(Circular Positioning)의 예제입니다.
3. 컨스트레인트레이아웃 예제 : 아날로그 시계
컨스트레인트레이아웃의 원형 위치 지정(Circular Positioning) 방법으로 만들 수 있는 UI로 어떤 화면이 가장 먼저 떠오르나요? "아날로그 시계"가 딱 떠오르지 않나요? 아래와 같이, 아주 간단하게 한번 만들어봤습니다. 음... 뭐, 일단 동작은 하네요.
어쨌든, 아래 예제 코드에서는 원형 위치 지정(Circular Positioning)으로 레이아웃을 구성하는 방법에 대해 확인하실 수 있습니다.
[STEP-1] "activity_main.xml" - 메인액티비티에서 아날로그 시계 레이아웃 구성
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">
<View
android:layout_width="10dp"
android:layout_height="10dp"
android:background="#00FF00"
android:id="@+id/watchCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="1"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="30" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="2"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="60" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="3"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="90" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="4"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="120" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="5"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="150" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="6"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="180" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="7"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="210" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="8"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="240" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="9"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="270" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="10"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="11"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="330" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="12"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="250dp"
app:layout_constraintCircleAngle="0" />
<View
android:layout_width="200dp"
android:layout_height="2dp"
android:background="#FF0000"
android:id="@+id/handSecond"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="90" />
<View
android:layout_width="160dp"
android:layout_height="2dp"
android:background="#0000FF"
android:id="@+id/handMinute"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="80dp"
app:layout_constraintCircleAngle="90" />
<View
android:layout_width="100dp"
android:layout_height="2dp"
android:background="#000000"
android:id="@+id/handHour"
app:layout_constraintCircle="@id/watchCenter"
app:layout_constraintCircleRadius="50dp"
app:layout_constraintCircleAngle="90" />
</android.support.constraint.ConstraintLayout>
[STEP-2] "MainActivity.java" - 메인 액티비티에서 시각 갱신하는 코드 작성.
public class MainActivity extends AppCompatActivity {
private View handSecond = null ;
private View handMinute = null ;
private View handHour = null ;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... 코드 계속
handSecond = findViewById(R.id.handSecond) ;
handMinute = findViewById(R.id.handMinute) ;
handHour = findViewById(R.id.handHour) ;
handSecond.setPivotX(0.0f) ;
handSecond.setPivotY(0.0f) ;
handMinute.setPivotX(0.0f) ;
handMinute.setPivotY(0.0f) ;
handHour.setPivotX(0.0f) ;
handHour.setPivotY(0.0f) ;
final Handler handler = new Handler() ;
final Runnable runnalble = new Runnable() {
@Override
public void run() {
LocalTime time = LocalTime.now() ;
int second = time.getSecond() ;
int minute = time.getMinute() ;
int hour = time.getHour() ;
System.out.println("hour : " + hour) ;
System.out.println("minute : " + minute) ;
System.out.println("second : " + second) ;
// Set the rotation of the view.
handSecond.setRotation(360 * second / 60 - 90) ;
handMinute.setRotation(360 * minute / 60 - 90) ;
// handHour.setRotation(360 * hour / 12 - 90);
handHour.setRotation(360 * ((hour%12)*60+minute)/(12*60) - 90) ;
handler.postDelayed(this, 1000);
}
} ;
handler.postDelayed(runnalble, 1000) ;
}
}
4. 참고.
- 개발자 레시피 - 컨스트레인트레이아웃 관련 항목
- [안드로이드 컨스트레인트레이아웃 (Android ConstraintLayout)]을 참고하세요.
- [컨스트레인트레이아웃 - 레이아웃 공통사항 (ConstraintLayout - Layout Common)]을 참고하세요.
- [컨스트레인트레이아웃 - 상대 위치 지정 (ConstraintLayout - Relative Positioning)]을 참고하세요.
- [컨스트레인트레이아웃 - 여백 (ConstraintLayout - Margins)]을 참고하세요.
- [컨스트레인트레이아웃 - 가운데 위치 지정과 Bias (ConstraintLayout - Centering Positioning and Bias)]을 참고하세요.
- 개발자 레시피 - 레이아웃 관련 항목
- [안드로이드 레이아웃 (Android Layout)]을 참고하세요.
- [안드로이드 레이아웃 공통사항 (Android Layout Common)]을 참고하세요.
- [안드로이드 리니어레이아웃 (Android LinearLayout)]을 참고하세요.
- [안드로이드 렐러티브레이아웃 (Android RelativeLayout)]을 참고하세요.
- 레이아웃에 대한 자세한 도움말.
- [안드로이드 개발 참조문서 레이아웃 항목]을 참고하세요.
- ConstraintLayout에 대한 자세한 도움말.
- [안드로이드 개발 참조문서 ConstraintLayout]을 참고하세요.
- [안드로이드 개발 참조문서 ConstraintLayout.LayoutParams]을 참고하세요.
- Build a Responsive UI with ConstraintLayout.
- [Build a Responsive UI with ConstraintLayout ]을 참고하세요.
.END.