레이블이 인 게시물을 표시합니다. 모든 게시물 표시
레이블이 인 게시물을 표시합니다. 모든 게시물 표시

2024년 5월 27일 월요일

[Android Studio 팁] powershell7 터미널 설정

증상:

Android Studio 터미널

Powershell7 을 윈도에 설치하였지만 Android Studio 터미널은 내장 powershell (5.1 버전) 이다.

해결:

File -> Settings

Tools -> Terminal -> Application Settings -> Shell path


e.g.) C:\Program Files\PowerShell\7\pwsh.exe

Android Studio 터미널


2024년 5월 7일 화요일

[Android Studio 팁] 자동으로 Run 탭으로 이동하는 증상


증상




앱을 실행하면 자동으로 Run 탭으로 이동한다.

  • 로그캣 탭을 보고 있는데 자동으로 Run 탭으로 넘어가 불편하다



실행시 자동으로 Run 탭으로 이동하지 않도록 하기




메뉴 -> Run -> Edit Configurations...




제일 아래 Activate tool window 체크를 없애면 증상이 없어진다

  • 제일 아래 있기 때문에 스크롤 필요할 수 있다



2017년 5월 26일 금요일

adb 및 adb shell 명령어 정리

PC 에 연결된 Device 목록 확인
$ adb devices

logcat 캐시 지우기
$ adb logcat -c

logcat 시간 정보 포함하여 출력
$ adb logcat -v threadtime

APK 설치
$ adb install <file_path>

pm 명령 (adb shell)
$ pm install <apk path>

  • full path 로 입력해야 됨
  • -r 옵션: 기존에 설치된 경우 재설치


앱 제거
$ adb unintsall <package_name>

pm 명령 (adb shell)
$ pm uninstall <package name>

앱 종료 (adb shell)
$ kill <pid>

activity 시작 (adb shell)
$ am start <package>/<activity>

e.g.)
$ am start com.sample/.MainActivity

service 시작
$ am startservice <package>/<service>

e.g.)
$ am startservice com.sample/.MyService

package 정보 확인
$ dumpsys package <package name>
  • permission 정보, data path 등 여러 정보 확인 가능

e.g.)
$ dumpsys package com.android.systemui

permission 목록
$ pm list packages -g

broacast
$ adb shell am broadcast -a ${action_name} -e ${extra_key} ${extra_string_value}

  • 가능한 옵션들

    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
    [-c <CATEGORY> [-c <CATEGORY>] ...]
    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
    [--esn <EXTRA_KEY> ...]
    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]
    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]
    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]
    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]
    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as Integer[])
    [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as List<Integer>)
    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as Long[])
    [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as List<Long>)
    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as Float[])
    [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as List<Float>)
    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as String[]; to embed a comma into a string,
         escape it using "\,")
    [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as List<String>; to embed a comma into a string,
         escape it using "\,")
    [--grant-read-uri-permission] [--grant-write-uri-permission]
    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]
    [--debug-log-resolution] [--exclude-stopped-packages]
    [--include-stopped-packages]
    [--activity-brought-to-front] [--activity-clear-top]
    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
    [--activity-launched-from-history] [--activity-multiple-task]
    [--activity-no-animation] [--activity-no-history]
    [--activity-no-user-action] [--activity-previous-is-top]
    [--activity-reorder-to-front] [--activity-reset-task-if-needed]
    [--activity-single-top] [--activity-clear-task]
    [--activity-task-on-home]
    [--receiver-registered-only] [--receiver-replace-pending]
    [--selector]
    [<URI> | <PACKAGE> | <COMPONENT>]

2015년 1월 29일 목요일

Eclipse -> Android Studio

참고

Android Studio 로 변환 과정

Android Studio 가 Android 공식 개발 도구로 지정되었습니다.
Android Studio 사용법을 익혀야 겠습니다.

소개

  • 간단하게 이야기 하자면 Android Studio 에서 eclipse project 를 불러오면 자동으로 변환이 됨
  • 예제 프로젝트를 생성하여 설명

1. sample 용 Eclipse 안드로이드 project 생성

  • hello : Android application project
  • appcompat_v7 : hello 생성하면서 자동으로 import 된 appcomat v7 library project
  • libproject :임의로 생성한 library project (hello 가 의존)



