[안드로이드 앱 개발] 안드로이드 수평계 앱 개발(GUI 만들기)_세번째
본문 바로가기
IT기술 및 프로그래밍

[안드로이드 앱 개발] 안드로이드 수평계 앱 개발(GUI 만들기)_세번째

by 리뷰하는 인간 2022. 6. 8.

저번 시간에 이어 안드로이드 수평계 앱 개발 세 번째 시간입니다. 
요사이에 수평계 앱 개발 마무리 작업과 동시에 출시까지 좀 바쁜 일정으로 포스팅이 늦어지게 되었습니다. 오늘은 그 세 번째 시간인 클래스 GUI 꾸미기 시간을 가져 보겠습니다. 

 

 

우선 저번 포스팅을 보지 못하신 분들은 아래 링크를 따라가서 저번 시간에 해보았던 수평계 앱 개발 개요와 안드로이드 프로젝트 만들기를 먼저 보고 오시면 더 이해하기 쉬우실 것 같습니다.

>>> [안드로이드 앱 개발] 가속도 센서를 활용한 수평계 앱 만들기

>>>[안드로이드 앱 개발] 수평계 앱 개발 프로젝트 만들기

이번 포스팅은 수평계 앱 디자인 이미지를 띄우고 표시하는 것 부터 필요한 버튼까지 앱에 표시해 보겠습니다.

아래 제가 직접 제작한 수평계 앱 디자인 소스 입니다. 상업적으로 사용하시는 것은 상관없지만 완전히 똑같이 사용하지는 마시고 약간 수정하여 사용하시는 것은 괜찮습니다. 

수평계 본체 이미지
수평계 본체 이미지
수평계 눈금
수평계 눈금

수평계 이미지는 위 두가지 이미지가 필요합니다. 수평계 본체와 수평계 눈금입니다. 아래 첨부의 링크에서 파일을 받으시면 됩니다. 제가 사용한 이미지 리소스는 위 두 가지 밖에는 없습니다.

drawable.egg
0.11MB

 

이 두가지 파일을 우선 아래의 경로에 넣어 줍니다. 경로 명은 프로젝트 명 > app > src > main > res > drawable 안에 넣어 줍니다.

리소스 파일 저장경로
리소스 파일 저장경로

이제 코드로 들어가서 보겠습니다. 우선 저번에 만들었던 프로젝트는 빈 프로젝트가 아니라서 Firstfragment와 Secondfragment 클래스를 삭제하겠습니다.

불필요한 클래스 제거
불필요한 클래스 제거

위 붉은 박스로 된 클래스에 마우스 우측 키를 누른 후 delete키를 누른 후 delete anyway를 선택하시면 삭제됩니다. 이상태에서는 제대로 빌드가 되지 않던지 프로그램이 실행되지 않습니다.

이제 MainActity를 아래와 같이 수정합니다. onCreate 부분을 아래와 같이 수정합니다. 나머지 불필요한 요소들은 모두 제거했습니다. import 선언된 부분은 검게 표시가 되어 있는데 코드와 크게 상관없으니 수정하지 않아도 됩니다.

MainActivity.java 수정
MainActivity.java 수정

이제 layout로 가서 activity_main을 제외한 나머지 불필요한 xml 파일들을 모두 삭제합니다.

  • content_main.xml
  • fragment_first.xml
  • fragment_second.xml

activity_main.xml 위치
activity_main.xml 위치

 

activity_main.xml과 삭제했던 content_main.xml 등은 GUI를 xml 코드와 연동하여 표현합니다. 따라서 디자인에서 컨트롤을 추가하거나 하면 자동으로 xml 코드가 반영되고 또한 코드 보기에서 xml코드를 추가하거나 수정하면 디자인 보기에서 바로 적용이 됩니다.

GUI를 작성하기 위해 코드 보기에서 진행을 해도 반영이 되고 디자인에서 수정을 해도 반영이 됩니다. 따라서 적절히 사용을 하게 됩니다.

이제 activity_main.xml를 더블클릭하여 우측 상단의 코드 보기를 클릭하여 코드 상태에서 아래 코드를 삭제처리합니다.

activity_main.xml 코드보기
activity_main.xml 코드보기

삭제를 시킨 후 빌드를 눌러 줍니다.

이후 디버깅 모드로 실행시키면 빈 화면을 확인할 수 있습니다.

GUI 구성은 이 activity_main에서 xml 코드를 활용하여 제작할 수도 있고 design보기에서 마우스를 활용하여 드래그할 수 도 있습니다. 

아래 coordinatorlayout으로 되어 있는 부분을 constraintlayout로 변경합니다.

