post list

2018년 1월 22일

[WordPress] 설치 및 환경설정

1. Web Hosting 업체를 선정

개인적으로 아래의 동영상을 참고하여 국내에서 가장 많이 사용된다는 Cafe24에서 호스팅을 하기로 하였다

https://youtu.be/-gZC5lJsyMU

Cafe24 > 10G 광아우토반 Full SDD > 일반형으로 신청 > php5.0 워드프레스 지원용으로 결제

결제 후 > 나의 서비스 관리 > 왼쪽 서비스 접속 관리 > 웹 FTP >
여기서 도메인과 접속포트 그리고 비밀번호를 기억한 다음에 ForkLift 앱으로 접속


2. WordPress 설치

wordpress.com 에서 wordpress 프로젝트를 다운로드하고 압축을 푼다

웹호스팅 도메인으로 FTP 접속하여 wordpress 프로젝트를 모두 업로드한다


3. 시작 설정

도메인으로 접속해 보면 데이터베이스 설정을 하라고 나온다

Database Name : FTP 접속할때의 아이디
Username : FTP 접속할때의 아이디
Password : FTP 접속 비밀번호
Database Host 와 Table Prefix는 그대로 두면 된다

다음으로 넘어가면

사이트 제목, 사용자명, 비밀번호를 입력하도록 하는데,
사이트에 대한 admin 계정을 생성하는 것이다

보통 사용자명은 admin으로 하고, 다른 것들은 수정가능하므로 아무거나 하자

여기까지 하고 나면 신청한 도메인 접근이 가능하다

2018년 1월 4일

Ref. for English

- be used to (adj.) vs used to (verb)
http://www.5minuteenglish.com/mar20.htm

2017년 5월 23일

[일본] 도쿄 여행 준비 지식 및 경험 나눔

일본 여행 전 준비했던 것은 비행기표, 숙소, USIM, 기모노 체험예약, 환전 그리고 교통편이었다

비행기표는 SkyScanner를 이용해서 진에어공항으로 예매 했다

숙소는 신주쿠 가부키초 근처에 있는 Gran Bell Hotel에서 3박 일정으로 미리 예약했는데 가부키초가 워낙 외국인들이 많이 오는 곳이라 그런지 호텔에 한국인이 있어서 편하게 체크인/체크아웃을 했다.

USIM은 미리 말톡을 통해서 LTE 무제한으로 구입을 해두었다. 수령은 인천공항이나 택배를 통해서 할 수 있는데, 어차피 사용은 일본에서만 가능하므로 인천공항으로 수령하기로 해뒀다. 인천공항에서 수령장소는 인천공항 지하 1층에서 무빙워크타고 교통센터로 가면 탑항공이라고 있다. 거기서 수령할 수 있었다. 구입시 유심교체핀과 유심 어댑터를 준다. 해당 도구들을 이용해 일본 도착시 유심을 갈아끼우면 된다. 유심 교체 후에 설정하는 것이 상품마다 다르고 안드로이드, 아이폰 별로 다르니 잘 확인해야 한다

인천공항이 워낙 넓어서 항공사별 탑승수속 카운터 위치를 모를 수 있는데, 인터넷으로 검색하거나 직접가서 맵을 보던지 물어보면 된다. 비행기 출발 2시간 전에 도착했더니 넉넉하게 쇼핑까지 하고 기다리다가 탔다.

맨날 까먹는데... 기내수화물(비행기 내에 반입물건)과 위탁수화물(캐리어따위)에 제한이 있다.  액체나 흉기류는 기내로 가지고 타면 안된다.  물, 스프레이, 맥가이버칼 따위이다. 캐리어에는 보조배터리를 넣어서는 안된다. 보조배터리는 기내로 들고 타야한다

기모노 체험예약은 검색하면 몇 군데 나오는데... 미리 예약을 안하고 가면 빌려주질 않는 것 같았다. 돈은 예약한 날짜에 맞춰서 직접 가서 지불하면 된다

환전은 위비톡으로 90% 우대하여 환전했다. 6만엔 정도를 들고 갔는데, 3박 4일 정도로 적당한 듯 했다. 많이 썼나 싶기도 하고..

교통편은 꼭 미리 알아보고 가야한다. 일본은 교통비가 창렬하다. 우리는 나리타에서 스카이라이너 왕복 + 도쿄 서브웨이 72시간 티겟을 끊어서 사용했다. 1인 5400엔인데 3박 4일 동안 굉장히 편리하게 이용할 수 있었다. 구입 방법은 나리타 공항에서 지하로 내려가서 인포메이션을 찾아서 구입해야한다


- 1일차
오후 비행기여서 느즈막히 출발했고, 출발 2시간 전에 도착했다. 탑승수속, 수화물 위탁은 오래 걸리지 않았고, 출국장에서는 보안검색/출국심사 또한 빠르게 끝났다.  여자친구가 미리 면세점에서 구입한 물품이 있어 미리 가서 물품을 찾기도 했다. 그래도 시간이 남아서 카페에서 음료좀 마시다가 비행기 타고 출발했다

일본 나리타 공항에 도착하게 되면 내국인 외국인 별로 따로 입국심사를 하게 된다. 별거 없었음. 나리타 공항에서 지하로 내려가서 스카이라이너 케이세이선을 타고 우에노까지 갔다. 우에노에서는 도쿄 서브웨이 티겟을 이용해서 히가시 신주쿠까지 이동했다. 갈아타는 것이 헷갈릴 수 있으나 구글맵과 함께라면 무섭지 않을 것이다

