* 목적
android 기본적으로 제공되는 ant 스크립트로 자동 build 환경을 구성하는 방법을 알아보도록 하겠습니다.


* android project 만들기
- 기존 이클립스 프로젝트 만드는것과 동일합니다.


* ant project 만들기
- sdk/tools 디렉토리 path 설정
여기서는 sdk 에 포함되어있는 툴을 많이 사용하기때문에 path 로 잡아두는것이 편합니다.
androidsdk/tools 디렉토리를 path 에 포함시킵니다.


- android update project ./ 실행
해당 프로젝트 디렉토리로 가서 아래 명령어를 실행하면 자동으로 build.xml 파일이 생성됩니다.

android update project –p ./
image

- keystore 설정하기
패키징을 하기위해서는 keystore 파일을 설정해야 합니다.

local.properties 파일을 열어서 아래와같이 keystore 의 위치와 alias 를 잡아줍니다.
image

 

* 컴파일직전에 config 설정하기
- build.xml 수정하기
maven 에서처럼 컴파일전에 자동으로 config 를 수정하도록 하려면 아래와같은 코드를 build.xml 에 추가합니다.
그리고 ant 실행시 파라메터에 config 를 변경해주면 됩니다.

<!-- extension targets. Uncomment the ones where you want to do custom work
     in between standard targets -->
    <property name="config" value="beta" />   
    <target name="-pre-build">
        <echo>config setting!</echo>
        <delete file="src/config.properties" />
        <copy tofile="src/config.properties" file="config/config_${config}.properties" />
    </target>

- 코드상에서 config.properties 사용하기

package net.cranix.android.anthello;


import java.io.IOException;
import java.util.Properties;