2. Android Studio 실행


3. Import Non-Android Studio project 를 선택하여 eclipse 프로젝트를 변환


Import Non-Android Studio projct 선택

eclipse 에서 생성한 hello 프로젝트를 선택
변경될 프로젝트가 저장될 위치 선택

변환 옵션 (기본으로 두고 finish 했음)


변환

변환 완료시 Language Level Changed 다이얼로그가 뜨기도 함 (Yes 선택)


4. 변환 완료

  • import-summary.txt : 변환과정 중 일어난 일들을 표시함
    • Ignored Files 항목을 보면 옮겨지지 않은 파일들이 표시됨 (필요시 직접 이동) 
  • 프로젝트 구조
    • hello(hello-hello 로 표시) : eclipse 의 hello project 에 해당하는 모듈
    • libproject : eclipse 의 libprojct project 에 해당하는 모듈


5.Android Studio프로젝트 구조 (맛보기)


구조
  • Eclipse 에는 workspace 라는 개념이 있고 그 아래 여러 프로젝트들이 존재
  • Android Studio 에는 프로젝트 아래 여러 모듈들이 존재
gradle 파일
  • settings.gradle
    • 관련 모듈 목록을 설정
    • 프로젝트에 하나 존재
  • build.gradle 파일
    • build 관련 설정 (의존성등을 설정)
    • 프로젝트에 하나, 모듈마다 하나씩 갖고 있음
왼쪽 상단에 Project 보기를 선택


변환된 폴더 구조를 확인 가능

6. 앱 실행 해보기


실행 버튼


기기가 없다면.. 에뮬레이터 선택

앱이 실행됨

하단에 logcat 창 표시됨

결론

이 외 아직 제대로 사용해 보진 않았지만 메뉴들을 잠깐씩 훑어보니 어렵지는 않겠습니다.
물론 겪어봐야 알 일이겠지요

2013년 10월 11일 금요일

앱 완전 종료 (kill process)

안드로이드에서는 앱 종료시 process 를 종료하는 방법을 권하지 않는 듯 보이지만 때론 고민을 덜어줄 수도 있다.

android.os.Process.killProcess(android.os.Process.myPid());

조용하고 즉각적으로 앱을 완전히 종료 시킨다.

2013년 9월 4일 수요일

EditText show password toggle

먼저 layout xml 에 해당 EditText 의 password 속성을 true 로 설정한다.

자바 코드에서 해당 EditText 객체의 setInputType() 함수를 이용해서 toggle 한다.

Password 보이기
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);

Password 숨기기
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);

!) input type 을 변경하게 되면 커서 위치가 맨 앞으로 이동한다.
아래와 같이 마지막 커서 위치를 복원한다.
int pos = password.getSelectionEnd();
// input type 변경
if (pos >= 0) {
    password.setSelection(pos);
}


안드로이드 소스 참고:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.3_r2.1/com/android/settings/wifi/WifiConfigController.java#WifiConfigController.updatePasswordVisibility%28boolean%29

2013년 7월 31일 수요일

파일 변경 사항을 PC explorer 에서 볼 수 없는 문제

몇몇 안드로이드 기기들 (ASUS tablet 등) 에서 코드로 파일을 생성 또는 삭제한 사항이 PC explorer 에 반영되지 않는 문제가 있다.

아래 링크에서 해결 방법을 찾았다.
http://stackoverflow.com/a/9462347

context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

다른 방법으로는 MediaScannerConnection.scanFile() 함수를 이용하는 방법이 있었는데 해당 함수에는 반드시 파일 경로를 넣어 줘야 해서 파일이 삭제된 경우에는 갱신할 방법이 없어 보였다.

2013년 7월 16일 화요일

layout 조정 완료 event

도움: http://stackoverflow.com/questions/8418868/how-to-know-when-an-activity-finishes-a-layout-pass

layout 조정이 완료된 시점을 알아야 하는 경우가 있다.

가변적인 부모 view 의 크기를 이용해서 자식 view 의 크기를 결정해야 하는 경우가 있었다.

ViewTreeObserver 에 OnGlobalLayoutListener 를 추가해 줌으로써 해결 할 수 있었다.
View view = findViewById(R.id.view);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 @Override
 public void onGlobalLayout() {
  
  // layout 이 완료되고 모든 view 들의 크기가 정해진 상태
 }
});