가부키초 Gran Bell Hotel호텔은 온갖 국가에서 온 외국인들도 많았다. 모르는 사람이라도 헬로우하면서 인사를 해줘서 나도 인사해줬다 (부끄러워 말고 당당하게 헬로우!) 해당 호텔에서 가장 가까운 역은 히가시신주쿠였는데, 걸어서 5분 정도라 꽤 괜찮았다. 그런데 익숙하질 않아서 오고 갈때마다 구글맵으로 길을 찾아다녔다


- 2일차
이 날은 미리 기모노 렌탈을 예약해두었는데, 마침 아사쿠사 축제여서 입고 돌아다녀도 1도 이상하지 않았다. 우리 이외에도 많은 사람들이 입고 돌아다녔고... 사람이 너무 많아서 그런지 별로 관심 받지도 않았다.... 만 웬 양덕들이 자꾸 사진 좀 찍자고 그래서 귀찮긴 했다. 신기한건 영어로 하는 것도 아니고 일본어로 사진좀 찍어도 괜찮냐고 물어보더라

아사쿠사는 확실히 축제 기간에 가면 좋은 것이 많다. 길거리에 온갖 맛있는 군것질거리들이 넘처나고 마을이 활기가 넘친다. 물론 단점은 사람이 너무 많아서 불편한 것도 있긴 한데 막 오래 기다려야 하는 일은 별로 없었다. 한해의 점을 보는 미쿠지도 해봤는데 Bad fortune 이 나와서 깜짝 놀래서 해석해보니 온갖 악담과 저주가 적혀 있었다. 얼른 종이를 접어서 걸어놓고 기부로 1원 던져놓고 왔다. 연기를 들이마시는 행위, 물로 손씻는 행위등도 체험했다. (행위라고 적은 이유는 재미는 있었으나 의미가 없기 때문이다) 

2-3시간 돌아다니고 나니까 녹초가 되어서 집으로 돌아왔다. 저녁에는 쇼핑을 했는데.. 특히 드럭스토어에서 물건을 많이 샀다. 일본은 Tax Free 제도가 잘되어 있어서 그런지 외국인들이 물건을 엄청나게 사가더라. 어떤 중국인은 내 눈앞에서 폼클렌징 100개를 사갔다! 그 외에도 5만원 이상만 사면 Tax Free가 되다보니 정말 쇼핑하기 좋긴 했다


- 3일차
이 날은 도쿄에서 멀리 떨어진 히타치 해변공원으로 갔다. 히타치 해변공원이 어떤 곳인지는 검색하면 나오니까 생략. 히타치 해변 공원으로 가려면 우에노 뭐시기 역으로 가서 히타치 특급 열차를 타야했다. 히타치 특급열차 가격이 3820엔. 여튼 타고 도착하면 가쓰타 (Katsuta)역에 도착하게 되는데 거기서 100엔을 내고 버스를 타면 해변공원까지 데려다준다. 400엔을 내면 더 빨리 가는 버스도 있었다. 플랫폼에서 1200엔을 내면 하루 종일 탈 수 있는 티켓을 파는데 무슨 버스인지 모르겠다. 잘못했다가는 1200엔 내고 엄한데 돌아다닐뻔 했다. 우리는 근처에 계시던 할머니 한분이 도와주셔서 잘 타고 갔다

히타치 해변공원은 4월 중순에서 5월 중순 사이가 절정인 것 같았다. 우리는 5월 말에 갔더니 꽃들이 반쯤 사그라 들었더라

돌아갈때도 100엔 혹은 400엔 버스타고 가쓰타로 온다음 특급열차 3820엔 내고 집으로 돌아왔다. 역시 일본은 교통비가 창렬하다

밤에는 사촌동생과 시부야에서 만나서 샤브샤브를 먹었다. 3명이서 1만엔 정도가 나왔다. 그리고 드럭스토어에서 1.6만엔치 물건을 사서 돌아왔다


- 4일차
무사히 4일차가 되었고 미리 예매해뒀던 스카이라이너 케이세이선의 티켓을 이용해서 나리타로 돌아왔다. 비행기 타고 한국으로 복귀







2017년 5월 9일

[Eclipse for Mac] C++ 개발 환경 구축

1. 이클립스 설치
검색해서 최신 버전의 이클립스를 설치하자

2. install new software 에서 CDT 검색해서 모두 체크후 설치

3. C++ 프로젝트 개설한 후에 빌드시 에러가 나면 아래와 같이 함

The fix go into Project > Properties > C/C++ Build > Settings > Binary Parsers : Make sure "Mach-O 64 Parser is selected"


4. debugging이 안될 경우 compiler를 gdb에서 lldb로 바꿈 

