2016년 6월 30일 목요일

Android Studio - res 를 활용한 국제화 및 portrait / landscape 적용


[res 를 활용한 국제화]

android project view 익스플로러 화면에서
res 우클릭 -> new -> android resource file 선택 후 다이얼로그에서 아래같이 하면
values-언어-지역 형식의 폴더가 생성이 되고
타겟 폰에서 언어 선택 시 자동으로 반영 되어진다.


[portrait / landscape 적용]

android project view 익스플로러 화면에서
res 우클릭 -> new -> android resource file 선택 후 다이얼로그에서 아래같이 하면
layout-land(port) 형식의 폴더가 생성이 되고
타겟 폰 기울기에 따라서 화면을 바꿀 수가 있게된다.




생성 완료 후 실제 폴더 구조






Android Studio - App 간에 데이터를 넘기면서 실행해보자


0. 내 App 내 다른 Activity 로 데이터를 넘겨서 화면을 전환 시키자
1. 다른 App 에 데이터를 넘겨서 실행시켜보자
2. 내 App도 다른 App 에서 넘겨주는 데이터를 받아서 실행하도록 설정하자
fin. 전환/실행 완료 후 Caller 에게 intent data를 넘기자


[내 App 내 다른 Activity 로 데이터를 넘겨서 화면을 전환 시키자]

1. ItemListActivity 에서 ItemViewActivity로 보내자.

ItemListActivity .java
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Log.d(ItemListActivity.class.getSimpleName(), position + ", " + id +" 클릭.");
    TodoItem item = data.get(position);
    Intent intent = new Intent(ItemListActivity.this, ItemViewActivity.class);
    intent.putExtra("item", item);
    startActivity(intent);
}

intent.putExtra 함수는 원시타입 외에는 Serializeable 인터페이스 형 객체만 전송이 가능하다.
그렇기 때문에 TodoItem 클래스를 아래와 같이 implements 한다.

TodoItem.java
public class TodoItem implements Serializable{

2. ItemViewActivity 에서 넘겨받은 Intent를 이용해서 수정한다.

ItemViewActivity.java

intent = getIntent();
TodoItem item = (TodoItem)intent.getSerializableExtra("item");

setTitle(item.getTitle());
content.setText(item.getContent());



[다른 App 에 데이터를 넘겨서 실행시켜보자]

App 내의 다른 Activity를 불러올 수 있는 Intent 중에서 명시적 아니고 암시적 Intent 를 이용해서 넘기게 된다.

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, content.getText().toString());
startActivity(Intent.createChooser(intent, "공유할 앱을 선택하세요."));

 - intent.setType : 보낼 데이터 타입을 지정
 - intent.putExtra : 보낼 데이터를 넘김





[내 App도 다른 App 에서 넘겨주는 데이터를 받아서 실행하도록 설정하자]

AndroidManifest.xml 샅샅이 디비보자!

<activity android:name=".ItemViewActivity"    android:theme="@style/AppTheme"/>
<activity android:name=".ItemEditActivity"    android:theme="@style/Theme.AppCompat.Light">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"></data>
    </intent-filter>
</activity>
<activity android:name=".ItemListActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

1. 지금 총 3개의 Activity 가 등록 되어 있다.
 - ItemViewActivity
 - ItemEditActivity
 - ItemListActivity

2. 이 3개의 Activity 중에서 .apk 설치를 통해 바탕화면에 런쳐로 등록될 Activity 는 ItemListActivity 이다.

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

이 intent-filter 라는 태그 중에 category 태그가 바로 그 역할을 해 준다.

3. intent-filter 내 아래 처럼 세팅 하면 다른 App 에서 보내는 데이터를 공유받아 실행될 수 있다.

    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"></data>
    </intent-filter>

 - action : 어떤 타입의 동작인가 (Intent 보낼 때 Intent.ACTION_** 으로 설정 가능)
 - category : 어떤 카테고리의 동작인가 (Intent 보낼 때 Intent.CATEGORY_** 으로 활용가능하며 리스팅된 모든 category에 일치해야만 동작하게 됨, DEFAULT 는 반드시 있어야함)
 - data : 어떤 타입의 데이터를 받을 것인가


