Nano Blog - 오병우

프로그래밍 +60

안드로이드 스튜디오 4.0.1까지는 잘 작동했는데 4.1로 업그레이드하니 Layout XML에서 지정한 id를 Kotlin (.kt) 파일에서 바로 참조할 수 없고 findViewById<클래스>(R.id.아이디)로 참조해야 한다.

안드로이드 4.2에서 View Binding을 사용하도록 변경될 것이라고 하는데 그 사이에 기존에 사용하던 코드를 사용할 수 있는 방법을 설명한다.

 

1. build.gradle (Module: ~.app) 파일 앞 부분에 'kotlin-android-extensions' 추가

(Before) -------------------------------------

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

(After) -------------------------------------

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}

 

2. 화면 오른쪽 위 Sync Now 클릭

 

하루가 다르게 변하고 점점 복잡해지는 안드로이드 개발 환경에 대한 피로도가 높아진다.

 

Android Studio에서 Tools 메뉴-SDK Manager-SDK Tools Tab 열기

 

Android Emulator Hypervisor Driver for AMD Processors (installer) 체크

OK 버튼

---------------------------------------------------------------------------

제어판-프로그램-프로그램 및 기능-Windows 기능 켜기/끄기

 

다음 두 개 기능 켜기 (체크)

1. Windows 하이퍼바이저 플랫폼

2. 가상 머신 플랫폼

 

그 뒤에 AVD 실행하면 x86 CPU 이미지도 실행 가능

프로그램 작성시 실행한 뒤에 종료하지 않고 다시 Build할 경우에 재시도하다가 다음과 같은 에러가 발생한다.

MSB3021, MSB3026, MSB3027 파일은 다른 프로세스에서 사용 중이므로 프로세스에서 액세스할 수 없습니다.

 

Ctrl+F5 눌러서 실행하고 나서 바로 실행이 안되면 매번 "아차 프로그램 종료 안했구나"하고 종료해 줘야하는게 귀찮으니 Build시에 taskkill 명령을 돌려버리면 편하다. (옵션에 있는지 모르겠는데 못찾아서 taskkill 사용함)

 

프로젝트명에서 마우스 오른쪽 버튼-속성-빌드 이벤트

"빌드 이벤트 명령줄 대화 상자(R):" 밑에 입력 또는 "빌드 전 편집(I)..." 버튼 클릭

TASKKILL  /FI "STATUS eq RUNNING" /IM [프로젝트 이름].exe /F /T

 

그 뒤에 Ctrl+F5(실행)이나 F7(빌드) 누르면 자동으로 이전 실행했던 exe 강제 종료해줘서 편하다. 속이 다 후련~

 

/FI는 filter인데 맨 처음에 실행되지 않고 있을 때 프로세스가 없다고 에러나는 것을 방지해 준다.

Kotlin으로 Volley NetworkImageView 사용하려고 공식 문서를 따라하는데 에러가 발생한다.


https://developer.android.com/training/volley/requestqueue#kotlin


원인은 Kotlin의 Null safety 때문.

다음의 코드로 해결할 수 있다.

mImageLoader = ImageLoader(mQueue,
object : ImageLoader.ImageCache {
private val cache = LruCache<String, Bitmap>(20)

override fun getBitmap(url: String): Bitmap? {
return cache.get(url)
}

override fun putBitmap(url: String, bitmap: Bitmap) {
cache.put(url, bitmap)
}
})

차이점은 getBitmap()의 return type을 Nullable인 Bitmap?으로 변경함

물음표 하나로 해결됨


Kotlin에 익숙하면 금방 고칠 수 있겠지만... 공식 문서인데...


iOS 처럼 개발자를 위해 예제 좀 바로 가져다 쓰도록 잘 만들어 줬으면 좋겠다. 운영체제 옆그레이드에만 열을 올리는 듯...


실습 중에 R에 빨간색으로 표시되는 에러로 고생하는 학생이 많다.

class R은 Resource를 관리하기 위해 자동으로 생성되는 클래스이다.

XML을 추가했는데 오타가 있거나 하면 R 클래스가 제대로 생성되지 않는다. 일단 최근에 추가한 내용에 오타가 없는지 확인이 필요하다.


간혹 오류가 없는데도 R이 제대로 생성되지 않는 경우가 있다.

메뉴 Build-Rebuild Project를 수행하거나 Android Studio를 다시 시작해 보면 해결되는 경우가 있다.


메뉴 File-Invalidate Cashes/Restart로 해결되는 경우도 있다.


이클립스 사용할 때 Ctrl+Shift+O를 애용했는데 안드로이드 스튜디오는 매번 Alt+Enter 눌러줘야 되서 불편하다.


Auto Import 기능을 사용하면 편리해 진다.


File-settings-Editor-General-Auto Import


Ask를 All로 변경.

Add unambiguous ... 체크

Optimize ... 체크

이 에러도 최신 업데이트로 해결되었으리라 믿습니다. 최신 업데이트 설치를 생활화합시다.


비정기적으로 resource.h 파일에 한자가 잔뜩 들어가 있어서 에러가 발생합니다.