(https://wiki.eclipse.org/CDT/User/FAQ#How_do_I_get_the_LLDB_debugger.3F)

[Eclipse C++] Dark theme 최적화 시키기

 Preferences > General > Appearance 에서 기본 Theme로 제겅되는 Dark를 적용시키면 배경이 Dark라는 이름에 맞게 변경된다. 하지만 세세한 부분에서 불편함을 초래하는 것들이 몇가지 있어서 기록해 둔다. 매번 Eclipse 환경설정할때마다 까먹어서 귀찮다.

1. Font Size
Font Size는  Appearance > Colors and Fonts 에서 Basic > Text Font의 Size를 변경하면 Code Font 사이즈가 설정된다. 나는 14가 적당한 것 같다.

2. 선택한 글자와 동일한 글자들 Highlight 시 잘 안보이는 문제 교정
General > Editors > Text Editors > Annotations > C/C++ Occurrences 와 C/C++ Write Occurrences에서 Text as highlighted 의 Color를 수정해주면 된다 (Color는 Tin을 추천)

(http://stackoverflow.com/questions/10310597/cannot-highlight-all-occurrences-of-a-selected-word-in-eclipse)


3. Market플레이스에서 color theme 설치
eclipse market place 에서 theme을 검색해서 Color theme을 설치하면 다양한 theme이 나온다. 그 중에 하나 골라쓰는것도 괜츈! Dark 랑 어울리는 Zenburn이 제일 취향에 맞다

2016년 7월 8일

[Data Structure] Binary Indexed Tree


오늘은 Binary Indexed Tree 를 정리해볼까 한다. 나만을 위한. 나를 위한 정리다.

Binary Indexed Tree의 존재 이유는 부모노드가 자식노드들을 대표할 수 있게 하기 위함인데 자식들의 합이 부모노드가 된다. 예를 들어 방대한 수의 배열이 있을 때 그것들을 다 더하다가 시간이 다 지나는 경우에 쓰기 적합하다. 최솟값, 최댓값도 적용 가능한 대표적인 예다.

Binary 라는 이름이 붙은 것 답게 부모 노드는 자식 노드를 오직 2개만 갖게 된다.
기본적인 개념은 아래와 같다.





검색 하다보니 정말 잘 정리된 사이트를 찾았다.
https://www.acmicpc.net/blog/view/21
위의 설명을 손으로 따라간 흔적을 여기에 남긴다.











해당 블로그에 있는 코드

#include <cstdio> #include <vector> using namespace std; long long sum(vector<long long> &tree, int i) { long long ans = 0; while (i > 0) { ans += tree[i]; i -= (i & -i); } return ans; } void update(vector<long long> &tree, int i, long long diff) { while (i < tree.size()) { tree[i] += diff; i += (i & -i); } }





2016년 2월 26일

[Algorithm] Prim 과 Kruskal 그리고 Dijkstra 와 Floyd

이 4가지 알고리즘이 자꾸만 헷갈려서 심플하게 정리해 둔다.


- Prim과 Kruskal : MST (Minimumm Spanning Tree) 를 만드는 알고리즘

Prim 은 시작 정점부터 시작해서 가까이에 있는 정점으로 가는 패스 중 가장 최소값을 찾아서 연결해 나가는 방식이다. Greedy 적인 요소가 다분하다. 또한 Shortest Path를 찾는 알고리즘인 Dijkstra와 아주아주아주아주 유사하다.

반면 Kruskal은 Heap과 Union-Find 자료구조를 이용해서 MST를 만드는데 전체에서 가장 작은 Path들을 찾아서 서로서로 연결시켜 나가면서 MST를 만든다. Kruskal도 Greedy 적이다.



- Shortest Path 를 찾는 알고리즘 : Dijkstra 와 Floyd

Dijkstra는 시작점에서 가장 가까운 정점들 중 가장 최소값을 가진 Path를 따라간다. Prim과 매우 유사하다.

Floyd는 DP(Dynamic Programming) 방식으로 전체를 모두 스캔해 가면서 개선한다... 코드가 아주 짧은 것이 특징!

정리 종료!!


2015년 8월 18일

[Tizen] EFL을 위한 Vim 환경 설정


EDC Support 파일 다운로드를 다운로드 하자. 아래의 링크에서 Zip을 다운로드 받는다

https://git.enlightenment.org/editors/vim-configs.git/tree/syntax

다운로드 된 파일은 압축을 풀어서 ~/vim/bundle/ 에 넣어 놓는다.

끝!



# 참고 : https://github.com/vim-scripts/edc-support

# 참고 : http://storycompiler.tistory.com/57


































[Tizen] EFL Relative



description {
..
rel1 {
relative: 0.0 0.0;
offset: 0 0;
to: [another part's name]
}
..
rel2 {
relative: 1.0 1.0;
offset: -1 -1;
to: [another part's name]
}
..
}
The rel1 and rel2 blocks are used to define the position of each corner of the part's container. With rel1 being the left-up corner and rel2 being the right-down corner.



EFL에서 좌표는 왼쪽 상단을 기준(Origin)으로 한다. 즉, (0,0)은 왼쪽 상단을 의미하며 (1,1)은 오른쪽 하단을 의미한다.

relative 속성에서 rel1 은 왼쪽 위의 좌표를 의미하며, rel2는 오른쪽 아래의 좌표를 의미한다. 이 두 좌표로 하나의 Part의 위치를 지정한다. 즉, description이라는 상자의 위치를 정하는데 사용 하는 block 이다.


relative (in rel1 or rel2) : to 속성에 지정된 Part를 기준으로 상대적인 좌표를 지정하는 속성이다. relative [x axis] [y axis] 로 두개의 인자를 가진다. x axis가 0일 경우 to에 지정된 part의 왼쪽 x좌표를 가져오며 1일 경우 맨 오른쪽 x좌표를 가져온다. y axis도 마찬가지다.



relative [X axis] [Y axis]
Moves a corner to a relative position inside the container of the relative "to" part. Values from 0.0 (0%, beginning) to 1.0 (100%, end) of each axis.



offset (in rel1 or rel2) : to 에 지정된 Part와의 절대적인 거리를 둔다




offset [X axis] [Y axis]
Affects the corner position a fixed number of pixels along each axis.



to (in rel1 or rel2) : 어떤 대상을 기준으로 삼을 것인지 지정하는 속성이다. part name을 적어주면 된다.



to [another part's name]
Causes a corner to be positioned relatively to another part's container. Setting to "" will un-set this value for inherited parts.



to_x (in rel1 or rel2) : x 좌표만 기준으로 삼을 part의 name을 지정하는 속성이다. relative 에서 [x axis] 에 지정된 값이 to_x로 지정된 part의 x값을 결정하게 된다.



to_x [another part's name]
Causes a corner to be positioned relatively to the X axis of another part's container. Simply put affects the first parameter of "relative". Setting to "" will un-set this value for inherited parts.


to_y (in rel1 or rel2) : to_x와 마찬가지다. to_x 와 to_y가 겹치는 곳 사이가 결국 기준점이 된다.


to_y [another part's name]
Causes a corner to be positioned relatively to the Y axis of another part's container. Simply put, affects the second parameter of "relative". Setting to "" will un-set this value for inherited parts.
































2015년 8월 17일

[Tizen] EFL Text


part {

description {
..
text {
text: "some string of text to display";
font: "font_name";
size: SIZE;
text_class: "class_name";
fit: horizontal vertical;
min: horizontal vertical;
max: horizontal vertical;
align: X-axis Y-axis;
source: "part_name";
text_source: "text_part_name";
ellipsis: -1.0 (since 1.8), 0.0-1.0;
style: "stylename";
}
..
}
}

 

min (in text block) : 만약 0이 아닌 어떤 값(보통 1)이 horizontal 이나 vertical 위치에 들어가게 되면 아무리 크기를 얼마로 잡아놨든 글자에 맞게 최소 크기를 확장된다. 본래 크기가 크다면 상관 없지만 글자영역보다 작다면 확장된다는 의미다.

max [horizontal] [vertical]
 When any of the parameters is enabled (1) it forces the maximum size of the container to be equal to the maximum size of the text. The default value is "0 0".




max (in text block) : 만약 0이 아닌 어떤 값(보통 1)이 horizontal 이나 vertical 위치에 들어가게 되면 아무리 크기를 얼마로 잡아놨든 글자에 맞게 최대 크기가 확장된다. max 와 min의 차이가 뭘까.


max [horizontal] [vertical]
 When any of the parameters is enabled (1) it forces the maximum size of the container to be equal to the maximum size of the text. The default value is "0 0".














ellipsis (in text block) : 글자 수가 많아서 본래 크기를 넘어버릴 때 생략 (...)을 한다. 0.0이면 글자의 앞을 살리고(끝을 생략한다는 의미) 1.0이면 글자의 뒤를 살린다(앞을 생략한다). 0.5라면? 중간을 살리고 양쪽을 생략해버린다.

ellipsis [point of balance]

Used to balance the text in a relative point from 0.0 to 1.0, this point is the last section of the string to be cut out in case of a resize that is smaller than the text itself. The default value is 0.0.



아래는 EFL 문서에 있는 본문이다.
(https://docs.enlightenment.org/auto/edje/edcref.html)


2015년 8월 13일

[Cocos2d-x] 화면 사이즈

http://egloos.zum.com/icegeo/v/298307

[Android] Service

Service는 Thread와 마찬가지로 백그라운드 작업자다. 그러나 가장 큰 차이점은 명시적으로 Kill 하지 않으면 죽지 않는다는 점인데, 명시적 Kill 하지 않았는데 죽을 경우에는 다시 살아난다.

1. Unbound Service (or Started Service)
startService(); 로 호출

이러한 서비스는 작업 결과를 돌려주기 위해서는 BroadcastReceiver를 만들어서 결과를 받아야 한다.

그 Receiver는 Activity 와 상호 작용하여 UI를 업데이트 해야 한다.


2. Bound Service
bindService(); 로 호출

Unbound Service와 달리 Activity와 즉각 상호작용이 가능하다.



p.s 간단하게 썼지만 BroadcastReceiver 와 Activity간의 상호작용은 꽤나 어렵다. 다음의 예제에서 답을 찾을 수 있다.


package com.example.usecalcproject;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.example.calcproject.ICalc;

public class MainActivity extends Activity implements View.OnClickListener {

private EditText mEditExpr1;
private Button mBtnCalc1;
private TextView mtxtResult1;

private EditText mEditExpr2;
private Button mBtnCalc2;
private TextView mtxtResult2;

private BroadcastReceiver mBR;

/* 옵션 구현(1/5) */
ICalc mCalc;

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

mEditExpr1 = (EditText) findViewById(R.id.editExpr1);
mBtnCalc1 = (Button) findViewById(R.id.btnCalc1);
mBtnCalc1.setOnClickListener(this);
mtxtResult1 = (TextView) findViewById(R.id.txtResult1);

mEditExpr2 = (EditText) findViewById(R.id.editExpr2);
mBtnCalc2 = (Button) findViewById(R.id.btnCalc2);
mBtnCalc2.setOnClickListener(this);
mtxtResult2 = (TextView) findViewById(R.id.txtResult2);

if (savedInstanceState != null) {
String expr1 = savedInstanceState.getString("expr1", "");
mEditExpr1.setText(expr1);
String result1 = savedInstanceState.getString("result1", "");
mtxtResult1.setText(result1);
String expr2 = savedInstanceState.getString("expr2", "");
mEditExpr2.setText(expr2);
String result2 = savedInstanceState.getString("result2", "");
mtxtResult2.setText(result2);
}

mBR = new MyReceiver();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("expr1", mEditExpr1.getText().toString());
outState.putString("result1", mtxtResult1.getText().toString());
outState.putString("expr2", mEditExpr2.getText().toString());
outState.putString("result2", mtxtResult2.getText().toString());
}

// Inner class
// Receiver가 Activity에 접근하는 하나의 방법
private class MyReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String result = intent.getStringExtra("result");
mtxtResult1.setText(result);
}
}

@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter("example.mytest.CALCRESULT");
registerReceiver(mBR, filter);

/* 옵션 구현(2/5) */
Intent i = new Intent();
i.setClassName("com.example.calcproject", "com.example.calcproject.CalcService");
bindService(i, srvConn, BIND_AUTO_CREATE);
}

@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mBR);

/* 옵션 구현(3/5) */
unbindService(srvConn);
}

/* 옵션 구현(4/5) */
ServiceConnection srvConn = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
mCalc = ICalc.Stub.asInterface(binder);
}

@Override
public void onServiceDisconnected(ComponentName className) {
mCalc = null;
}
};

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnCalc1:
if (mEditExpr1.getText().length() > 0) {
Intent i = new Intent();
i.setClassName("com.example.calcproject", "com.example.calcproject.CalcService");
i.putExtra("expr", mEditExpr1.getText().toString());
startService(i);
}
break;
case R.id.btnCalc2: /* 옵션 구현(5/5) */
if (mEditExpr2.getText().length() > 0) {
try {
String result = mCalc.calc(mEditExpr2.getText().toString());
mtxtResult2.setText(result);
}
catch (RemoteException e) {
e.printStackTrace(); // Security risk!
}
}
break;
}
}
}