레이아웃 변경
레이아웃 변경

Convert CoordinatorLayout to ConstraintLayout로 변경합니다. 

이제 activity_main.xml 레이아웃 창에서 design 보기를 눌러 전환합니다. 이후 버튼을 아래 그림과 같이 두 개 추가하여 줍니다.

버튼 추가하기
버튼 추가하기

위에 적용된 툴바를 먼저 제가 하여 줍니다.

아래 그림과 같이 왼쪽 상단에 적당히 버튼을 위치시켜 줍니다. ConstraintLayout은 컨트롤 배치가 컨트롤들의 상대적인 위치로 제어되기 때문에 화면의 해상도가 변해도 컨트롤들의 위치가 해상도에 맞게 변경되는 효과를 볼 수 있습니다.

따라서 다양한 디바이스에서 프로그램을 실행시켜도 컨트롤들이 해상도에 구애받지 않고 디자인이 뭉개지지 않습니다.

하지만 저도 아직까지 컨트롤들의 위치는 이런 식으로 해결을 해보았지만 사이즈나 글자크기 등은 같이 변하는 것을 해보지 못했습니다. 혹시 아시는 분이 있으시면 지식공유 부탁드립니다. 각 버튼의 text속성을 변경하여 왼쪽부터 Offset, Reset로 설정해 줍니다.

버튼 배치
버튼 배치

이제 TextView를 2개 추가해 줍니다. TextView도 버튼을 추가할 때와 마찬가지로 TextView를 찾아서 드래그 앤 드롭하여 원하는 위치에 우선 끌어 넣어 줍니다. 아래 그림처럼 설정해줍니다. TextView를 아래 그림과 같이 설정해 줍니다. TextView의 textSize 속성을 24sp로 맞추어 주고 아래 그림처럼 text 속성을 X axis : 0와 Y axis: 0으로 설정합니다. 

마지막으로 TextView 컨트롤 4면의 anchor를 디스플레이 끝에 끌어다가 붙여주고 적당히 위치시켜 줍니다. 아래 그림의 지그재그 화살표 선을 보시면 됩니다.

Textview 배치
Textview 배치

 이제 FrameLayout를 넣어 줍니다. 이는 위에서 만든 수평계 이미지를 그리기 위해 넣어 줍니다.

FrameLayout 추가 하기
FrameLayout 배치하기
위 그림과 같이 FrameLayout를 추가합니다. 방법은 button 추가 방법 및 TextView 추가 방법과 마찬가지로 FrameLayout를 끌어다가 넣어 주면 됩니다.

이후 디자인 보기에서 코드 보기로 전환하여 줍니다. 

코드 보기로 전환하면 제일 아래에 FrameLayout 관련 코드가 적용된 것이 확인됩니다. 이를 아래 그림처럼 코드의 제일 상단으로 올려 줍니다. 그리고 전체 화면 표시를 위해 layout_width와 layout_height를 각각 "match_parent"로 설정합니다.

 

FrameLayout의 수정
FrameLayout의 수정

이유는 제일 먼저 배경이 되는 수평계 이미지가 그려지고 이후에 컨트롤들이 그려져야 하기 때문입니다.

수평계 이미지는 배경으로 간주해도 됩니다. 만약 배경이 되는 수평계 이미지가 가장 나중에 그려지면 전체를 그려버리기 때문에 나중에 프로그램을 실행시키는 경우 컨트롤들이 묻혀서 나타나지 않기 때문입니다.

아래 그림처럼 최종 디자인이 완성된 것을 확인합니다. FrameLayout가 전체 꽉 찬 것을 확인합니다.

이제 이 FrameLayout에 이미지를 그리는 코드를 작성해 보겠습니다.

전체 GUI
전체 GUI

이제 좌측 프로젝트 구성 창의 accello_meter에서 마우스 우클릭 New -> Java Class를 선택하여 새로운 클래스를 작성합니다. 이름을 아래와 같이 'Level_View'라고 작성합니다.

아래 그림과 같이 추가된 것을 확인합니다.

추가된 Level_View 클래스
추가된 Level_View 클래스

 Level_View클래스는 프로그램의 이미지 그리기를 담당하는 클래스입니다. 이후 수평계의 이미지와 애니메이션 구현을 담당하는 클래스 입니다.

이제 아래와 같이 Level_View클래스를 작성합니다. 

package com.accel.accello_meter;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.View;