파일-솔루션 닫기 해주시고 다시 열어서 리소스 뷰에서 버튼 같은 것 하나 넣어주신 후 빌드하시면 resource.h를 덮어쓰겠냐고 물어 봅니다. 

이 때 "예" 해주시면 에러가 해결됩니다. 

아까 괜히 넣었던 버튼 같은 것을 삭제해 주시고 다시 빌드 하시면 에러를 해결할 수 있습니다.

Visual Studio 2017 사용시 최신 버전으로 업데이트를 하시면 에러가 많이 줄어드니 반드시 업데이트 하시고 사용하시기를 권장합니다.


대화 상자 기반 프로젝트를 생성한 뒤에 리소스 뷰에서 컨트롤을 추가하고 빌드하면 "file not found" 에러가 발생합니다.

이 떄에는 다이얼로그 GUI 편집 화면을 닫고, 솔루션 탐색기에서 .rc 파일에서 오른쪽 버튼을 눌러서 "코드 보기"를 선택합니다.


소스 코드에서 48라인에 "언어"라고 한글로 되어 있는 것을 "Language"로 변경하고 저장하신 뒤에 파일을 닫고, 리소스 뷰에서 작업하시면 에러가 발생하지 않습니다.


MFC 개발 환경은 VS Community 버전에서 자동으로 설치되지 않습니다. 

"새 프로젝트 만들기..."에서 " Visual Studio 설치 관리자 열기"를 눌러서 "C++를 사용한 데스크톱 개발"을 누르고 MFC 개발 환경을 설치하시기 바랍니다.


파일-새로 만들기-프로젝트 메뉴에서 Visual C++에서 MFC 응용 프로그램을 선택하고 확인 버튼을 누릅니다.


응용 프로그램 종류에서 "단일 문서" 선택하고 프로젝트 스타일을 "MFC 표준" 선택합니다.


원래 이렇게 하면 단일 문서 생성되어야 하는데, Visual Studio 스타일의 프로젝트가 생성됩니다. ㅠㅠ


해결 방법은 "고급 기능"에 가서 "탐색기 도킹 창", "출력 도킹 창", "속성 도킹 창"을 체크 해제합니다.


그리고, 마침을 눌러야 합니다. (다음을 누른뒤에 마침을 누르면 마찬가지로 Visual Studio 스타일의 프로젝트가 생성됩니다.)


4개(정보 다이얼로그까지 치면 5개)의 클래스로 구성된 깔끔한 단일 문서 프로젝트를 만들 수 있습니다.



BitmapDrawable d = (BitmapDrawable)getResources().getDrawable(R.drawable.img, null); // , null 추가


build.gradle에서 minSdkVersion 21로 변경

마시멜로부터 드디어 Android에서 공식 MIDI API를 제공한다. iOS에 비해 너무 늦다.


공식 API 설명

http://developer.android.com/intl/ko/reference/android/media/midi/package-summary.html


MIDI 노트 재생하는 소소를 원하면 다음의 샘플 참조.

http://developer.android.com/intl/ko/samples/MidiSynth/index.html


미디 디바이스와 연결 관련 내용은 다음의 링크 참조.


http://developer.android.com/intl/ko/samples/MidiScope/index.html


비록 MIDITools라는 라이브러리를 사용하긴 하지만 MIDIKeyboard는 봐둘 필요가 있을 듯 하다. 이 분이 아마도 Android MIDI API 개발에 참여하신듯...(잘 모름)

https://github.com/philburk/android-midisuite



1. Volley 사용 (권장)


http://developer.android.com/intl/ko/training/volley/index.html




2. build.gradle에 apache 라이브러리 사용 추가 (권장하지 않음. 임시로 돌릴 때만 사용할 것)


android {
compileSdkVersion 23
buildToolsVersion "23.0.1"

defaultConfig {
...
}
buildTypes {
...
}
    useLibrary 'org.apache.http.legacy'
}


왼쪽 위 삼선 눌러서 DrawerLayout 나올 때 원래 내용(나머지 부분)이 어두워 지는 것을 막는 방법

mDrawerLayout.setScrimColor(Color.TRANSPARENT);


---------------------------------------

그냥 그림자 없애려면 다음 함수 사용


setDrawerShadow(Drawable shadowDrawable, int gravity)

setDrawerShadow(int resId, int gravity)

솔루션 뷰 - [프로젝트명].rc 파일 더블 클릭

String Table - String Table - IDR_MAINFRAME


  <windowTitle>\n<docName>\n<fileNewName>\n

                    <filterName>\n <filterExt>\n<regFileTypeID>\n

                    <regFileTypeName>\n <filterMacExt(filterWinExt)>\n

                    <filterMacName(filterWinName)>


출처: http://support.microsoft.com/kb/129095/ko


Visual Studio 2013 기준으로 다음 위치에 구현 소스 코드 있음 (다른 버전은 12.0 부분 변경)


C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfc


찾으려던 Polygon은 Win32 API에 있어서 실패...