해결 방법 중 하나인 AIDL 파일
package com.example.calcproject;

interface ICalc {
String calc(String expr);
}


[Android] Thread

DDMS 에서 원하는 앱을 선택하고 왼쪽고 상단에 화살표 세개 모양의 아이콘을 눌리고 오른쪽 창에서 같은 아이콘 모양의 Threads를 눌리면 현재 앱의 Thread 상태들을 볼 수 있다.

Android는 다음의 방식으로 Thread를 지원한다.

1. Looper
- 메시지 큐를 생성하고 초기화
- 메시지 큐에서 데이터를 꺼내서 Handler에게 전달

2. Handler
- Looper가 전달해 준 데이터를 처리
- 메시지 큐에 데이터를 삽입


UI Main Thread 도 Looper를 가지고 있으나 우리의 코드에 보이지는 않는다. 다만 Handler를 붙여서 Looper를 이용할 수는 있다.

보통 UI Thread와 다른 Thread (이하 Worker Thread) 간의 통신을 위해서 Handler를 사용하게 되는데 양방향 통신을 위해서는 Handler 또한 양 쪽 Thread 모두에게 붙여야 한다. Handler는 UI Thread와 Worker Thread에서 모두 접근 가능하도록 코드를 작성 해야 한다.

예제는 다음과 같다.