한가지 주의할 점은 이벤트가 불렸을 때 어떠한 view 의 layout 을 변경한다면 무한대로 해당 이벤트가 불리기 때문에 layout 을 변경해야 하는 시점에만 layout 을 변경하도록 처리해야 한다.

예를 들어 해당 이벤트 발생시 어떠한 view 의 크기를 변경해야 한다면 이미 크기 변경이 이루어진 경우에는 처리하지 않도록 하는 방법이 있겠다.

2013년 6월 6일 목요일

DIP (Density Independent Pixels)

PX(Pixel)은 화면에 표시할 수 있는 최소한의 단위이다.

PX 의 크기는 물리적으로 아주 작지만 해상도마다 크기가 다르다.

DPI (Dot per inch) 는 1인치 안에 들어있는데 픽셀의 개수를 나타낸다.

160 dpi 는 1인치에 160 개의 픽셀이 존재한다고 보면 된다.

해상도가 높을 수록 픽셀의 물리적 크기는 더욱 작아진다.
 - 결국 더 세밀하게 표현할 수 있다.

DIP (Density independent pixels) 는 해상도에 상관없이 물리적으로 같은 크기의 출력을 원할 때 사용한다.

안드로이드 앱에 여러 해상도 별로 보여줄 이미지를 제공할 수 있다.
ldpi (low dpi), mdpi (medium dpi), hdpi (high dpi), xhdpi (xhigh dpi)  ...
 - 이런 기능을 제공하는 이유는 물론 여러 해상도에서 물리적으로 항상 같은 크기의 영역에 이미지 등을 화면에 출력하기 위해서 이다.

코드상으로 화면에 view 를 출력하거나 직접 그리기 위해서는 PX 단위를 이용해야 한다.

dip 값을 px 으로 변환하기 위해서는 일반 dpi 와 현재 화면의 dpi 의 비율을 나타내는 density 값을 이용해야 한다.

안드로이드의 기본 dpi 는 mdpi (medium dpi, 160 dpi) 이다.
 - mdpi 에서는 100dp = 100px 이다.
 - 즉 mdpi 에서 density 는 1 이다.

ldpi (low dpi, 120dpi) 에서는 100dp = 75px 이다.
 - density 는 120 / 160 = 0.75 이다.

Density 얻기
Context 의 getResources().getDisplayMetrics().density 값

Dip 단위를 px 로 변환
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100f, getResources().getDisplayMetrics());


사실 mdpi 라고 해서 무조건 160dpi 인거는 아니다.
그리고 x 축과 y 축의 dpi 가 다를 수 있다.

더 정확하게 계산하기 위해서 x 축 dpi 와 y 축 dpi 를 이용해야 한다.

float xdensity = getResrouces().getDisplayMetrics().xdpi / 160f;
float ydensity = getResrouces().getDisplayMetrics().ydpi / 160f;

2013년 5월 2일 목요일

Google play 개발자 콘솔 접속 오류 발생하는 경우

Google play 개발자 콘솔 접속 시 무한 redirection 시도로 인한 오류가 발생하는 경우

웹 브라우저의 쿠키를 삭제하고 다시 접속해 보시기 바랍니다.

구글 검색해도 나오지 않아 적습니다.

EditText in ListView

EditText 를 ListView 에서 사용하는 데에 많은 고난과 시련이 따랐다.

Focus 문제

EditText 를 선택하면 focus 가 제대로 가지 않는 문제가 있었다.
AndroidManifest.xml 수정: 해당 activity 에 아래 속성을 추가
android:windowSoftInputMode="adjustPan"
EditText 에 입력하는 내용을 ListView 에 적용
  • 시도 1) EditText  에 onFocusChangedListener 를 입력하고 focus 를 잃을 때 ListView adapter 를 업데이트 하게 했다.
    • 키보드 눌릴 때 마다 focus 를 잠깐 잃었다가 다시 들어와서 실패
  • 시도 2) EditText 에 onEditorActionListener 를 입력하고 사용자 입력이 끝나면 ListView adpater 를 업데이트하게 했다.
    • 이벤트가 제대로 오지 않았다.
  • 결론
    • EditText 변경이 끝나는 시점을 찾아서 ListView adpater 를 업데이트 하는 거는 실패했다.
    • 단지 해당 아이템에 값만 변경해 주면 되었다. (아래와 같은 이유)
      • EditText 가 보이는 동안에는 입력한 내용이 보일 것이다.
      • EditText 가 보이지 않았다가 보이게 되는 경우 adapter 의 getView() 함수가 불리면서 알아서 갱신된다.
    • *) EditText 에 addTextChangedListener() 함수를 이용해서 text 변경시 마다 listview item 에 반영했다.

activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

</RelativeLayout>
layout_list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    >

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sample"
        android:layout_weight="0.3" />

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.7"/>

</LinearLayout>
MainActivity.java:
import android.content.Context;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends ActionBarActivity {

    private ListView listView;
    private List<Item> list = new ArrayList<Item>();

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

        for (int i = 0; i < 30; i++) {
            list.add(new Item("Item" + (i+1), "" + i));
        }

        listView = (ListView)findViewById(R.id.listView);
        listView.setAdapter(new MyAdapter(this, list));

    }

    private class Item {
        public String text;
        public String edit;
        public Item(String text, String edit) {
            this.text = text;
            this.edit = edit;
        }
    }

    private class MyWatcher implements TextWatcher {

        private EditText edit;
        private Item item;
        public MyWatcher(EditText edit) {
            this.edit = edit;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.d("TAG", "onTextChanged: " + s);
            this.item = (Item)edit.getTag();
            if (item != null) {
                item.edit = s.toString();
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
        }
    }

    private class MyAdapter extends ArrayAdapter<Item> {

        private final static int resId = R.layout.layout_list_item;
        private Context context;
        List<Item> list;

        public MyAdapter(Context context, List<Item> list) {
            super(context, resId, list);
            this.context = context;
            this.list = list;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            View v = convertView;

            Item item = getItem(position);

            if (v == null) {

                v = getLayoutInflater().inflate(resId, null);
                EditText et = (EditText)v.findViewById(R.id.edit);
                et.addTextChangedListener(new MyWatcher(et));
            }

            TextView tv = (TextView)v.findViewById(R.id.text);
            EditText et = (EditText)v.findViewById(R.id.edit);
            et.setTag(item);

            tv.setText(item.text);
            et.setText(item.edit);

            return v;
        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}



EditText 외의 영역을 건드리면 EditText 의 focus 없애기
EditText 이외의 모든 view 에 OnTouchListener 를 등록하고 touch 하면 키보드를 숨기고 해당 view 로 focus 를 이동하게 한다.

{키보드 숨기기}
InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);

{Root view 얻기}
아무 view 나 getRootView() 함수를 이용해서 얻는다.
view.getRootView()
예)
public void hideSoftKeyboard() {
 InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
 imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

public void inspireViewsHideKeyboardOnTouch(View v, boolean recursive) {
 if (!(v instanceof EditText)) {
  v.setOnTouchListener(new View.OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    hideSoftKeyboard();
    v.requestFocus();
    return false;
   }
  });
 }
 
 if (recursive && v instanceof ViewGroup) {
  ViewGroup g = (ViewGroup)v;
  for (int i = 0; i < g.getChildCount(); i++) {
   inspireViewsHideKeyboardOnTouch(g.getChildAt(i), recursive);
  }
 }
}


2013년 3월 12일 화요일

APK install & uninstall batch 파일 (편리)

APK 설치하고 제거하는 일이 귀찮아서 batch 파일을 만들었습니다.

1. 사용법
파일명은 ak.bat 라고 지었습니다.
윈도7에서 테스트 해봤습니다.
  • PATH 지정:
    • adb 와 aapt 위치를 PATH 환경 변수에 추가해 주어야 합니다.
    • 예)
$ANDROID_SDK\platform-tools
$ANDROID_SDK\build-tools\20.0.0

  • 설치 방법:
> ak install <apk file path>
또는
> ak -s <device id> install <apk file path>
  • 제거 방법: apk 파일이 필요한 이유는 package 명을 얻기 위해서 입니다.
> ak uninstall <apk file path>
또는
> ak -s <device id> uninstall <apk file path>

2. 스크립트 파일
간단히 설명하자면 설치할 때는 adb install 을 실행하고 제거할 때는 apk 파일에서 package 이름을 추출해서 adb uninstall 을 실행합니다.