[전환/실행 완료 후 Caller 에게 intent data를 넘기자]

Intent



2016년 6월 29일 수요일

Android Studio - 안드로이드위젯을 활용하여 나만의 위젯만들기


1. 위젯을 상속 받은 나만의 위젯구현
2. XML 파일에서 위젯태그의 attribute 속성을 추가
3. res/layout 밑에 새로 만든 위젯을 입력
4. 추가된 attribute 속성을 TitleTextView.java 에서 접근



[위젯을 상속 받은 나만의 위젯구현]

public class TitleTextView extends TextView {

    private boolean complete;
    private int completeColor;
    private float completeWidth;

    public TitleTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    public TitleTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public TitleTextView(Context context) {
        super(context);
        init(context, null);
    }

    private void init(Context context, AttributeSet attrs){
        complete = true;
        completeColor = Color.RED;
        completeWidth = 6;
    }

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

        if(complete){
            Paint paint = new Paint();
            float x=0;
            float y=getHeight()/2;
            float dx = getWidth();
            float dy = y;
            paint.setColor(completeColor);
            paint.setAlpha(0x99);
            paint.setStrokeWidth(completeWidth);
            canvas.drawLine(x,y,dx,dy,paint);
        }

    }
}

[XML 파일에서 위젯태그의 attribute 속성을 추가]

res/value 밑에 attrs.xml 생성



[res/layout 밑에 새로 만든 위젯을 입력]

스키마 namespace를 추가 하고(Android studio는 자동 추가 되긴 함)

xmlns:app="http://schemas.android.com/apk/res-auto"

<myapp.TitleTextView    
android:id="@+id/titleTxt"    
android:layout_width="match_parent"    
android:layout_height="wrap_content"    
android:layout_toRightOf="@id/icon"    
style="@style/TodoStyle"    
android:lines="1"    
app:complete="true"    
app:completeColor="@android:color/holo_green_dark"    
app:completeWidth="12px"    
/>


[추가된 attribute 속성을 TitleTextView.java 에서 접근]

추가된 attribute 속성은, AttributeSet 클래스에 들어가게 된다.

public TitleTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}

이 AttributeSet 에서 내가 추가 한 attribute 속성을 가져 오기 위해선

private void init(Context context, AttributeSet attrs){
    TypedArray att = context.obtainStyledAttributes(attrs, R.styleable.TitleTextView);
    complete = att.getBoolean(R.styleable.TitleTextView_complete, false);
    completeColor = att.getColor(R.styleable.TitleTextView_completeColor, Color.RED);
    completeWidth = att.getDimension(R.styleable.TitleTextView_completeWidth, 6);
}


Android Studio - AsyncTask를 이용한 REST API GET/POST 콜


AsyncTask 관련해서 참조할 만한 사이트가 별로 없어서 만들어 봤다.


GetBear.java

import android.os.AsyncTask;
import android.util.Log;

import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;

/** * AsyncTask를 이용하여 REST GET콜을 통한 JSON을 얻어오는 클래스. */public class GetBear {

    public GetBear() {

    }

    public void getBear(String id){
        // inner class로 구현한 GetTask 객체를 통해 REST API        new GetTask().execute("http://70.12.108.133:8080/api/bears/"+id);
    }

    // AsyncTask inner class로 구현    private class GetTask extends AsyncTask<String, Void, String> {
        @Override        protected String doInBackground(String... params) {
            try{
                return GET(params[0]);
            }catch (IOException e){
                return "Unable to retreive data. URL may be invalid.";
            }
        }

        @Override        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }
    }

    private String GET(String myurl) throws IOException{
        InputStream inputStream = null;
        String returnString = "";

        int length = 500;


        try{
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            conn.connect();
            int response = conn.getResponseCode();
            Log.d("REST GET", "The response is : " + response);
            inputStream = conn.getInputStream();


            // convert inputStream into json            returnString = convertInputStreamToString(inputStream, length);
            JSONObject json = new JSONObject(returnString);

            Log.d("REST GET", "The response is : " + json.toString());
        }catch (Exception e){
            Log.e("REST GET", "Error : "+e.getMessage());
        }finally {
            if(inputStream != null)
                inputStream.close();
        }
        return returnString;
    }

    public String convertInputStreamToString(InputStream stream, int length) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");
        char[] buffer = new char[length];
        reader.read(buffer);
        return new String(buffer);
    }
}