public class C16_Handler extends Activity {

int mMainValue = 0;
int mBackValue = 0;
TextView mMainText;
TextView mBackText;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.c16_thread);

mMainText = (TextView) findViewById(R.id.mainvalue);
mBackText = (TextView) findViewById(R.id.backvalue);

BackThread thread = new BackThread();
thread.setDaemon(true);
thread.start();
}

public void mOnClick(View v) {
mMainValue++;
mMainText.setText("MainValue : " + mMainValue);
}

class BackThread extends Thread {

@Override public void run() {
while (true) {
mBackValue++;
// mHandler.sendEmptyMessage(0);
Message msg = new Message();
msg.what = 0;
msg.arg1 = 0;
msg.arg2 = 0;
msg.obj = null;
mHandler.sendMessage(msg);

try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
;
}
}
}
}

Handler mHandler = new Handler() {

@Override public void handleMessage(Message msg) {
if (msg.what == 0) {
mBackText.setText("BackValue : " + mBackValue);
}
}
};
}


Message를 다루는 방법도 여러가지가 있는데 여기서는 다루지 않겠다. 다만 다음과 같이 Pool 방식도 있음을 알아두자.

Message msg = Message.obtain();


다음은 명시적으로 Looper를 이용하는 방식의 예제다.


public class C16_Looper extends Activity {

int mMainValue = 0;

TextView mMainText;
TextView mBackText;
EditText mNumEdit;

CalcThread mThread;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.c16_looper);

mMainText = (TextView) findViewById(R.id.mainvalue);
mBackText = (TextView) findViewById(R.id.backvalue);
mNumEdit = (EditText) findViewById(R.id.number);

mThread = new CalcThread(mHandler);
mThread.setDaemon(true);
mThread.start();
}

public void mOnClick(View v) {
Message msg;

switch (v.getId()) {
case R.id.increase:
mMainValue++;
mMainText.setText("MainValue : " + mMainValue);
break;
case R.id.square:
msg = new Message();
msg.what = 0;
msg.arg1 = Integer.parseInt(mNumEdit.getText().toString());
mThread.mBackHandler.sendMessage(msg);
break;
case R.id.root:
msg = new Message();
msg.what = 1;
msg.arg1 = Integer.parseInt(mNumEdit.getText().toString());
mThread.mBackHandler.sendMessage(msg);
break;
}
}

Handler mHandler = new Handler() {

@Override public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
mBackText.setText("Square Result : " + msg.arg1);
break;
case 1:
mBackText.setText("Root Result : " + ((Double) msg.obj).doubleValue());
break;
}
}
};
}

class CalcThread extends Thread {

Handler mMainHandler;
Handler mBackHandler;

CalcThread(Handler handler) {
mMainHandler = handler;
}

@Override public void run() {
Looper.prepare();

mBackHandler = new Handler() {

@Override public void handleMessage(Message msg) {
Message retmsg = new Message();
switch (msg.what) {
case 0:
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
;
}
retmsg.what = 0;
retmsg.arg1 = msg.arg1 * msg.arg1;
break;
case 1:
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
;
}
retmsg.what = 1;
retmsg.obj = new Double(Math.sqrt(msg.arg1));
break;
}
mMainHandler.sendMessage(retmsg);
}
};

Looper.loop();
}
}






[Android] SaveInstanceState

안드로이드는 화면 회전이 일어나면 onCreate가 다시 호출된다. 게다가 변수들의 값까지 초기화 되어 버린다. 이럴 때 사용하는 것이 onSaveInstanceState 함수이다.