@echo off
setlocal
 

if [%1] == [-s] (
   set device=%2
   shift
   shift
)

for %%c in (install, uninstall) do (
    if [%1] == [%%c] (
       set cmd=%1
       set filepath=%2
       goto start
    )
)

echo "Usage: %0 [-s device] <command> <file path>"
goto end

:start

if not [%device%]==[] (
  set device= -s %device%
) else (
  set device=
)

goto cmd_%cmd%

:cmd_install

set full_cmd=adb%device%%cmd% %filepath%
echo %full_cmd%
%full_cmd%

goto end

:cmd_uninstall

aapt dump badging %filepath% | find "pack" > ~temp
set /p str=<~temp
del ~temp
call set v=%%str:* name=%%
set v=%v: =&rem.%
set v=%v:~2,-1%

set full_cmd=adb%device%%cmd% %v%
echo %full_cmd%
%full_cmd%

goto end

:end
echo.

2013년 2월 20일 수요일

WebView 만들기

1. 먼저 Internet 사용 권한이 있어야 한다.
<uses-permission android:name="android.permission.INTERNET" />

2. 간단한 WebView
예) 단순히 Google 페이지를 열어 준다.
WebView webView = new WebView(this);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.google.com");
setContentView(webView);


3. javascript 활성화
webView.getSettings().setJavaScriptEnabled(true);

4. back 키를 누르면 뒤로 가고 history 가 없으면 기본 back 키 동작
예)
@Override
public void onBackPressed() {
    if (webView.canGoBack()) {
        webView.goBack();
    } else {
        super.onBackPressed();
    }
}


5. asset 에 있는 web page 열기
loadUrl() 함수에 주소 지정시 아래와 같이 함
webView.loadUrl("file:///android_asset/html/index.html");
실제 위치는 assets/html/index.html

6. javasript alert 창 보이기
예) 간단하게 WebChromeCilent 객체를 지정해 주면 된다.
webView.setWebChromeClient(new WebChromeClient());

7. 위 사항들 종합
예) 자바 코드
webView = new WebView(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient());

webView.loadUrl("file:///android_asset/html/index.html");
setContentView(webView);


예) HTML 코드 : assets/html/index.html
<html>
<head>
<title>WebView Test</title>
</head>
<body>
<h1>WebView Test</h1>
<a href="javascript:alert('Test');">Alert Test</a><br />
Javascript is <span id="text">disabled</span>
<script type="text/javascript">
var text = document.getElementById("text");
text.innerHTML = "enabled";
</script>
</body>
</html>


8. Javascript Interface 사용
 - Javascript Interace 를 이용하면 웹 페이지에서 javascript 를 이용해서 java 코드를 호출 할 수 있다.
 - 참고 : http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface%28java.lang.Object,%20java.lang.String%29
 - 함수는 반드시 public 으로 지정해야 하고 안드로이드 4.2 (JellyBean MR1) 부터는 @JavascriptInterface 을 지정해 줘야 함
예) 자바 코드
webView.addJavascriptInterface(new Object() {
    @JavascriptInterface
    public String toString() {
        return "AppInterface";
    }
    @JavascriptInterface
    public void toast(String text) {
        Toast.makeText(WebViewActivity.this, text, Toast.LENGTH_LONG).show();
    }
}, "AppInterface");


예) HTML 코드
<a href="javascript:AppInterface.toast('Hello');">Javascript Interface Test : Toast</a><br />

9. 하이퍼 링크 URL 을 확인하여 기본 브라우저로 띄우기
webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
           
        if (Uri.parse(url).getHost().indexOf("google") >= 0) {
            // host 명에 google 이 포함되면 webview 로 보여줌
            return false;
        }
        

        // 아니면 기본 브라우저를 띄움
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
   
        return true;
    }
});


2013년 1월 12일 토요일

EditText spell check 끄기

input type 속성 에 textNoSuggestions flag 를 추가해 주면 된다.

xml 에서 설정하는 방법:
참고: http://stackoverflow.com/a/12022628

android:inputType="textNoSuggestions"

multi line 이 필요하다면 아래와 같이 설정한다.

android:inputType="textMultiLine|textNoSuggestions"

코드에서 설정하는 방법:

setRawInputType() 함수를 이용한다.
주의할 점은 setInputType() 함수와 혼동하지 말아야 한다.

예)
et.setRawInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

2013년 1월 8일 화요일

Developer option missing in JellyBean

4.2 버전 부터 기본 적으로 개발자 옵션이 설정 화면에서 사라졌다.


개발자 옵션을 활성화 하려면 설정 -> 태블릿 정보 (또는 About phone) -> 빌드 번호 항목을 여러번 탭해야 한다.

빌드 번호 항목을 여러번 탭하다보면 toast 메시지가 보인다.
"개발자가 되셨습니다" 라는 toast 를 문구가 나올 때까지 계속 탭하고 다시 설정 화면으로 돌아가면 이전 버전 처럼 개발자 옵션이 생성된 것을 확인할 수 있을 것이다.

참고: http://howto.cnet.com/8301-11310_39-57549207-285/restore-the-developer-options-menu-in-android-4.2/

!) Android 버전 항목을 여러번 탭하면 Jellybean 로고가 화면에 표시된다.

2013년 1월 5일 토요일

READ_LOGS on JellyBean

JellyBean 버전 부터 3rd party 앱은 시스템 또는 앱 자신의 로그캣만 취득할 수 있게 변경되었다.
참고: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/6U4A5irWang

앞으로 alogcat 을 이용해서 다른 앱들의 로그를 볼 수 없게 되었다.
물론 rooting 한 폰의 경우 방법이 있다고 한다. (직접 해보지는 않음)
참고: http://code.google.com/p/alogcat/issues/detail?id=42


아래는 인용문...

인용: https://groups.google.com/d/msg/android-developers/6U4A5irWang/eitsi9NVFNEJ

Great, thx for making it harder for us devs (again and again) to troubleshoot problems... 

Ruined my afternoon but why would you care...


2012년 12월 15일 토요일

?attr 지정자

참고: http://developer.android.com/intl/ko/guide/topics/resources/accessing-resources.html#ReferencesToThemeAttributes

layout 속성 값을 아래와 같이 지정하는 경우가 있다.
?android:attr/TextAppearance.Medium

?attr 지정자를 사용하는 속성 값은 현재 테마에 지정된 값을 사용하라는 의미이다.

예)
res/values/attrs.xml
<resources>
    <attr name="myattr" format="reference" />
</resources>

res/layout/simple_layout.xml
...
<TextView
   style="?attr/myattr"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Text" />
...

res/values/themes.xml
...
<style name="MyTheme" parent="android:Theme.Holo">
    <item name="myattr">@style/MyTextStyle</item>
</style>

<style name="MyTextStyle" parent="android:TextAppearance/>
    <item name="android:textColor">#ffff0000</item>
</style>

AndroidManifest.xml
...
<activity name=".Main" android:theme="@style/MyTheme" />

Custom View 만들기

Custom View 만들기

1. 생성자

View 객체는 코드 상에서 만들어 질 수도 있고 xml 파일을 통해서 만들어 질 수도 있다.
그러므로 아래 세 종류의 생성자를 반드시 작성하자.
public CustomView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
}

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

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

AttributeSet 인자에 xml 파일에서 기술한 속성 값들이 담겨서 전달된다.

2. 함수 Override

기본적으로 onMeasure() 함수와 onDraw() 함수를 상속받아 작성한다.

  • onMeasure() 함수는 View 의 크기를 결정할 때 불리는 함수
  • onDraw() 함수는 View 가 화면에 그려질 때 불리는 함수
    • invalidate() 함수를 부르면 강제로 화면을 갱신하면서 onDraw() 함수가 불리게 된다.

1) onMeasure() 함수 작성

크기 모드

onMeasure() 함수로 넘어온 인자 값을 MeasureSpec.getMode() 함수를 이용해서 크기 mode 값을 얻는다.
크기 모드 값은 UNSPECIFIED, AT_MOST 또는 EXACTLY 값 중에 하나이다.
wrap_content 로 설정한 경우 AT_MOST 모드로 전달되고, match_parent (또는 fill_parent) 로 설정한 경우 EXACTLY 모드로 전달된다.
UNSPECIFIED 모드는 값이 정의되지 않은 경우인데 이런 경우는 의미가 없다고 본다. (코드로 View 를 작성하는 경우 발생할 수 있다고 한다)

  • UNSPECIFIED 모드의 경우 그냥 인자로 넘어온 그대로 넘겨준다.
  • AT_MOST 모드의 경우 내용물의 크기를 계산한 값을 넘겨준다.
  • EXACTLY 모드의 경우 MeasureSpec.getSize() 함수로 얻은 값을 설정한다.