PostBear.java

import android.os.AsyncTask;
import android.util.Log;

import com.multicampus.anddbsample.vo.Bear;

import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
/** * AsyncTask를 이용하여 REST POST콜을 통해 JSON을 입력하는 클래스. */public class PostBear {

    private Bear bear;

    public void postBear(Bear bear){
        this.bear = bear;
        new PostTask().execute("http://70.12.108.133:8080/api/bears");
    }

    // AsyncTask inner class로 구현    private class PostTask extends AsyncTask<String, Void, String>{

        @Override        protected String doInBackground(String... params) {
            try{
                return POST(params[0]);
            }catch (IOException e){
                return "Unable to retreive data. URL may be invalid.";
            }
        }
    }

    private String POST(String myurl) throws IOException{
        InputStream inputStream = null;
        String returnString = "";

        JSONObject json = new JSONObject();

        int length = 500;

        try{
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            conn.connect();

            json.accumulate(Bear.NAME, bear.getName());

            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
            writer.write(json.toString());
            writer.flush();
            writer.close();

            int response = conn.getResponseCode();
            Log.d("REST POST", "The response is : " + response);
        }catch(Exception e){
            Log.e("REST POST", "Error : " + e.getMessage());
        }finally{
            if(inputStream != null)
                inputStream.close();
        }
        return returnString;
    }
}


MainActivity.java

private void setDefaultEvent(){
    btnSubmit.setOnClickListener(new View.OnClickListener(){

        @Override        public void onClick(View v) {

            getBear.getBear("57721c41aa42685017000001");
        }
    });

    btnList.setOnClickListener(new View.OnClickListener(){

        @Override        public void onClick(View v) {
            postBear.postBear(new Bear("AI-BaoBao"));
        }
    });
}



아래 기트허브 참조
https://github.com/GlennHKim/MessengerUsingAsyncTask

Android Studio - Custom View 만들기

안드로이드에서 게임을 만들지 않는 한 Custom View를 만드는 일은 그리 흔치 않다고 한다.
그래도 한번 공부 삼아 만들어 보자

직접 클래스 생성을 통한 CustomView 생성


1. View 클래스를 상속 받는 CustomView 생성
2. CustomView 를 Activity에 세팅
3. 이벤트 등록


[View 클래스를 상속 받는 CustomView 생성]

View 클래스를 상속 받는 CustomView를 만들자.
View 클래스를 구현 하려면 3개의 생성자 및 onDraw 메소드를 override 해서 구현해야 한다.

public class CircleView extends View {

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

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        canvas.drawCircle(50, 50, 30, paint);
    }
}



[CustomView 를 Activity에 세팅]

기본적으로 CustomView를 세팅하는 방법을 setContentView() 메소드를 호출하는 것은 똑같다.
기존에 res/layout 의 xml 파일의 R.layout 값을 파라미터로 넘기는것과 달리 CustomView 객체를 생성하여 넘겨준다.

public class CustomViewActivity extends AppCompatActivity {

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.ch07_activity_custom_view);
        CircleView circle = new CircleView(this);
        setContentView(circle);
    }
}


[이벤트 등록]