아래와 같이 쓸 수 있다.

private int num = 0;

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

if(savedInstanceState != null){
//화면 회전 등으로 인한 일시적인 파괴 후 재생성
num = savedInstanceState.getInt("num", 0);
}

findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("num", num);
}

[Android] Preference

Context.getSharedPreference() : 응용프로그램 간에 서로 공유할 수 있다.
저장되는 데이터는 다음의 경로에 저장된다.

/data/data/com.your.package/shared_prefs/com.your.package_preferences.xml

반면 Activity.getPreference() 는 해당 어플리케이션만 사용할 수 있다.



예제 코드

public class MainActivity extends Activity {

private EditText edit;
private Button btn;

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

edit = (EditText) findViewById(R.id.editText1);
edit.setOnKeyListener(new View.OnKeyListener() {

@Override public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
Toast.makeText(MainActivity.this, edit.getText().toString(), Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});

btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {

@Override public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ModifyText.class);
startActivity(intent);
}
});
}

@Override protected void onPause() {
super.onPause();
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
// SharedPreferences setting = getPreferences(MODE_PRIVATE);
Editor editor = setting.edit();
editor.putString("mytext", edit.getText().toString());
editor.commit();
}

@Override protected void onResume() {
super.onResume();
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
// SharedPreferences setting = getPreferences(MODE_PRIVATE);
edit.setText(setting.getString("mytext", ""));
}
}

[Android] Adapter

Grid View 로 예를 들어보자. 우선 XML 파일은 다음과 같다.

<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="90dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" />

android:horizontalSpacing 은 아이템 간의 간격
android:numColumns 는 컬럼의 갯수를 알아서 처리함을 의미
android:stretchMode 는 남는 공간을 위에서 지정한 90dp 에 더해서 아이템을 늘린다.


class 파일은 다음과 같다.

GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));

gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(v.getContext(), position + " selected!", Toast.LENGTH_SHORT).show();
}
});

보통 위와 같은 3가지 로직을 거친다. 뷰를 가져오고, 어댑터를 붙이고, 리스너를 붙인다.

뷰를 가져오는 것은 쉬우니 생략한다. 어댑터를 구현하는 방법을 알아보자. 안드로이드에서 제공하는 기본 어댑터보다는 우리가 만들어보자.

public class ImageAdapter extends BaseAdapter {

private Context mContext;

public ImageAdapter(Context c) {
mContext = c;
}

public int getCount() {
return mThumbIds.length;
}

public Object getItem(int position) {
//return "position " + position + " data";
return null;
}

public long getItemId(int position) {
return position;
}

// Create a new ImageView for each item referenced by the Adapter.
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = (ImageView) convertView;

if (imageView == null) { // If it's not recycled, initialize some attributes.
LayoutInflater inflater = LayoutInflater.from(mContext);
imageView = (ImageView) inflater.inflate(R.layout.my_list_item, parent, false);
}

imageView.setImageResource(mThumbIds[position]);
return imageView;
}
}

핵심적인 코드는 getView 함수다. 그 중에 convertView는 재활용 가능한 뷰로서 null 일때만 만들어주면된다. 리턴되는 view는 그리드 뷰에서 하나의 공간을 차지하는 View를 의미한다. 여기서는 단순 이미지를 리턴한다. 좀 더 복잡한 View를 만들고 싶다면 my_list_item.xml 을 수정하면 된다. 지금은 ImageView만을 리턴하기 때문에 my_list.item 은 다음과 같이 구현되어 있다.

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/imgItem"
    android:layout_width="85dp"
    android:layout_height="85dp"
    android:padding="5dp"
    android:scaleType="centerCrop" >
</ImageView>


2015년 8월 12일

[Cocos2d-x] Android 개발 환경 구축

우선 기본적인 Cocos2d-x 개발 환경이 구축되었다는 가정하에 이 포스팅을 쓴다. 기본적인 Cocos2d-x 개발 환경 구축이 되어 있지 않다면 아래의 포스트를 따라한 뒤에 다시 이 곳으로 돌아오길 바란다.

http://nospblog.blogspot.kr/2015/05/tizen.html

cocos2d-x, python 의 환경 변수 설정이 되어 있고 새로운 프로젝트까지 무사히 완성했다면 Android 에서 포팅할 수 있고 개발도 할 수 있는 환경을 마련해보자. (참고로 JDK설치는기본이다? 당연히 설치해야한다. 이것도 모른다면 다른 곳에서 설치하는 방법을 알아보고 오라!)


1. Eclipse 설치
Eclipse 를 설치한다. 아래의 링크에서 CDT가 아닌 Java EE 버전을 설치한다.

https://eclipse.org/downloads/


2. CDT Plugin 설치
Eclipse에서 Eclipse Marketplace에 접속해서 CDT 라고 검색하면 Eclipse CDT가 검색되며 그것을 선택해서 설치하면 된다.


3. NDK 설치
아래의 링크에서 Download 항목에서 NDK를 다운로드 받는다.

https://developer.android.com/ndk/downloads/index.html

다운로드 파일을 실행시키면 꽤나 오랫동안 압축을 풀게 된다. 압축을 풀게 되면 NDK 폴더가 생성이 될 것인데 자신이 원하는 곳으로 옮기도록 하자.  /Document 폴더에 두기로 한다면... 다음과 같은 경로가 나올 것이다. 잘 기억해 둔다.

C:\Users\deokil\Documents\android-ndk-r10e\


