IT정보공유/Android

안드로이드 움직이는 시계 소스 코드를 공개합니다.

알지오™ 2017. 6. 16.

아래사진과 같은 단순한 시계 앱 샘플입니다.

다만 시계가 가만히 있으면 재미없으니까 상하좌우로 스크린 영역내에서 왔다갔다 하는

애니메이션 기능을 추가해 봤습니다.

 

움직이는 시계 샘플 코드를 공개합니다.

 

프로젝트는 안드로이드 스튜디오의 기본 빈프로젝트로 생성했고, 

레이아웃은 별도의 XML 파일 없이 View를 상속받은 커스텀 뷰로 대체하여

onDraw 함수에서 시계를 그리도록 했습니다.

 

 

이번에 공부한 내용은 다음과 같습니다. 참고할 만한 내용은 참고하시기 바랍니다.

1. 해상도가 다른 안드로이드 기기에서도 글자 크기를 동일하게 맞추기 위해 폰트사이즈는 DP 단위로 사용

(dimens.xml 을 추가 또는 수정해야 합니다.)

 

 

2. 타이틀바와 액션바를 없애기 위한 코드가 사용되었으며,

코드방식이 아닌 styles.xml의 테마를 수정하는 방법도 사용됐습니다.

 

 

3. View를 상속받은 emptyCustomView 클래스 정의하고 onDraw() 함수를 오버라이드 하여

시계를 그렸습니다.

 

 

4. 움직이는 시계를 구현하기 위해 onDraw() 함수를 Handler()를 이용하여 주기적으로 호출합니다.

 

 

5. 소스에 친절하게 주석을 달아놨습니다.

 


//MainActivity.java

package bigenergy.tistory.com.simplefullclock;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    emptyCustomView ecv;

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



        //테마를 이용한 타이틀바 제거, 풀스크린 모드 설정
        setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);

        //타이틀 바를 없애고 풀스크린으로 만드는 다른 형태
        //requestWindowFeature(Window.FEATURE_NO_TITLE);
        //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);


        ecv = new emptyCustomView(this); //시계를 그릴 커스텀 뷰를 생성
        setContentView(ecv);              //커스텀 뷰를 콘텐트뷰로 설정

        ecv.init();                       //커스텀 뷰의 초기화 함수



    }

    @Override
    public void onBackPressed()
    {
        ecv.exitView();
        moveTaskToBack(true);
        finish();
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

 

//emptyCustomView.java

package bigenergy.tistory.com.simplefullclock;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import android.os.Message;

/**
 * Created by au on 2017-06-13.
 */

public class emptyCustomView extends View {

    //디바이스 해상도 구하기
    //private Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    //private int deviceWidth = display.getWidth();
    //private int deviceHeight = display.getHeight();

    //뷰의 사이즈 구하기
    private int deviceWidth;
    private int deviceHeight;

    //시계가 움직일 방향 플래그
    private boolean bDirectionDown = true;
    private boolean bDirectionRight = true;

    //시계가 표시될 초기 좌표
    private int initX = 0;
    private int initY = 0;

    //시계가 움직일때 가로 세로의 크기
    private int increaseX = 3;
    private int increaseY = 2;


    //OnDraw 이벤트에서 출력되는 시계의 글자의 가로, 세로 폭을 저장할 변수
    private Rect rectTimeText;


    Paint pnt = null; //시계 색상
    private Handler mHandler = null; //시계를 매번 갱신해서 그려줄 타이머

    public emptyCustomView(Context context)
    {
        super(context);
    }

    public void init()
    {
        // 시계의 색상, 글자 크기, 타이머 등의 초기화

        pnt = new Paint();
        pnt.setColor(Color.YELLOW);
        //pnt.setStyle(Paint.Style.STROKE);
        //pnt.setStrokeWidth(9);

        //글자 크기를 해상도가 다른 안드로이드 기기 마다 동일하게 구성하려면
        //sd, dp 등의 형태로 폰트 사이즈를 지정해야 하며,
        //리소스의 dimens.xml에 글자 사이즈 단위를 dp로 지정하여
        //리소스의 글자 크기를 가져와서 설정

        pnt.setTextSize(getResources().getDimensionPixelSize(R.dimen.fontsize_vert));

        rectTimeText = new Rect();

        /*
        deviceWidth = getWidth();
        deviceHeight = getHeight();

        Random r = new Random();
        initX = r.nextInt(deviceWidth) + rectTimeText.width();
        initY = r.nextInt(deviceHeight) + rectTimeText.height();
        */

        mHandler = new Handler(){
            public void handleMessage(Message msg){
                deviceWidth = getWidth();
                deviceHeight = getHeight();

                CheckFontSize();
                CalcDisplay();
                invalidate(); // onDraw 함수 호출
                mHandler.sendEmptyMessageDelayed(0, 10); //10ms마다 handleMessage(Message msg) 반복 호출, 즉 10ms 마다 다시 배경색을
            }
        };


        boolean bSend = mHandler.sendEmptyMessage(0);
    }

    private void CheckFontSize()
    {
        // 안드로이드 OS가 가로방향일때와 세로방향일때 글자 크기를 변경하기 위해
        // 방향체크와 글자 사이즈 변경 확인용 함수

        if(rectTimeText != null)
        {
            if(deviceWidth > deviceHeight)
                pnt.setTextSize(getResources().getDimensionPixelSize(R.dimen.fontsize_hori));
            else
                pnt.setTextSize(getResources().getDimensionPixelSize(R.dimen.fontsize_vert));
        }
    }

    private void CalcDisplay() // 시계를 상하 좌우로 움직이게 해줄 좌표 계산 로직
    {

        if(rectTimeText != null) {

            if (bDirectionDown)
            {
                initY += increaseY;

                if((initY) > deviceHeight)
                {
                    bDirectionDown = !bDirectionDown;
                }
            }
            else
            {
                initY -= increaseY;
                if((initY - rectTimeText.height()) <= 0)
                {
                    bDirectionDown = !bDirectionDown;
                }

            }

            if (bDirectionRight)
            {
                initX += increaseX;

                if((initX + rectTimeText.width()) > deviceWidth)
                {
                    bDirectionRight = !bDirectionRight;
                }
            }
            else
            {
                initX -= increaseX;
                if(initX <= 0)
                {
                    bDirectionRight = !bDirectionRight;
                }

            }

        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        canvas.drawColor(Color.BLACK);

        //현재 시간을 구해와서 문자열 "시간:분" 형태로 만듬
        long currNow = System.currentTimeMillis();
        Date currDate = new Date(currNow);
        SimpleDateFormat sdfNow = new SimpleDateFormat("HH:mm");
        String strDatetime = sdfNow.format(currDate);


        //글자의 가로, 세로 폭을 구함
        pnt.getTextBounds(strDatetime, 0, strDatetime.length(), rectTimeText);

        canvas.drawText(strDatetime, initX, initY, pnt);
        //canvas.drawText(Integer.toString(initX), 1, 200, pnt);
        //canvas.drawText(Integer.toString(initY), 1, 500 , pnt);
        //Log.d("TRACE", Integer.toString(rectTimeText.width()) + "/" + Integer.toString(rectTimeText.height()));

    }

    public void exitView() //타이머 종료
    {
        if(mHandler != null)
        {
            mHandler.removeMessages(0);
        }
    }
}


//dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="fontsize_vert">125dp</dimen>
    <dimen name="fontsize_hori">200dp</dimen>
</resources>

//styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- 액션바를 없애버림 -->
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

 

댓글

💲 추천 글