반드시 마지막에는 setMeasuredDimension() 함수로 계산된 width 와 height 값을 넘겨주어 설정해야 한다.

예)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  
  int width = (int)getDip(10);
  int height = (int)getDip(10);
  
  switch (widthMode) {
  case MeasureSpec.UNSPECIFIED: // unspecified
    width = widthMeasureSpec;
    break;
  case MeasureSpec.AT_MOST:  // wrap_content
    break;
  case MeasureSpec.EXACTLY:  // match_parent
    width = MeasureSpec.getSize(widthMeasureSpec);
    break;
  }
  
  switch (heightMode) {
  case MeasureSpec.UNSPECIFIED: // unspecified
    height = heightMeasureSpec;
    break;
  case MeasureSpec.AT_MOST:  // wrap_content
    break;
  case MeasureSpec.EXACTLY:  // match_parent
    height = MeasureSpec.getSize(heightMeasureSpec);
    break;
  }
  
  setMeasuredDimension(width, height);
}

// 사용편리를 위해 작성한 함수 : dip 값을 pixel 값으로 변환하는 함수
public float getDip(float value) {
 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}

2) onDraw() 함수 작성
예)
@Override
protected void onDraw(Canvas canvas) {
  Paint p = new Paint();
  p.setColor(Color.RED);
  canvas.drawRect(1, 1, canvas.getWidth() - 1, canvas.getHeight() - 1, p);
}

Canvas 클래스에는 그림을 그리기 위한 많은 함수들이 제공된다.
선, 사각형, 원, path, 글자, 비트맵 등을 그리는 함수가 제공된다.
그리고 그림을 그리는 함수에는 Paint 인자를 넘겨주는데 그림을 그리기 위한 속성들을 설정해서 넘겨 주게 된다.
면색, 선색, 그리기 스타일 (fill, stroke, fill & stroke) 등 이다.

!) dip, sp 등의 단위를 pixel 값으로 변환하기
TypedValue.applyDimension() 함수를 이용해서 여러 단위로 설정된 값을 pixel 값으로 변환 할 수 있다.

예)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());

TypedValue 클래스에는 여러 단위들이 정의 되어 있다.

3) ViewGroup 을 상속 받은 경우 dispatchDraw() 함수를 구현한다.

4) scroll 이벤트를 처리하고 싶다면 onScrollChanged() 함수를 구현한다.

2012년 9월 12일 수요일

앱 종료시 back 키 처리

참고: http://stackoverflow.com/questions/2257963/android-how-to-show-dialog-to-confirm-user-wishes-to-exit-activity

onBackPressed() 함수를 override 하고 super 클래스의 onBackPressed() 함수는 부르지 않으면 back 키를 눌러도 finish 처리하지 않게 할 수 있습니다.

예)
@Override
public void onBackPressed() {
// super.onBackPressed();

openDialog(DLG_ID_CLOSE_CONFIRM);
}

super 클래스의 함수를 부르는 대신 다이얼로그를 띄우게 하였습니다.
해당 다이얼로그에서 ok 버튼을 누르면 앱을 종료하게 하였습니다.

대신, onKeyDown() 함수를 이용하는 방법도 있습니다.


@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK) {
openDialog(DLG_ID_CLOSE_CONFIRM);
return false;
}

return super.onKeyDown(keyCode, event);
}

onKeyDown() 함수에서 false 를 리턴하면 기본 키 동작을 처리하지 않습니다.
Home 버튼 등은 적용되지 않는 것으로 알고 있습니다.

2012년 9월 6일 목요일

Activity 시작시 virtual keyboard 띄우지 않게 하기

EditText 를 배치하는 경우 Activity 를 시작하자 마자 virtual keyboard 가 뜨는 경우가 있다.

참고: http://stackoverflow.com/questions/4149415/onscreen-keyboard-opens-automatically-when-activity-starts

이를 막기 위해 onCreate() 함수에 아래 코드를 삽입한다.

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);