4. ADT 설치
사실 이제는 Android Studio 로 합쳐져 버린 녀석이다. 하지만 우리는 Cocos2d-x를 위해서 따로 설치를 해야한다. Eclipse에 접속한 뒤에 Help > Install new software를 선택한다. 그리고 Work with 항목에 다음의 링크를 입력해 준다.

https://dl-ssl.google.com/android/eclipse/

곧 Developer Tools 라는 항목이 뜰 것이다. 체크 한 뒤에 설치한다. 혹시나 사내망에서 설치를 한다면 Proxy 설정을 해줘야 한다는 점을 명시하자.


5. SDK 설치
설치하고 나서 Eclipse를 실행시키면 SDK 가 없다고 난리를 부린다. 예전에는 SDK마저 따로 설치해 줘야 했지만 지금은 그렇지 않다. 경고들을 취소 시키고 보면 SDK를 설치 하겠냐는 창이 있다.

기본적인 설치가 끝나면 추가적인 설치가 필요하다면서 SDK Manager를 열것을 요구한다. SDK Manager를 열어서 Android 2.3.3을 설치하자.


6. Import Projects
SDK 까지 설치가 모두 끝났다면 이제 프로젝트를 가져와야 한다. 코코스 라이브러리와 안드로이드 프로젝트를 들고 와야된다. 즉, 프로젝트 2개를 가져와야 한다.

먼저 Package Explorer에서 마우스 오른쪽을 눌러 Import를 선택한다. 그리고 Root Directory 항목을 선택한 후, 우리가 미리 만들어 두었던 새로운 프로젝트를 선택하도록 한다. 선택하고 나면 엄청난 양의 프로젝트가 선택이 되어 있을 것이다. 우선 Deselect All을 선택해서 모두 선택 해제 한다.

우리에게 필요한 프로젝트는 다음과 같다.

1. cocos2d\cocos\platform\android\java
2. proj.android


7. Property Setting
libcocos2dx 프로젝트에서 마우스 오른쪽 버튼을 눌러 Properties를 선택하고 Android 항목에서 Target Name 이 Android 2.3.3을 체크하고 아래에서 Is Library를 체크한다. Okay를 눌러 저장한다.

이번에는 실제 프로젝트에서 Properties 를 선택하고 Android 에서 Target Name 이 Android 2.3.3을 체크한다. 그리고 아래의 Library 항목에서 Add를 눌러 libcocos2dx를 선택한다. 혹시 이미 들어가 있다면 Remove로 제거하고 다시 추가한다.


8. Ant 설치
아래 링크에 접속한다.

http://ant.apache.org/bindownload.cgi

Current Release of Ant 항목 조금 아래에 보면 Zip archive 라고 다운로드 링크가 있을 것이다. 다운 받아서 설치한다. 압축을 풀고 나면 ant 폴더가 하나 생성될 것인데 자신이 원하는 곳에 옮겨놓는다. 나의 경우 C:\apache-ant 로 두었다.


9. 환경 변수 설정
우리는 다음 5개를 환경변수로 설정해 주어야 한다.

1. cocos2d
2. ndk
3. android sdk
4. python
5. ant

만약 하나라도 환경변수로 등록하지 않으면 작동이 되지 않으니 환경 변수 설정을 꼼꼼히 하자. 다음의 절차를 따르자.

제어판 > 시스템 > 고급 시스템 설정 > 환경변수 > 시스템 변수

그리고 우리가 설정해야 할 환경 변수 경로가 다음과 같다면

1. cocos2d
C:\Users\deokil\Documents\cocos2d-x-3.5-tizen\tools\cocos2d-console\bin\

2. ndk
C:\Users\deokil\Documents\android-ndk-r10e\

3. android sdk
C:\Users\deokil\android-sdks\

4. python
C:\Python27\

5. ant
C:\apache-ant-1.9.6\bin\


아래의 새로 만들기 버튼을 눌러서 변수 이름과 변수 값을 다음과 같이 설정한다.

변수이름 : 변수값
NDK_ROOT : 위에 적힌 ndk 경로
COCOS_CONSOLE_ROOT : coco2d 경로
ANDROID_SDK_ROOT : sdk 경로
ANT_ROOT : ant 경로

그리고 최종적으로 Path를 수정해야 한다. 여기서 Path를 수정하는데 하나의 환경 변수를 설정하고 나면 끝에 반드시 ; 를 붙이고 다른 환경 변수의 경로를 적어줘야 한다. 예를 들자면 이미 시스템 변수로 다음과 같은 Path가 있었다고 가정하자.

C:\ProgramData\Oracle\Java\javapath

그럼 다음과 같이 적어준다.

C:\ProgramData\Oracle\Java\javapath;C:\Users\deokil\Documents\cocos2d-x-3.5-tizen\tools\cocos2d-console\bin\;C:\Users\deokil\Documents\android-ndk-r10e;
와 같은 방식으로 모두 적어주어야 한다. 명심할 것은 경로 사이에 ; 가 있다는 것이다.

(사실 이렇게까지 안해도 개발은 할 수 있지만 나중에 편해진다)


10. Android 빌드 준비
Windows Key + R 을 눌러 cmd를 입력한다. cocos2d가 설치된 폴더로 이동해서 다음의 명령어를 입력한다.

python Setup.py

만약 중간에 not found라고 뜬다면 환경 변수 설정이 잘못된 것이니 9번 항목을 다시 해보길 권한다.

이제 다시 이클립스를 켜고 프로젝트를 연다. 프로젝트 하위 폴더 중에 jni 라는 폴더가 있을 것이데 내부에 Android.mk 라는 폴더를 열어보자. 그리고 다음과 같이 수정한다.

우선 LOCAL_SRC_FILES를 다음과 같이 주석처리한다.