마우스 클릭 이벤트를 등록 한다면, 아래 코드를 추가하면 된다.
View.onClickListener 클래스를 구현한 객체를 setOnClickListener 메소드를 호출하여 넘겨주면 되는데 다양한 방법 중 아래 방법(익명클래스생성)으로 넘기는게 보편적인 방법이다.

circle.setOnClickListener(new View.OnClickListener(){

    @Override    public void onClick(View v) {
        Log.d(this.getClass().getSimpleName(), "2-5. 리스너 인터페이스 그냥 바로 추가");
    }
});

위 코드는 람다표기법이라고 해서 아래 코드랑 동일하게 동작한다.
JDK 1.8 부터 가능하다고 한다

circle.setOnClickListener( (v) -> {
        Log.d(this.getClass().getSimpleName(), "2-5. 리스너 인터페이스 그냥 바로 추가");
});



직접 클래스 생성을 통한 CustomView 생성


1. View 클래스를 상속 받는 CustomView 생성
2. res/layout 에다가 해당 CustomView 를 사용하도록 xml 파일에 추가
3. CustomView 를 Activity에 세팅

[res/layout 에다가 해당 CustomView 를 사용하도록 xml 파일에 추가]

<mypackage.CircleView    android:id="@+id/circle"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    />

[CustomView 를 Activity에 세팅]

setContentView(R.layout.ch07_activity_custom_view);




Android Studio - Git 으로 형상관리를 시작하자.


1. Git Download
2. Android studio Git 설정
3. Git 저장소 생성(Git hub)
4. Github로 푸쉬


[Git Download]

구글에서 git download 검색하면 바로 뜨지만, 내가 귀찮으니 url 링크를 참조한다.
https://git-scm.com/downloads

OS에 맞는 깃을 다운받고 설치를 시작하면 자동으로 권장설정이 맞춰져 있으니 'next'만 연타


[Android studio Git 설정]

Ctrl + Alt + 's' 입력으로 환경설정을 띄우고 검색란에 'git'을 입력한다.


Git의 path가 정상인것을 확인하고 'Test' 버튼을 클릭해서 아래와 같은 결과가 나오면 성공~!



상단 메뉴바 'VCS -> Enable Version Control Integration' 이후 Git을 선택하면 Git으로 형상관리를 시작할 수 있고 아래처럼 메뉴가 바뀐다.



[Git 저장소 생성]

Github (https://github.com/)에 안드로이드 프로젝트를 위한 신규 Repository 를 생성한다.


[Github로 푸쉬]

푸쉬를 하기 전에 프로젝트를 Git으로 관리하도록 추가를 해야 한다.
아직 추가가 안되어 있기 때문에 프로젝트 브라우저에 파일들이 빨갛게 되어있다.


프로젝트 우클릭 -> Git 메뉴에 접근해서 'Add'를 하면,


초록색으로 파일들이 바뀌면서 변경된 부분이 있으니 언능 커밋해달라고 한다.


여기까지 Git init 작업이 끝났다고 보면 된다.
이제 할 일은 cmd를 띄워 해당 프로젝트 폴더로 이동하고 github 에서 가이드해주는 커맨드들을 차례로 입력하면 된다.

git commit -m "first commit"
git remote add origin https://github.com/GlennHKim/MessengerUsingAsyncTask.git
git push -u origin master




2016년 6월 28일 화요일

Android Studio - 윈도우 개발환경에서 Android Emulator 로 apk 파일 보내기

설치하고자 하는 Emulator가 띄워져 있는 상태에서,

Android studio sdk 가 설치되어 있는 폴더내부 'platfor-tools' 의 adb.exe를 이용하자

# adb install 'c:\*.apk'


를 입력하면 성공!


Android Studio - 좀 더 스마트하고 이쁜 방법으로 메뉴 만들기

안드로이드의 메뉴 3대장

1. 외부 메뉴 버튼
2. 액션 메뉴 버튼
3. 컨텍스트 메뉴 버튼

이 중에서 1,2 번 메뉴는 서로 왔다리갔다리 할 수 있다.



외부 메뉴버튼을 누르면 앱 하단부에 stack 형식으로 메뉴가 나타나게 되는데,
이 메뉴를 앱 상단부 타이틀바 부근에다가 갖다가 붙이는 방법이 있다.

그 방법을 알아보자


[하드코딩으로 메뉴 만들기]

MainActivity.java 파일에다가 아래 함수를 overriding

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    menu.add(0,0,0,R.string.share);
    menu.add(0,1,0,R.string.delete);

    return true;
}