모바일 융합 기술 센터

스마트 앱 창작터 안드로이드 기초 및 실습

 

Day9Ex1 앞부분 구현 내용 (내용 추가 필요)

 

package kr.re.mctc.android.day9ex1weatherinfo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Window;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 String mResult = null;
 WeatherTask mTask = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  // setContentView() 앞에서 호출
  requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

  setContentView(R.layout.activity_main);
  
  mTask = new WeatherTask();
  mTask.execute();
 }

 class WeatherTask extends AsyncTask<Void, Void, String> {
  @Override
  protected void onPreExecute() {
   super.onPreExecute();
   setProgressBarIndeterminate(true);
  }

  @Override
  protected String doInBackground(Void... params) {
   String query = "http://api.openweathermap.org/data/2.5/weather?q=kumi";
   BufferedReader rd = null;
   String rLine = "";
   StringBuilder result = new StringBuilder();
   
   HttpClient httpClient = new DefaultHttpClient();
   HttpGet httpGet = new HttpGet(query);
   try {
    HttpResponse response = httpClient.execute(httpGet);
   
    rd = new BufferedReader(new InputStreamReader(
       response.getEntity().getContent()));
    while ((rLine = rd.readLine()) != null) {
     result.append(rLine);
    }
    return result.toString();
   } catch (ClientProtocolException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
   return null;
  }
  
  @Override
  protected void onPostExecute(String result) {
   super.onPostExecute(result);
   setProgressBarIndeterminate(false);
   
   Toast.makeText(MainActivity.this,
     result, Toast.LENGTH_SHORT).show();
   
   mResult = result;
  }  
 }
}

모바일 융합 기술 센터

스마트 앱 창작터 안드로이드 기초 및 실습 강의 자료

Day8Ex2MyList - MainActivity.java

 

import java.util.ArrayList;

import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity
       implements OnItemClickListener {

// protected String[] mArray = { "걸스데이", "에이핑크", "소녀시대", "시크릿",
//   "걸스데이", "에이핑크", "소녀시대", "시크릿",
//   "걸스데이", "에이핑크", "소녀시대", "시크릿",
//   "걸스데이", "에이핑크", "소녀시대", "시크릿" };
 ArrayList<IdolInfo> mArray = new ArrayList<IdolInfo>();
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mArray.add(new IdolInfo("걸스데이", "썸씽"));
  mArray.add(new IdolInfo("에이핑크", "No No No"));
  mArray.add(new IdolInfo("소녀시대", "Gee"));
  mArray.add(new IdolInfo("시크릿", "매직"));
  mArray.add(new IdolInfo("걸스데이", "썸씽"));
  mArray.add(new IdolInfo("에이핑크", "No No No"));
  mArray.add(new IdolInfo("소녀시대", "Gee"));
  mArray.add(new IdolInfo("시크릿", "매직"));
  
  ListView list = (ListView)findViewById(R.id.listView);
//  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
//        android.R.layout.simple_list_item_1,
//        mArray);
  IdolAdapter adapter = new IdolAdapter(this,
    android.R.layout.simple_list_item_1);
  list.setAdapter(adapter);
  list.setOnItemClickListener(this);
 }

 @Override
 public void onItemClick(AdapterView<?> arg0, View arg1,
   int position,
   long arg3) {
//  Toast.makeText(this, "클릭: " + position + "(" + mArray[position] + ")",
//      Toast.LENGTH_SHORT).show();
  new AlertDialog.Builder(this)
  .setTitle("아이템 선택")
  .setMessage(mArray.get(position).getName() + " 아이템을 선택했습니다.")
  .setPositiveButton("확인", null)
  .show();
 }
 
 public class IdolInfo {
  protected String name;
  protected String song;
  
  public IdolInfo(String name, String song) {
   super();
   this.name = name;
   this.song = song;
  }

  public String getName() {
   return name;
  }

  public void setName(String name) {
   this.name = name;
  }

  public String getSong() {
   return song;
  }

  public void setSong(String song) {
   this.song = song;
  }
 }
 
 static class IdolViewHolder {
  TextView idolName;
 }
 
 public class IdolAdapter extends ArrayAdapter<IdolInfo> {

  protected LayoutInflater mInflater = null;
  
  public IdolAdapter(Context context, int resource) {
   super(context, resource);
   mInflater = LayoutInflater.from(context);
  }

  @Override
  public int getCount() {
   return mArray.size();
  }

  @Override
  public View getView(int position, View v, ViewGroup parent) {
   IdolViewHolder viewHolder;
   if (v == null) {
    v = mInflater.inflate(android.R.layout.simple_list_item_1,
         parent, false);
    
    viewHolder = new IdolViewHolder();
    viewHolder.idolName = (TextView)v.findViewById(android.R.id.text1);
    v.setTag(viewHolder);
   }
   else {
    viewHolder = (IdolViewHolder)v.getTag();
   }
   
   IdolInfo info = mArray.get(position);
   if (info != null) {
    viewHolder.idolName.setText(info.getName());
   }
   
   return v;
  }
 }
}