/**
* @author cranix
*/
public class Constants {
    private static final Properties config = new Properties();
    static {
        try {
            config.load(Constants.class.getClassLoader().getResourceAsStream("config.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
   
    public static final String URL = config.getProperty("url");
}


* ant 를 이용하여 실행하기
- build.xml 수정하기
google 에서 기본적으로 제공하는 ant build 스크립트에는 실행할수있는 스크립트가 없습니다.
그래서 아래와같은 코드를 build.xml 에 추가하면 실행을 해볼수 있습니다.

    <target name="run">
        <xpath input="AndroidManifest.xml" expression="/manifest/@package" output="manifest.package" />
        <xpath input="AndroidManifest.xml" expression="/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN']/@android:name" output="manifest.mainactivity" />

        <exec executable="${adb}" failonerror="true">
            <arg line="${adb.device.arg}" />
            <arg value="shell" />
            <arg value="am" />
            <arg value="start" />
            <arg value="-a" />
            <arg value="android.intent.action.MAIN" />
            <arg value="-n" />
            <arg value="${manifest.package}/${manifest.mainactivity}" />
        </exec>
    </target>


* ant 를 이용하여 빌드하기
프로젝트 디렉토리에서 아래와같은 명령으로 빌드를 해볼수 있습니다.
단 이전에 ant 는 받아서 path 로 잡혀 있어야 합니다.

- ant debug -> debug 키로 빌드해서 bin 디렉토리에 apk 생성
- ant debug install -> debug 키로 빌드해서 bin 디렉토리에 apk 생성하고 디바이스에 설치
- ant release -> release 키로 빌드해서 bin 디렉토리에 apk 생성
- ant release install -> release 키로 빌드해서 bin 디렉토리에 apk 생성하고 디바이스에 설치
- ant debug install run –> apk 생성하고 디바이스에 설치해서 실행해보기
- ant debug install run –Dconfig=beta –> beta 환경으로 디바이스에서 실행해보기
- ant emma debug install test –> test 를 돌리고 emma coverage report 를 뽑아냅니다. (test 프로젝트에서 실행)


* 문제점
- maven 에서와같이 dependency 를 관리해주지 않아서 수동으로 해야 합니다.

신고
by cranix 2012.01.30 11:14

android 기본 개발환경인 이클립스 ADT 만을 이용하면 환경설정파일을 자동으로 관리해줄수가 없습니다.
그래서 이 문서에서는 maven 을 이용해 환경설정파일을 소스 수정없이 빌드옵션 수정만으로 관리할수있도록 구성하는 방법을 알아봅니다.

* maven 을 이용한 android 자동 빌드환경 구성

   - http://cranix.net/374

 

* resource 디렉토리 만들기
  - src/main/resources 디렉토리를 생성합니다.
image

- 프로젝트 마우스오른쪽 –> maven –> update project configuration 을 실행하면 위와같이 소스폴더에 포함되는것을 확인할수 있습니다.

 


* config 파일 읽기
  - resource 디렉토리에 config.properties 파일을 생성하고 아래와같이 입력합니다.

url = http://test/real

  - properties 파일을 읽기위한 코드는 아래와 같습니다.

package net.cranix.android.hello;


import java.io.IOException;
import java.util.Properties;

/**
* @author cranix
*/
public class Constants {
    private static Properties config = new Properties();
    static {
        try {
            config.load(Constants.class.getClassLoader().getResourceAsStream("config.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
   
    public static String URL = config.getProperty("url");
}

   - 이것의 사용은 간단합니다.

package net.cranix.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        TextView tv = (TextView) findViewById(R.id.textView1);
        tv.setText(Constants.URL);
    }
}

 


* 디렉토리 구조 만들기

   - config 디렉토리를 생성하고 config_beta.properties,config_real.properties 파일을 만들어서 집어넣습니다.

image

  - 위와같은 형태로 디렉토리 구조를 만듭니다.
  - config_beta.properties  파일과 config_real.properties 파일에는 beta 와 real 에서 사용될 적절한 환경변수를 집어넣습니다.

 

* maven pom 파일 수정하기
  - pom 파일을 아래와같이 수정합니다.


<properties>
   …
   <config>beta</config>
</properties>


<build>

    <plugins>
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.7</version>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <configuration>
                        <target>
                            <delete
                                file="${project.basedir}/src/main/resources/config.properties" />
                            <copy tofile="${project.basedir}/src/main/resources/config.properties"
                                file="${project.basedir}/config/config_${config}.properties" />
                        </target>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        …
   </plugins>

</build>

   - 위와같이 했을때 execution 에서 오류나는 경우가 있는데 이것은 단순 버그임으로 이클립스의 Problems view 에서 delete 합니다.

image

  - 프로젝트 마우스오른쪽 –> maven –> update project configuration 을 실행합니다.

 

* build 파라메터를 변경하여 실행
   - real 환경으로 실행하기
     --> run as –> maven build … 을 클릭하고 아래와같이 입력하고 실행합니다.
image

   - 이제부터는 위와같이 build 의 parameter 를 변경하는것 만으로 beta 와 real 설정파일을 변경할 수 있습니다.

신고
by cranix 2012.01.25 12:09

* 필요한 eclipse 플러그인 설치 (마켓에서 다운로드 가능합니다.)
- Maven Integration for Eclipse
- Android Configurator for M2E


* 이클립스 환경설정
- maven 3.0.3 이상 설치
--> http://maven.apache.org/download.html
현재의 eclipse 플러그인의 maven 은 3.0.3 이하 버젼이라서 최신버젼을 다운받아야 합니다.
다운받은후에 eclipse 의 maven 설정에서 설치한 3.0.3 을 선택해 줍니다.

- jdk 6이상 선택
이클립스 기본 컴파일러를 jdk 6 이상으로 선택해 줍니다.
jre 가 선택되어 있다면 컴파일시 오류납니다.


* eclipse 에서 android 프로젝트 생성
- 이 부분은 기존의 android 프로젝트 생성하는것과 동일합니다.


* maven 프로젝트로 변경
- 생성된 android 프로젝트에 마우스오른쪽 -> Configure -> Convert to maven project 를 클릭합니다.

위와같이 입력하면 pom.xml 이 생기면서 maven 프로젝트가 됩니다.

- 추가후 바로 오류가 나는데 아래와같이 pom.xml 파일을 변경하면 오류가 없어집니다.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.cranix.android</groupId>
    <artifactId>mvnandroidtest2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>apk</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>android</artifactId>
            <version>2.3.3</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <build> 
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                <artifactId>android-maven-plugin</artifactId>
                <version>3.0.0-alpha-13</version>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </build>
</project>

- 원래 있던 src 폴더를 src/main/java 로 변경하여 아래와같은 구조가 되도록 합니다.

image

- 프로젝트 마우스오른쪽 –> maven –> update project configuration 을 실행해서 pom 에 설정된값을 eclipse 에 적용합니다.
- 이작업을 하면 위의 src/main/java 폴더가 자동으로 소스폴더가 되는것을 확인할수 있습니다.

 

* 빌드 테스트 해보기
- Run As –> maven build … 을 클릭해서 아래와 같이 채웁니다.



- 실행한후에 target 디렉토리에 apk 파일이 생성되었다면 제대로 셋팅 완료된것입니다.

 

* apk 실행하기
apk 를 실행해보기 위해서는 에뮬레이터를 켜놓거나 디버그용 폰을 꼽아놔야 합니다.
그상태에서 maven 으로 아래 명령을 실행하면 됩니다.

 

 

* sign 파일로 서명하기
apk 를 만들때 서명하기 위해서는 아래와같은 플러그인을 추가하면 됩니다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jarsigner-plugin</artifactId>
    <version>1.2</version>
    <executions>
        <execution>
            <id>signing</id>
            <goals>
                <goal>sign</goal>
            </goals>
            <phase>package</phase>
            <inherited>true</inherited>
            <configuration>
                <archiveDirectory></archiveDirectory>
                <includes>
                    <include>target/*.apk</include>
                </includes>
                <keystore>${keystorepath}</keystore>
                <storepass>${storepass}</storepass>
                <keypass>${keypass}</keypass>
                <alias>${alias}</alias>

            </configuration>
        </execution>
    </executions>
</plugin>

굵은 부분은 자신이 가지고있는 사인파일과 비밀번호로 쓰시고 clean package 를 했을때 오류없이 잘 되었다면 제대로 설정된 것입니다.

 

* 문제점
- 테스트는 지원이 되지만 기존에 ant 기반에서 지원하던 emma coverage report 는 지원되지 않습니다.

--> emma coverage report 를 뽑아내는 과정이 실행되는곳은 test 프로젝트 인데 여기서 원래 프로젝트의 소스가 필요합니다. 그런데 maven 기반이면 각 프로젝트를 dependency 로서 관리하기때문에 따로 소스에 접근할 방법이 없습니다.

 

 

* 참고사이트

- http://code.google.com/p/maven-android-plugin/

신고
by cranix 2012.01.25 10:50

안드로이드에는 이미지크기를 줄일때 Bitmap.createScaledBitmap() 이라는 유용한 함수를 제공해 줍니다.
함수원형을 보면 아래와 같습니다.


public static Bitmap createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)

여기서 문제는 Bitmap 소스가 들어가야 한다는 것입니다.
결국 저 함수를 쓰기위해서는 미리 디코딩 작업을해서 이미지소스를 메모리에 올려야 한다는 것이죠.


Bitmap tempBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, resizeOpts);
Bitmap finalBitmap = Bitmap.createScaledBitmap(tempBitmap, dimen[0], dimen[1], false);
위와 같이 사용해야 합니다.
이러한 형태라면 소스 이미지가 커진다면 메모리도 그만큼 커야하는데 모바일 환경이라 그만큼 큰 가용메모리를 가진디바이스가 별로 없습니다.
결국 OutOfMemoryError 를 벹어내면서 크래시가 나게 되는것이죠.

그래서 있는것이 inSampleSize 라는 옵션입니다.
이것은 decodeStream 을 이용해 이미지를 디코딩할때 애초에 줄여서 디코딩해주는 역할을 하죠.
단점은 정확한 크기를 지정할수 없다는것입니다.
사용법은 아래와 같습니다.

BitmapFactory.Options resizeOpts = new Options();
resizeOpts.inSampleSize = 2;
Bitmap tempBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, resizeOpts);
Bitmap finalBitmap = Bitmap.createScaledBitmap(tempBitmap, dimen[0], dimen[1], false);
이것은 디코딩을 할때 원본크기의 1/2 로 디코딩하라는 말입니다.
즉 inSampleSize 의 역수만큼의 크기로 사이즈를 줄입니다.
예를들어 4 라면 1/4 로 이미지가 줄어서 디코딩 되게 되죠.

어차피 사이즈를 줄일껀데 소스이미지 전체를 메모리에 올릴필요가 없습니다.
결국 이미지를 줄이기 위해서는 inSampleSize 를 이용하여 줄이고자하는 크기의 최대한 근접한 값으로 줄인후에 createScaledBitmap 함수를 호출해서 정확하게 줄이는것이 OutOfMemoryError 를 피하는 방법입니다.

신고
by cranix 2011.10.20 17:58


Lazy장기 온라인!


이번엔 장기 입니다.
온라인상의 다른 유저들과 장기를 즐길수 있습니다.

안드로이드 마켓에서 "Lazy장기" 로 검색해 주세요.



<스크린샷>

[##_http://cranix.net/script/powerEditor/pages/1C%7Ccfile7.uf@125B174F4E5602D1251692.png%7Cwidth=%22390%22%20height=%22688%22%20alt=%22%22%20filename=%22cfile7.uf@125B174F4E5602D1251692.png%22%20filemime=%22%22%7C_##]



[##_http://cranix.net/script/powerEditor/pages/1C%7Ccfile2.uf@165B174F4E5602CA24D97C.png%7Cwidth=%22390%22%20height=%22688.2352941176471%22%20alt=%22%22%20filename=%22cfile2.uf@165B174F4E5602CA24D97C.png%22%20filemime=%22%22%7C_##]





 

<게임설명>
- 여러 안드로이드 유저들과 온라인으로 장기게임을 즐길수 있습니다.
- 게임대기하면 다른 대기중인 상대방과 게임이 시작되며 조작법도 간단합니다.
- 물론 아이디를 직접 입력해서 원하는 사용자와 대국할수도 있습니다.
- 온라인상의 다른 사용자들과의 랭킹을 비교할수 있습니다.
- 상대방 차례에 다른 작업을 해도 무방합니다. 내 차례가되면 알아서 노티로서 알려주게 됩니다.
- 이전에 한 게임들의 기보를 보면서 다시 확인할수 있습니다. 


<룰>
- 한턴에 2분을 사용할수 있으며 2분이 넘어갔을때 상대방이 "승리" 버튼을 누르면 바로 승리하도록 되어있습니다.
- 축구와 마찬가지로 승리 3점 비김 1점 패배 0점 으로 점수를 계산합니다.

신고
by cranix 2011.08.18 00:17

안드로이드 에서는 xml 만을 이용하여 이미지의 전환효과를 줄수 있습니다.
예를들어 웹상에서 롤오버 같은 기능을 xml 만을 이용해서 만들수 있게 되는것이죠.
해당 xml 은 아래와 같이 작성합니다.

- btn.xml
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_focused="true" android:drawable="@drawable/btn_focus"/>
     <item android:state_enabled="false" android:drawable="@drawable/btn_off"/>
     <item android:state_selected="true" android:drawable="@drawable/btn_select"/>
    
     <item android:drawable="@drawable/btn_normal" /> <!-- default -->
 </selector>

위와 같은 xml 을 작성해서 drawable 에 넣어놨다면 안드로이드 전역에서 R.drawable.btn 이라는 리소스를 사용 가능하게 됩니다.
예를들어 이 xml 을 background 로 넣은 view 는 상태가 focused, enabled 혹은 selected 될때 위 xml 에 정의된대로 바뀌게 됩니다.
신고
by cranix 2011.07.14 15:10
안드로이드에서 애니메이션 처리를 하기위해서는 두가지를 해야 합니다.
animation 을 정의하는 xml 파일을 작성하고, 해당 xml 을 view 나 activity 에 적용하는 작업 입니다.

<Animation xml 만들기>
- pull_down_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate android:fromYDelta="0" android:toYDelta="100%p" android:duration="300"/>
 
</set>

위 소스는 타겟이 pull_down 으로 없어지는 애니메이션을 정의한 것입니다.
이러한 움직임을 정의하는 translate 태그 말고도, 투명도를 제어하는 alpha 태그, 회전을 제어하는 rotate 태그, 크기를 제어하는 scale 태그가 있습니다.

<Animation 적용하기>
이와같은 xml 은 아래와같이 적용하고 또 리스너를 이용해서 애니메이션의 시작/끝 지점을 잡아낼 수 있습니다.
 Animation anim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.push_up_webview);   
   anim.setAnimationListener(new Animation.AnimationListener() {
    public void onAnimationStart(Animation animation) {
    }
    
    public void onAnimationRepeat(Animation animation) {
    }
    public void onAnimationEnd(Animation animation) {
     runOnUiThread(new Runnable() {
      public void run() {
       progressLoading.setVisibility(View.GONE);
      }
     });     
    }
   });   
   progressLoading.startAnimation(anim);

신고
by cranix 2011.07.14 15:04
안드로이드에서 가장 많이 사용하는 View 는 ListView 일 것으로 생각됩니다.(적어도 저는...)
이 리스트뷰는 기본적으로 리스트형의 많은양의 데이터를 표시하기위해 존재합니다.
그래서 프로그래밍 하는 스타일에 따라 체감속도에 영향을 많이 줍니다.

안드로이드에서는 이러한 데이터의 효율적인 처리를 위해서 이미 사용되어진 view 를 재활용 할 수 있도록 해 주고 있습니다.
이번 포스트에서는 그 재활용 하는 방법에 대해서 알아보도록 하겠습니다.


안드로이드는 ListView 에 데이터를 채울때 Adapter 를 이용합니다.
이 Adapter 를 생성할때 가장 중요한메소드는 getView() 입니다.
아래와 같은 기본형을 가집니다.
View getView(int position, View convertView, ViewGroup parent)

이 메소드의 파라메터중 convertView 가 재활용과 관련된 파라메터 입니다.
이 convertView 파라메터에서는 재사용할 view 가 없다면 null 을 넘겨주고 있다면 해당 view 를 넘겨줍니다.

이를 이용하여 Holder 패턴을 구현하는데 대부분 아래와 같이 사용합니다.
 public class ItemHolder {
  public TextView textView = null;
 }

 public View getView(int position, View convertView, ViewGroup parent) {
  ItemHolder holder = null;
  if (convertView == null || convertView.getId() != R.layout.list_item) {
// 재사용할 view 가 없거나 재사용할 view 가 다른 layout 으로 작성되어졌을경우 xml 새로 파싱합니다.
// convertView 에는 여기서 inflate 한 view 만 넘어오는것이 아닙니다.
// 예를들어 이 listview 에 headerView 가 있다면 headerView 역시 convertView 에 넘어오게 됩니다. 
// 이럴경우 원하지 않은 결과가(거의 크래쉬) 나오기때문에 반드시 layout 이 맞는지를 비교해 줘야합니다.


   convertView = inflater.inflate(R.layout.list_item, null);
   holder = new ItemHolder();
   holder.textView = (TextView) convertView.findViewById(R.id.textView1);
// 파싱된 결과를 holder 에 담아서 tag 로 저장해둡니다.
   convertView.setTag(holder);
   convertView.setId(R.layout.list_item);
  }
  else {
// 재사용할 view 가 있을경우 해당 view 의 tag 를 가져와서 ItemHolder 로 캐스팅하여 사용합니다.
   holder = (ItemHolder) convertView.getTag();
  }

// 여기부터는 얻어온 holder 를 이용하여 데이터를 채웁니다.
  holder.textView.setText(getItem(position).toString());
  
  return convertView;
 }

이런 형태를 취하는 이유는 안드로이드에서 시간이 가장 오래걸리는 작업중의 하나가 findViewById 메소드를 이용한 xml 파싱작업 이기 때문에 해당 메소드의 호출을 최소화 하기 위함입니다.




신고
by cranix 2011.06.27 18:17

 

Lazy 오목 Online!

 

- 게임물등급위원회 전체용가 -


 

- 인공지능 플레이 기능 -

- 터치실수를 없애기위해 두번 터치해야 돌 놓아지는 시스템 적용 -

- 온라인상의 다른 유저와 게임기능 -

- 채팅 기능 -

- 렌주룰에의한 금수 완벽 적용(단 AI 플레이시는 제외) -

- 백이 금수(3*3,4*4,6목,장목)를 이용하여 승리할 경우 가산점 부여 -

- 유저랭킹 및 금수랭킹 기능 -


- 전화번호로 직접요청 가능-

- 다른 작업중에도 내 차례가 되면 노티 해주는 기능 -


- 룰정보 확인 -



Lazy 오목용 유저 게시판을 만들었습니다. 
아래 주소로 접속해 주세요.

http://apps.silpir.net
 



업데이트 로그
- v2.2 ad
    - 네트워크 연결 안되어있어도 AI 와 플레이 가능
    - 전체적인 반응속도 대폭 개선
    - 렌주룰완전 적용
    - 서버 안정화

- v2.15 ad
   - 채팅 기능이 추가되었습니다.

- v2.1 ad
   - 흑이 3/3/3,4/4/4 되던거 안되도록 수정
   - 게임에 들어가자마자 메뉴 버튼을 누르면 튕기던 버그 수정
   - 금수로 승리하면 +1 점 추가됩니다.
   - 영어옵션 추가 (번역이 완벽하지 않습니다.)
   - 종료예약 추가
    - 사운드 추가
    - 최근게임리스트 추가
    - 노티가 너무 많이나오던거 수정




- v2.0 ad
   - 흑(선) 플레이시 3*3, 4*4, 6목 모두 안되게 수정
   - 룰정보 페이지 추가 (게임룰,금수룰,점수룰 설명)
   - 커뮤니케이션용 홈페이지 생성 : http://apps.silpir.net
   - 여러 디바이스 대응을 위해서 레이아웃의 크기단위를 dp 로 수정
   - 게임랭킹 기존 11개에서 15개 보이도록 수정


- v1.9 ad
   - 점수 시스템 개선 --> 30일동안 업데이트가 없는 점수데이터는 다른 유저의 점수에 영향을 미치지 않고 삭제됨
   - 랭킹화면에서 원하는 유저를 클릭하면 "게임신청" 이 나와서 직접 신청할수 있는 기능
   - 흑(선) 플레이시 3*3 룰을 적용받는 기능 ( 단 컴퓨터 "autobot" 은 3/3 적용받지 않음 )
   - 현재 기다린 시간 표시해주는 기능
   - 웹 클라이언트와 대국시 점수반영안되게 함.


- v1.82 ad
<광고 삽입>
   - 사용자가 많아지다보니 서버유지가 힘들어져 서버유지 비용을 벌기위해 광고 삽입


- v1.8 beta
<노티 시스템 업데이트>
    - 내가 해야할 차례라면 어플을 꺼놔도 노티가 오게 됩니다. (예를들어 게임대기를 눌러놓고 어플을 꺼놓으면 게임이 시작될때 문자온것과 똑같이 노티로 알려주게 됩니다.)

    - 어플을 처음 실행하면 가장최근에 플레이한 게임으로 자동으로 이동시켜주는 기능이 있었는데 이 기능을 빼고 내가 해야할 게임들의 노티를 띄우도록 하였습니다. (여러게임을 동시에 진행하고 있다면 이 기능이 오히려 불편하기 때문입니다.)


<기타 버그수정>
   - 노티로 오는 전화번호가 가려지지않고 모두 나오던것 수정

   - 노티가 제대로 동작하지 않던것 수정



====== 룰 정보 =======

렌주룰
- LazyOmok 은 렌주룰을 따릅니다.
- 렌주룰은 먼저하는 흑의 유리함을 어느정도 없애기위해 개발된 룰입니다.
- 간단한 룰에대해서는 아래에 정리하였습니다.
- 그 외에 자세한 정보를 원하시면 아래 홈페이지를 이용해 주시기 바랍니다.
- http://www.renjukorea.com

게임룰
승리
- 자신의 돌을 가로,세로 혹은 대각선중의 하나로 5개 연속으로 놓으면 승리하게 됩니다.
- 백은 6개 이상 연속으로 놔도 승리 처리됩니다.
- 게임중 상대 턴인 상태에서 5분이상 기다렸을때 종료하면 승리하게 됩니다.
패배
- 상대가 먼저 승리조건을 만족시키면 패배합니다.
- 게임중 자신의 턴인 상태에서 종료하면 패배합니다.

금수룰
- LazyOmok 에서는 먼저하는 흑의 유리함을 어느정도 없애기 위해서 유저가 흑 플레이시 금수를 적용하고 있습니다.
- LazyOmok 에서 금수는 3*3, 4*4, 6목, 장목 입니다.
- 장목은 돌을 6개 이상 이어나가는것을 말합니다.
- 백과, AI(autobot)는 금수룰을 적용받지 않습니다.
- 만약 이길수 있는 자리에 금수가 겹쳐있다면 금수우선룰에 의해 놓을수 없게 됩니다.

점수룰
- 점수는 승리시 +2 점, 패배시 -2점 입니다.
- 만약 백이 금수로 승리할때에는 승점에 +1 점 추가됩니다.
- 점수는 유저랭킹과 금수승랭킹으로 나뉘어서 저장됩니다.
- 30일동안 점수의 업데이트가 없으면 자동으로 점수데이터가 초기화됩니다.


 
언젠가 아이폰용 한게임 고스돕을 해본적이 있습니다.

상당한 퀄리티에 잘 만들어져 있었으나 한가지 불편한점이 있었습니다.

 

바로 게임도중 끊기면 그 판이 날라간다는 것이지요.

이런 상황이 발생하면 폰 탓만 했는데 그게 아니라는 생각이 들었습니다.

 

아무리 현재 3G 가 좋아졌고 곳곳에 와이파이 존이 있다고 하지만

여러가지 다른 환경적인 요인에 의해서 순간적으로 네트웍이 끊기는 상황은 자주 나타나고 있습니다.

 

이러한 상황에서 끊김없는PC 에서 하던것과 똑같이 온라인게임을 만들면,

위와 같이 해당 게임이 날라가는 상황을 피할수 없게 됩니다.

 

그래서 좀 더 모바일 환경에 맞는 Online게임을 만들수 없을까?

라는 생각에서 나온것이 바로 “Lazy 오목 Online!” 입니다.

 

이 게임은 모바일에서 메시지 주고받듯이 게임을 진행할 수 있습니다.

예를들면 게임을 시작하고 자신의 차례가 되면 아래와 같은 노티가 오게됩니다.

해당 노티를 터치하면 바로 해당 게임으로 넘어가게 되고 플레이를 계속할 수 있게 됩니다.

(단 이러한 노티는 게임을 Home 키를 눌러서 백그라운드 상태로 돌려놨을때만 동작합니다. 즉 현재 게임화면을 보고있으면 노티가 오지 않습니다.)

 

또한 이 게임은 시간제한이 없습니다.

PC 에서처럼 모바일 환경은 언제나 게임에 집중할 수 없기 때문에 시간제한을 없앴습니다.

대신 누구나 종료할수 있도록 만들었습니다.

 

이렇게 시간제한 없이 게으른 플레이를 할 수 있다고 하여서 이름도 “Lazy” 입니다.^^

 

 

신고
by cranix 2011.02.14 13:36

안드로이드에서 기본적으로 지원하지 않는 UI 를 만들때 CustomView 를 사용합니다.

이러한 CustomView 의 기본적인 작성방법을 알아보도록 하겠습니다.

 

CustomView 는 “android.view.View” 클래스를 상속해서 만들어 집니다.

기본적으로 onDraw() 메소드만 재정의해서 xml 에 view 태그만 추가하면 오류없이 출력되는것을 볼 수 있습니다.

 

이번 포스트에서는 간단히 클릭하면 반응하는 CustomView 를 만들어 보도록 하겠습니다.

먼저 CustomView 소스를 확인해 보도록 하겠습니다.

 

- CustomView.java

package net.cranix.android.customviewtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

public class CustomView extends View {

private String text = null;
private int backgroundColor = Color.RED;

private String tempText;


// 속성이 없는 생성자는 소스상에서 직접 생성할때만 쓰인다.
public CustomView(Context context) {
super(context);
Log.w(Constants.TAG,"CustomView("+context+")");
}
/*
* 리소스 xml 파일에서 정의하면 이 생성자가 사용된다.
*
* 대부분 this 를 이용해 3번째 생성자로 넘기고 모든 처리를 3번째 생성자에서 한다.
*/
public CustomView(Context context,AttributeSet attrs) {
this(context,attrs,0);
Log.w(Constants.TAG,"CustomView("+context+","+attrs+")");
}

/*
* xml 에서 넘어온 속성을 멤버변수로 셋팅하는 역할을 한다.
*/
public CustomView(Context context,AttributeSet attrs,int defStyle) {
super(context,attrs,defStyle);

this.text = attrs.getAttributeValue(null,"text");

Log.w(Constants.TAG,"CustomView("+context+","+attrs+","+defStyle+"),text:"+text);
}

/*
* xml 로 부터 모든 뷰를 inflate 를 끝내고 실행된다.
*
* 대부분 이 함수에서는 각종 변수 초기화가 이루어 진다.
*
* super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
*/
@Override
protected void onFinishInflate() {
setClickable(true);
Log.w(Constants.TAG,"onFinishInflate()");
}

/*
* 넘어오는 파라메터는 부모뷰로부터 결정된 치수제한을 의미한다.
* 또한 파라메터에는 bit 연산자를 사용해서 모드와 크기를 같이 담고있다.
* 모드는 MeasureSpec.getMode(spec) 형태로 얻어오며 다음과 같은 3종류가 있다.
* MeasureSpec.AT_MOST : wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
* MeasureSpec.EXACTLY : fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
* MeasureSpec.UNSPECIFIED : MODE 가 셋팅되지 않은 크기가 넘어올때 (대부분 이 경우는 없다)
*
* fill_parent, match_parent 를 사용하면 윗단에서 이미 크기가 계산되어 EXACTLY 로 넘어온다.
* 이러한 크기는 MeasureSpec.getSize(spec) 으로 얻어낼 수 있다.
*
* 이 메소드에서는 setMeasuredDimension(measuredWidth,measuredHeight) 를 호출해 주어야 하는데
* super.onMeasure() 에서는 기본으로 이를 기본으로 계산하는 함수를 포함하고 있다.
*
* 만약 xml 에서 크기를 wrap_content 로 설정했다면 이 함수에서 크기를 계산해서 셋팅해 줘야한다.
* 그렇지 않으면 무조껀 fill_parent 로 나오게 된다.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// height 진짜 크기 구하기
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = 0;
switch(heightMode) {
case MeasureSpec.UNSPECIFIED: // mode 가 셋팅되지 않은 크기가 넘어올때
heightSize = heightMeasureSpec;
break;
case MeasureSpec.AT_MOST: // wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
heightSize = 20;
break;
case MeasureSpec.EXACTLY: // fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
heightSize = MeasureSpec.getSize(heightMeasureSpec);
break;
}

// width 진짜 크기 구하기
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = 0;
switch(widthMode) {
case MeasureSpec.UNSPECIFIED: // mode 가 셋팅되지 않은 크기가 넘어올때
widthSize = widthMeasureSpec;
break;
case MeasureSpec.AT_MOST: // wrap_content (뷰 내부의 크기에 따라 크기가 달라짐)
widthSize = 100;
break;
case MeasureSpec.EXACTLY: // fill_parent, match_parent (외부에서 이미 크기가 지정되었음)
widthSize = MeasureSpec.getSize(widthMeasureSpec);
break;
}


Log.w(Constants.TAG,"onMeasure("+widthMeasureSpec+","+heightMeasureSpec+")");

setMeasuredDimension(widthSize, heightSize);
}


/*
* onMeasure() 메소드에서 결정된 width 와 height 을 가지고 어플리케이션 전체 화면에서 현재 뷰가 그려지는 bound 를 돌려준다.
*
* 이 메소드에서는 일반적으로 이 뷰에 딸린 children 들을 위치시키고 크기를 조정하는 작업을 한다.
* 유의할점은 넘어오는 파라메터가 어플리케이션 전체를 기준으로 위치를 돌려준다.
*
* super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
Log.w(Constants.TAG,"onLayout("+changed+","+left+","+top+","+right+","+bottom+")");
}


/*
* 이 뷰의 크기가 변경되었을때 호출된다.
*
* super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

Log.w(Constants.TAG,"onSizeChanged("+w+","+h+","+oldw+","+oldh+")");
}


/*
* 실제로 화면에 그리는 영역으로 View 를 상속하고 이 메소드만 구현해도 제대로 보여지게 된다.
*
* 그릴 위치는 0,0 으로 시작해서 getMeasuredWidth(), getMeasuredHeight() 까지 그리면 된다.
*
* super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다.
*/
@Override
protected void onDraw(Canvas canvas) {
final Paint p = new Paint();
p.setColor(backgroundColor);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(), p);
if (text != null) {
p.setColor(Color.BLACK);
canvas.drawText(text, 10, 15, p); // 왼쪽 아래를 0,0 으로 보고있음
}
Log.w(Constants.TAG,"onDraw("+canvas+")");
}


/*
* 현재 view 가 focus 상태일때 key 를 누르면 이 메소드가 호출됨.
* 즉 이 메소드를 사용하려면 setFocusable(true) 여야함.
*
* 그리고 super 메소드에서는 기본적인 키 작업(예를들면 BACK 키 누르면 종료)을 처리하기 때문에 일반적으로 return 시에 호출하는게 좋다.
* 만약 기본적인 작업을 하지않게 하려면 super 함수를 호출하지 않아도 된다.
*
* 다른 event 메소드들도 유사하게 동작한다.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.w(Constants.TAG,"onKeyDown("+keyCode+","+event+")");
return super.onKeyDown(keyCode, event);
}

/*
* 이 view 에 touch 가 일어날때 실행됨.
*
* 기본적으로 touch up 이벤트가 일어날때만 잡아내며
* setClickable(true) 로 셋팅하면 up,move,down 모두 잡아냄
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.w(Constants.TAG,"onTouchEvent("+event+")");
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
backgroundColor = Color.RED;
text = tempText;
break;
case MotionEvent.ACTION_DOWN:
backgroundColor = Color.YELLOW;
tempText = text;
text = "Clicked!";
break;
case MotionEvent.ACTION_MOVE:
backgroundColor = Color.BLUE;
text = "Moved!";
break;
}
invalidate();
return super.onTouchEvent(event);
}


public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}

- 크기 계산하기

여기서 중요한 메소드는 onMeasure() 메소드 입니다.

이 메소드는 뷰의 전체 크기를 정하는 메소드 인데 안드로이드의 크기 정하는 방법에 따라 구현법이 달라져야 합니다.

 

안드로이드 레이아웃 xml 파일에서 크기를 지정하는 방법은 4가지가 있습니다.

   - fill_parent (상위 View 의 크기에 따름)

   - match_parent (상위 View 의 크기에 따름)

   - fixed (100px 와 같이 픽셀로 박아놨을때)

   - wrap_content (현재 뷰의 내용에 따름)

 

이렇게 4가지 방법의 특성에 따라서 넘어오는 크기의 종류는 3가지로 구분됩니다.

   - MeasureSpec.EXACTLY : fill_parent, match_parent, fixed 와 같이 상위에서 이미 결정되어버린 크기가 넘어올때 선택됩니다.

   - MeasureSpec.AT_MOST : wrap_content 를 선택했을때 선택됩니다.

   - MeasureSpec.UNSPECIFIED : xml 에 의하지 않고 소스상에서 직접 넣었을 때 나옵니다.

 

여기서 EXACTLY 과 UNSPECIFIED 는 외부에서 크기가 구해져서 내려오는 것이기 때문에 따로 계산할 것이 없으나 AT_MOST 는 내부적으로 크기계산을 해 주어야 합니다.

위의 소스에서는 간단하게 100,20 으로 박아놨지만 실제로 CustomView 를 구현하게 된다면 뷰의 특성에 따라 구현이 달라져야 할 것입니다.

 

 

- xml 에서 파라메터 받아내기

안드로이드 리소스 xml 에서 파라메터를 받아내려면 위 소스의 3번째 생성자에 있는것 처럼 아래와 같은 구문을 써야 합니다.

this.text = attrs.getAttributeValue(null,"text");

 

 

- xml 파일 구성하기

이렇게 만든 CustomView 를 xml 파일에서 사용하려면 아래와같은 xml 구성이 필요합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<view class="net.cranix.android.customviewtest.CustomView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
text="test"
/>
</LinearLayout>

 

- 실행해 보기

이렇게 구성된 뷰를 Activity 에 넣고 실행해 보면 아래와 같은 화면이 나옵니다.

마우스를 클릭,이동 할때마다 색깔이 변경되는것을 볼 수 있습니다.

image

image

image

신고
by cranix 2011.01.07 17:03
| 1 2 3 4 5 ··· 10 |