하드코딩이라는 어감이 너무 안좋으니 smarter 한 방법을 생각 해 본다면,
메뉴도 나름 GUI 이기 때문에 res/layout 형식으로 xml 리소스로 빼보자.


[리소스를 활용한 메뉴 만들기]

res/menu 리소스디렉토리를 생성한 후 item_view.xml 파일을 생성하자.

<item    android:id="@+id/shareMenu"    android:title="@string/share"    android:icon="@android:drawable/ic_menu_share"/>
<item    android:id="@+id/deleteMenu"    android:title="@string/delete"    android:icon="@android:drawable/ic_menu_delete"/>

추가할 메뉴 항목을 item tag로 만들자.

MainActivity.java 파일에다가 아래 함수를 overriding

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.item_view, menu);

    return true;
}

MenuInflag 객체를 이용해서 res/menu에 생성한 xml 파일을 import!


[액션 메뉴로 추가시키기]

메뉴 3대장의 1,2 항목을 여기뗐다 저기붙였다가 하는 부분이 바로 app:showAsAction 프로퍼티다.
아래처럼 res/menu/list_view.xml 파일의 showAsAction 프로퍼티에 ifRoom 이라는 값을 넣으면 1번 항목의 메뉴가 2번 항목의 메뉴로 이동하게 된다.(ifRoom 이라는 값 처럼 공간이 있다면)

<item    android:id="@+id/shareMenu"    android:title="@string/share"    android:icon="@android:drawable/ic_menu_share"    app:showAsAction="ifRoom"/>
<item    android:id="@+id/deleteMenu"    android:title="@string/delete"    android:icon="@android:drawable/ic_menu_delete"    app:showAsAction="ifRoom"/>

app 이라는 프로퍼티는 아래처럼 따로 정의된 스키마 소스를 임포트 해줘야 정상 동작 한다.

xmlns:app="http://schemas.android.com/apk/res-auto"


[컨텍스트 메뉴 만들기]

컨텍스트 메뉴를 만들기 위해서는 TextView를 하나 선언해서 메뉴가 들어갈 view를 생성해 주어야 한다.

MainActivity.java

private TextView listView;
private void setEvent(){
    listView = (TextView)findViewById(R.id.listView);
    registerForContextMenu(listView);
}

@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    menu.add(0,MENU_MAKE_COMPLETE,0,R.string.complete);

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.context_item_list, menu);
}


res/menu/context_item_list.xml

<item    android:id="@+id/deleteMenu"    android:title="@string/delete"></item>



Android Studio - @string 리소스 'R' 관련해서 쉽게 추가하자

Android 에는 편리한 단축 기법이 많이 있는데, 이것만 기억하자

Alt + 'Enter'

빨간줄이 나오거나할 때 위에 단축키만 입력하면 알아서 해법을 찾아다 준다.

1. res/layout 등의 xml 파일에서 바로 res/values 밑에다가 string 을 추가한다고하면

  - 일단 @string/todo 라고 선언 해 두고
  - 빨간 불이 들어오면 Alt + 'Enter'

  - 생성된 마법사 메뉴를 통해서 생성!
  - 끝

2. .Java 소스코드에서 R.string.* 에다가 추가 한다고 한다면

  - 일단 @string/todo 라고 선언 해 두고
  - 빨간 불이 들어오면 Alt + 'Enter'

 - 생성된 마법사 메뉴를 통해서 생성!

  - 끝!