public class Level_View  extends View {
    float  img_height;
    float img_width;
    Bitmap BackGround, BackGround_Line;
    Bitmap resizeImgBitmap;
    Bitmap resizeBackGround_Line;
    public Level_View(Context context) {
        super(context);
        Resources r = context.getResources();
        BackGround =  BitmapFactory.decodeResource(r, R.drawable.background);
        BackGround_Line =  BitmapFactory.decodeResource(r, R.drawable.background2);
        img_height= BackGround.getHeight();
        img_width = BackGround.getWidth();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        canvas.drawColor(Color.BLACK);
        // canvas.drawCircle(centerX,centerY,radius,wPaint);
        resizeImgBitmap = Bitmap.createScaledBitmap(BackGround, (int)(img_width), (int)(img_height), true); //사진이 클 경우, 다음과 같이 resize한다.
        resizeBackGround_Line = Bitmap.createScaledBitmap(BackGround_Line, (int)(img_width), (int)(img_height), true); //사진이 클 경우, 다음과 같이 resize한다.
        canvas.drawBitmap(resizeImgBitmap, 0, 0, null); //캔버스의 적절한 위치에 그린다.
        canvas.drawBitmap(resizeBackGround_Line, 0, 0, null);
        super.onDraw(canvas);
    }
}

위와 같이 우선 작성을 해 줍니다.

프로그램 변수 선언부에 이미지의 높이와 너비 값을 저장하기 위한 변수 그리고 두 가지 이미지를 그릴 Bitmap을 선언합니다. 

float  img_height;
float img_width;
Bitmap BackGround, BackGround_Line;
Bitmap resizeImgBitmap;
Bitmap resizeBackGround_Line;

아래 Level_View 생성자에 아래 그림과 같이 리소스 파일에 저장된 이미지를 불러들이는 코드를 작성합니다. 그리고 불러온 이미지의 높이값과 너비 값을 저장합니다.

public Level_View(Context context) {
    super(context);
    Resources r = context.getResources();
    BackGround =  BitmapFactory.decodeResource(r, R.drawable.background);
    BackGround_Line =  BitmapFactory.decodeResource(r, R.drawable.background2);
    img_height= BackGround.getHeight();
    img_width = BackGround.getWidth();
}

아래 on draw 함수에는 전경을 검은색으로 칠한 후 이미지 사이즈를 이미지 크기대로 그리기 위해 스케일 조정 후 실제 canvas에 이미지를 그리는 코드를 구현하였습니다.

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawColor(Color.BLACK);
    // canvas.drawCircle(centerX,centerY,radius,wPaint);
    resizeImgBitmap = Bitmap.createScaledBitmap(BackGround, (int)(img_width), (int)(img_height), true); //사진이 클 경우, 다음과 같이 resize한다.
    resizeBackGround_Line = Bitmap.createScaledBitmap(BackGround_Line, (int)(img_width), (int)(img_height), true); //사진이 클 경우, 다음과 같이 resize한다.
    canvas.drawBitmap(resizeImgBitmap, 0, 0, null); //캔버스의 적절한 위치에 그린다.
    canvas.drawBitmap(resizeBackGround_Line, 0, 0, null);
    super.onDraw(canvas);
}

 

그리고 이를 실행하기 위해 MainActivity.java도 아래와 같이 수정합니다.

package com.accel.accello_meter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.FrameLayout;

public class MainActivity extends AppCompatActivity {
    Level_View lvl_view;//추가
    FrameLayout fL;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lvl_view = new Level_View(this);//추가

        fL = (FrameLayout)findViewById(R.id.sensorLayout) ;//추가
        fL.addView(lvl_view);
    }
}

아래 클래스 Level_View를 쓰기 위해 선언합니다. 그리고 프레임 레이아웃을 선언합니다.

Level_View lvl_view;//추가
FrameLayout fL;

Level_View를 초기화합니다. 그리고 프레임 레이아웃에 Level_view를 디자인에 적용된 프레임 레이아웃에 넣어주고 추가해 줍니다.

 

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    lvl_view = new Level_View(this);//추가

    fL = (FrameLayout)findViewById(R.id.sensorLayout) ;//추가
    fL.addView(lvl_view);
}

이제 프로그램을 가상 디바이스로  실행시켜 봅니다.

아래 그림과 같이 실행되는 것을 확인합니다.

실행된 프로그램

현재 이미지 크기대로 설정을 해서 이미지 크기로 표시가 되어 꽉 찬 화면으로 디스플레이되지 않았습니다. 이는 다음 시간에 처리해보도록 합니다. 아까 제작한 TextView도 문자 색상을 변경하지 않아 나타나지 않은 것 같습니다.

댓글


TOP

TEL. 02.1234.5678 / 경기 성남시 분당구 판교역로