#LOCAL_SRC_FILES := hellocpp/main.cpp \
#                   ../../Classes/AppDelegate.cpp \
#                   ../../Classes/HelloWorldScene.cpp

그 밑에 다음의 내용을 추가 한 후 저장한다.

FILE_LIST := $(wildcard $(LOCAL_PATH)/../../Classes/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES += hellocpp/main.cpp


11. 빌드
먼저 libcocos2dx 프로젝트를 누르고 Ctrl+B를 해서 빌드 한다. 빌드가 끝나면 게임 프로젝트를 열어 build_native.py 를 더블클릭하여 실행 한번 한다. 그리고 게임 프로젝트 또한 Ctrl+B를 눌러 빌드를 해준다.


12. 실행
프로젝트에서마우스 오른쪽 클릭해서 Run as > Android Application 을 선택해 실행한다.

p.s.
혹시나 extra qualification 에러가 발생할 수도 있다. Visual Studio는 융통성이 있어 그냥 넘어가주는 문제이지만 이클립스는 그러하지 못해서 생기는 문제다. 에러가 난 코드를 잘 보라.. 표준에 맞지 않는 에러가 있을 것이다.












[Android] UI Event Handling

1. Event Listener

1) Anonymous Inner Class
가장 일반적이고 심플한 방식의 Event 처리 방식이다. 보통 다음의 로직을 쓴다. 1회용 클래스를 넘겨준다.

btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Pressed btn", 0).show();
}
});

(MainActivity.this 는 MainActivity 객체를 강제로 가리키게 하는 익명 내부 클래스 코딩 방식이다)

2) NewClass implement Interface
그렇지 않고 Interface인 View.OnClickListener 을 구현하는 방식이 있다. 아래 처럼 클래스를 하나 만든다.

class MyClass implements View.OnClickListener {
public void onClick(View v) {
Toast.makeText(v.getContext(), "Pressed btn1", 0).show();
}
}

그리고 리스너를 MyClass로 넘겨준다.

btn1.setOnClickListener(new MyClass());


3) Activity implement Interface
액티비티 자체에서 리스너를 구현하는 방식이다. 다음과 같이 액티비티에서 리스너를 구현한다.

public class MainActivity extends Activity implements View.OnClickListener {
...
public void onClick(View v) {
Toast.makeText(v.getContext(), "Pressed btn1", 0).show();
}
...
}

그리고 사용할 때는 요렇게만 적어주면 된다.

btn1.setOnClickListener(this);




2. Event Handler

2015년 8월 11일

[Android] Activity Life Cycle with Debugging

DDMS 를 쓰는 것이 좋다. DDMS Perspective를 선택해서 LogCat 창의 +를 눌러 아래 항목을 채운다.

Filter name : MyFilter
by log Tag : test_tag

MainActivity를 다음과 같이 채운다.


package com.example.activitycycle;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
Log.d("test_tag", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
protected void onDestroy() {
Log.d("test_tag", "onDestroy");
super.onDestroy();
}
protected void onPause() {
Log.d("test_tag", "onPause");
super.onPause();
}
protected void onRestart() {
Log.d("test_tag", "onRestart");
super.onRestart();
}
protected void onResume() {
Log.d("test_tag", "onResume");
super.onResume();
}
protected void onStart() {
Log.d("test_tag", "onStart");
super.onStart();
}
protected void onStop() {
Log.d("test_tag", "on");
super.onStop();
}
}

앱을 껐다 켜면서 실제로 언제 각 함수가 호출되는지 확인한다. 후후후후후후. 그림 같은거 보여주면 설명할거라 생각했다면 오산이다. 후후훟ㅎㅎㅎㅎ. 그리고 그게 가장 직관적이고 빠르게 이해가 된다.


p.s.

 혹시 로그가 안나온다면 이클립스를 껐다 켠다. 그래도 안나오면 작업관리자에서 adb.exe를 껐다가 켠다.

에뮬레이터나 실기기의 화면의 가로,세로모드를 바꿔보면 Destroy되었다가 다시 살아나는 것을 볼 수 있다.

onPause, onStop, onDestroy 함수가 호출되면 앱이 kill 될 수 있다.



[Android] Intent

Intent 는 Action, Category, Data, Component Name, Extras, Flags 로 이루어지며 컴포넌트들간의 통신에 이용된다. 또한 Intent는 명시적(Explicit) 인텐트와 암시적(Implicit) 인텐트로 나누어진다.

명시적 암시는 인자로 Component Name 을 적어준다. 예를 들면 아래와 같은 인텐트는 전화앱을 구동시키는 명시적 인텐트가 된다.

Intent intent = new Intent(Intent.ACTION_DIAL);

혹은 다음과 같이 클래스 이름을 통해 명시적으로 전달할 수도 있다.

intent.setClassName("com.example.packagename", "com.example.packagename.ActivityName");


반면 아래와 같은 인자로 Intent를 호출하게 되면 암시적인 인텐트가 된다.

Action, Category, Data : 암시적 인텐트


Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("image/*");
startActivity(intent);


상대 컴포넌트에게 더 많은 정보를 넘겨주고 싶다면 Extras 인자를 사용하면 되고 Flags 는 안드로이드 시스템에게 제어 정보를 전달한다. 아마 Flags는 쓸 일이 없을 것이다.  다음의 코드처럼 intent에 데이터를 저장시킨다.

intent.putExtra("msg", "initial data");

그 후에 받는 쪽에서는 extra를 이용해 정보를 얻는다.

Intent intent = getIntent();
String s = intent.getStringExtra("msg"); // s = initial data


Activity, Service, Content Provider는 반드시 Manifest에 등록해야 한다. 반면 BroadcastReceiver만은 Manifest에 등록하지 않아도 된다.