Layout 은 ViewGroup 클래스로부터 상속을 받으며, Layout 안에 존재하는 위젯(버튼, 텍스트뷰,에디트뷰 등)을 배치한다. 화면에 구성하려면 Layout 에 대해 이해를 해야 한다.
스마트폰의 크기가 다양하므로 절대 픽셀 위주로 Layout을 잡는 것은 대단히 위험하다.
그래서 본 내용에서는 절대 Layout 은 다루지 않는다.
공통
android:layout_width="match_parent" // 무조건 남아있는 여유공간을 채움 android:layout_width="wrap_content" // View의 내용물의 크기에 따라 결정되도록 함 android:layout_margin="10dp" // 위젯과 여유 공간 사이의 여백 설정 android:padding="20dp" // 위젯 내부 여백 설정
LinearLayout
간단하고 직관적으로 이해하기 쉬워서 많아 사용한다.
자식(child) View 들을 수평 또는 수직으로 배치한다.
android:orientation="vertical" // 수직으로 배치(수직으로 차곡차곡 쌓겠다)
Gravity 속성을 이용하여 자식(child) View 를 상단, 하단, 중앙에 배치할 수 있다.
android:layout_weight="1" // View 내부 child끼리의 크기 비율 지정(공간 가중치)
child View 의 가중치를 지정하지 않으면 0 으로 간주하고 확장하지 않는다.
LinearLayout 을 여러개 사용하면 각 Layout의 크기를 지정해야 한다.
내부 LinearLayout 에서 layout_weight="1" 로 모두 지정하면 비율이 동일하게 출력된다.
layout_margin은 "값"만큼 띄어서 배치하겠다는 뜻
android:layout_gravity=center
gravity 속성 앞에 layout이 붙어 있으면 해당 위젯이 포함되어 있는 Layout 위젯의
정렬방식을 의미한다.
RelativeLayout
View 들의 위치가 상대적으로 정해진다.
(기준 View 가 있고, 배치하고자 하는 View를 기준 View의 어디에 둘 것인지 설정)
RelavtiveLayout 은 자신을 포함하고 있는 컨테이너나 다른 위젯을 기준으로 위젯을
배치하는 레이아웃(layout)이다.
부모 컨테이너나 다른 View(위젯)와의 상대적 위치를 이용해 화면을 구성한다.
android:layout_below="@id/topButton" <-- topButton 기준으로 하단에 위치
android:layout_above="@id/bottomButton" <-- bottomButton 기준 위에 위치
★ 자신을 포함하는 컨테이너를 기준으로 해당 위젯을 배치하는 속성들
layout_alignParentTop : 부모 컨테이너의 위쪽과 뷰의 위쪽을 맞춤 layout_alignParentBottom : 부모 컨테이너의 아래쪽과 뷰의 아래쪽을 맞춤 layout_alignParentLeft : 부모 컨테이너의 왼쪽 끝과 뷰의 왼쪽 끝을 맞춤 layout_alignParentRight : 부모 컨테이너의 오른쪽 끝과 뷰의 오른쪽 끝을 맞춤 layout_centerHorizontal : 부모 컨테이너의 수평방향 중앙에 배치 layout_centerVertical : 부모 컨테이너의 수직 방향 중앙에 배치 layout_centerInParent : 부모 컨테이너의 수평과 수직 방향 중앙에 배치
★ 다른 위젯을 기준으로 해당 위젯을 배치하는 속성들
layout_above : 지정한 뷰의 위쪽에 배치 layout_below : 지정한 뷰의 아래쪽에 배치 layout_toLeftOf : 지정한 뷰의 왼쪽에 배치 layout_toRightOf : 지정한 뷰의 오른쪽에 배치 layout_alignTop : 지정한 뷰의 위쪽과 맞춤 layout_alignBottom : 지정한 뷰의 아래쪽과 맞춤 layout_alignLeft : 지정한 뷰의 왼쪽과 맞춤 layout_alignRight : 지정한 뷰의 오른쪽과 맞춤 layout_alignBaseline : 지정한 뷰와 내용물의 아래쪽 기준선(baseline)을 맞춤
android:layout_centerVertical="true"
FrameLayout
여러 Layout 을 중첩시켜, 원하는 Layout 만 볼 수 있도록 해준다.
자식 View 들은 등장하는 순서대로 화면에 중첩되어 표시된다.
기준점은 좌측 상단이 된다.
TableLayout
격자모양의 배열을 이용하여 화면을 구성한다.
자식 View 들을 테이블 형태로 배치한다.
layout_span 은 열을 합쳐서 표시하라는 의미.
layout_span="3" 은 현재 셀부터 3개를 합쳐서 표시하라는 의미
@ : 리소스에서 참조한다는 의미
+ : 새로 생성한다는 의미
id : 식별자를 나타내는 패키지
layout1 : 식별자
이 id 를 이용하여 activity 의 onCreate() 메소드에서 findViewById()를 호출하여 layout1 을 찾을 수 있다.
java 코드에서 참조할 일이 없으면 id를 만들지 않아도 된다.
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // layout 폴더에 있는 main.xml 파일을 찾는다.
// 화면에 나타낼 View를 지정하고, xml Layout의 내용을 메모리상에 객체화한다.
// 이 두줄의 코드 위에 정보를 넣으면 객체화되지 않아서 강제종료 처리가 된다.
layout1 = (LinearLayout) findViewById(R.id.layout1); // id 가 layout1 인 것을 찾는다.
네트워크 상태체크, 폰에 자동로그인 정보기록하기, POST로 로그인 처리 하는 부분까지는 이전 게시글에 있는데 본격적으로 서버에 있는 다른 자료를 검색하면서 SESSION 검사를 해서 비정상 접속은 차단하는 것까지 해야 안전하기 때문에, 세션정보까지 연동되도록 처리하는 과정을 추가했다.
처음에 세션이 생성되는지에 대한 로그, 생성된 세션이 다른 Activity 에서 제대로 받는지 여부에 대해서 확인하는 로그인데 동일한 세션 값이 넘어가는 걸 확인할 수 있다.
자료 검색해서 원하는 결과를 제대로 얻기까지 많은 블로그를 탐독하고 찾으면서 겨우 성공했다.
$conn = new MySQLiDbClass(); // DB 함수 $DB_CONNECT = $conn->isConnectDb($DB);
// 화면에 출력할 칼럼 발췌 $sql = "select uid,name,mobile from Person "; if(!empty($query)) { $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'"; } $result = mysqli_query($DB_CONNECT,$sql);
// 1. JSON 데이터 생성방법 $c = new jsonClass(); echo $c->JSONEncode($result,'result');
?>
안드로이드 로그인 소스코드 부분이다.
IP주소 부분을 여러곳에서 사용해야 해서 별도로 Value.java 파일로 작성했다.
===== Value.java ====
import android.app.Activity;
public class Value extends Activity { public static final String IPADDRESS = "http://IP주소"; // SERVER IP public static final String VERSION = "1.0.0"; // App Version
// 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요 TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); if (mTelephony.getDeviceId() != null){ getDeviceID = mTelephony.getDeviceId(); // 스마트폰 기기 정보 } else { getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); }
String postURL = Value.IPADDRESS + "/loginChk.php"; HttpPost post = new HttpPost(postURL);
public void onStop(){ // 어플리케이션이 화면에서 사라질때 super.onStop(); // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장 if (autologin.isChecked()) { settings = getSharedPreferences("settings",Activity.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit();
// Back 버튼을 누르면 어플 종료여부 확인 처리 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == KeyEvent.KEYCODE_BACK ) { new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { moveTaskToBack(true); // 본Activity finish후 다른 Activity가 뜨는 걸 방지. finish(); //application 프로세스를 강제 종료 android.os.Process.killProcess(android.os.Process.myPid() ); } }).setNegativeButton( "No", null ).show();
// 서버 정보를 파싱하기 위한 변수 선언 String myJSON; private static final String TAG_RESULTS="result"; private static final String TAG_UID = "uid"; private static final String TAG_NAME = "name"; private static final String TAG_Mobile ="mobile";
GetDataJSON g = new GetDataJSON(); g.execute(string);
}
protected void showList() { // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력 try { JSONObject jsonObj = new JSONObject(myJSON); peoples = jsonObj.getJSONArray(TAG_RESULTS);
for(int i=0;i<peoples.length();i++){ JSONObject c = peoples.getJSONObject(i); String uid = c.getString(TAG_UID); String name = c.getString(TAG_NAME); String mobile = c.getString(TAG_Mobile); Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
class ViewHolder { public LinearLayout child_layout; public ImageView mImage; public Button childListBtn; public TextView name; public TextView mobile; }
private class ListViewAdapter extends BaseAdapter {
public void remove(int position){ mListData.remove(position); mAdapter.notifyDataSetChanged(); }
}
// Back 버튼을 누르면 어플 종료여부 확인 처리 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == KeyEvent.KEYCODE_BACK ) { new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { moveTaskToBack(true); // 본 Activity finish후 다른 Activity가 뜨는 걸 방지. finish(); //application 프로세스를 강제 종료 android.os.Process.killProcess(android.os.Process.myPid() ); } }).setNegativeButton( "No", null ).show();
MySQL 접속하는 경우를 테스트해보면 킴스큐RB 함수를 이용해도 아무런 문제없이 잘 넘어간다.
Web 상에서 접속하면 MySQL, MySQLi 모두 정상적으로 접속이 잘 된다.
안드로이드폰(삼성 갤럭시 S4)에서만 발생하는 증상인가?
심지어 로그를 찍어보면 Response : Login Success 라고 정상적으로 접속이 되었다고 나오는데도 불구하고 다음 Activity 로 넘어가질 못한다.
구글링을 해보니
I also encuntered the same in S4. I've tested the app in Galaxy Grand ,
HTC , Sony Experia but got only in s4. You can ignore it as its not
related to your app.
안드로이드와 PHP 연동하여 MySQL DB에서 결과를 회신받아 안드로이드에서 보는 방법이다.
안드로이드 폰에서 검색어를 입력하면 서버에서 해당 검색어에 대한 결과를 돌려주는 방식이다.
인터넷에 올라온 자료들은 참조하여 테스트를 하다보니 결과가 나오질 않는다. 뭐가 잘못된 것인지 찾기 위해 각종 게시글을 참조하면서 하나 하나 잘못된 점을 찾아나갔다. 틀린 점이 하나 발견되었다. conn.setDoInput(true); // 서버로부터 응답 헤더와 메시지를 읽어들이겠다는 옵션 이 코드가 추가되면 결과값이 없는지 화면에 아무것도 없다. 이 한줄의 쓰임새는 아직 파악을 못한 상태다.
$conn = new MySQLDbClass(); // DB 함수 $DB_CONNECT = $conn->isConnectDb($DB);
// 화면에 출력할 칼럼 발췌 $sql = "select uid,name,mobile from Person "; $result = mysql_query($sql);
// 1. JSON 데이터 생성방법 $c = new jsonClass(); echo $c->JSONEncode($result,'result'); ?>
참고
class jsonClass { function JSONEncode($result,$varname){ $R = array(); // 결과 담을 변수 생성 while($row = mysql_fetch_object($result)) { array_push($R, $row); }
return json_encode(array($varname=>$R)); //배열-문자열등을 json형식의 '문자열'로 변환 } } // end of Class
이제 검색 기능을 추가했다.
안드로이드에서 검색 명령어를 입력하면 PHP에서 검색명령어를 인식해야 한다.
GET 방식과 POST 방식 모두 입력값을 인식하도록 $_REQUEST['search'] 를 적어줬다.
$conn = new MySQLDbClass(); // DB 함수 $DB_CONNECT = $conn->isConnectDb($DB);
// 화면에 출력할 칼럼 발췌 $sql = "select uid,name,mobile from Person "; if(!empty($query)) { $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'"; } $result = mysql_query($sql);
// 1. JSON 데이터 생성방법 $c = new jsonClass(); echo $c->JSONEncode($result,'result'); ?>
// 서버 정보를 파싱하기 위한 변수 선언 String searchJSON; private static final String TAG_RESULTS="result"; private static final String TAG_UID = "uid"; private static final String TAG_NAME = "name"; private static final String TAG_Mobile ="mobile";
if(conn != null){ // 연결되었으면 conn.setConnectTimeout(10000); // milliseconds 연결 타임아웃시간
//add request header conn.setUseCaches(false); conn.setDefaultUseCaches(false); //conn.setDoInput(true); // 서버로부터 응답 헤더와 메시지를 읽어들이겠다는 옵션 // 이 코드를 활성화하면 응답된 내용이 전혀 없다. 비활성화하면 정상이다. conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션
GetDataJSON g = new GetDataJSON(); g.execute(string);
}
protected void showList() { // 서버에서 읽어온 정보를 sAdapter 에 저장하고 화면에 출력 try { JSONObject jsonObj = new JSONObject(searchJSON); peoples = jsonObj.getJSONArray(TAG_RESULTS);
for(int i=0;i<peoples.length();i++){ JSONObject c = peoples.getJSONObject(i); String uid = c.getString(TAG_UID); String name = c.getString(TAG_NAME); String mobile = c.getString(TAG_Mobile); Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
$conn = new MySQLDbClass(); // DB 함수 $DB_CONNECT = $conn->isConnectDb($DB);
// 화면에 출력할 칼럼 발췌 $sql = "select uid,name,mobile from Person "; if(!empty($query)) { $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'"; } $result = mysql_query($sql);
// 1. JSON 데이터 생성방법 $c = new jsonClass(); echo $c->JSONEncode($result,'result'); ?>
// 서버 정보를 파싱하기 위한 변수 선언 String searchJSON; private static final String TAG_RESULTS="result"; private static final String TAG_UID = "uid"; private static final String TAG_NAME = "name"; private static final String TAG_Mobile ="mobile";
GetDataJSON g = new GetDataJSON(); g.execute(string);
}
protected void showList() { // 서버에서 읽어온 정보를 sAdapter 에 저장하고 화면에 출력 try { JSONObject jsonObj = new JSONObject(searchJSON); peoples = jsonObj.getJSONArray(TAG_RESULTS);
for(int i=0;i<peoples.length();i++){ JSONObject c = peoples.getJSONObject(i); String uid = c.getString(TAG_UID); String name = c.getString(TAG_NAME); String mobile = c.getString(TAG_Mobile); Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
// 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요 TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); if (mTelephony.getDeviceId() != null){ getDeviceID = mTelephony.getDeviceId(); // 스마트폰 기기 정보 } else { getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); }
String postURL = "http://IP주소/loginChk.php"; HttpPost httppost = new HttpPost(postURL);
public void onStop(){ // 어플리케이션이 화면에서 사라질때 super.onStop(); // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장 if (autologin.isChecked()) { settings = getSharedPreferences("settings",Activity.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit();
// Back 버튼을 누르면 어플 종료여부 확인 처리 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == KeyEvent.KEYCODE_BACK ) { new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { moveTaskToBack(true); // 본Activity finish후 다른 Activity가 뜨는 걸 방지. finish(); //application 프로세스를 강제 종료 android.os.Process.killProcess(android.os.Process.myPid() ); } }).setNegativeButton( "No", null ).show();
public void onStop(){ // 어플리케이션이 화면에서 사라질때 super.onStop(); // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장 if (autologin.isChecked()) { settings = getSharedPreferences("settings",Activity.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit();
AsyncTask를 사용해서 스케줄링 할 수 있는 작업 수의 제한이 있고, 몇 초 정도의 짧은 작업에서만 이상적으로 동작한다는 한계가 있다. 또한, 안드로이드의 버전별로 병렬 처리 동작이 다르므로 허니콤 이후 버전에서 멀티 스레드로 병렬적인 동작을 원한다면 AsyncTask를 실행할 때 AsyncTask.THREAD_POOL_EXECUTOR 스케줄러를 지정해야 한다.
(Example)
private class DownloadFiles extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); if (isCancelled()) break; } return totalSize; }
private ListView mListView = null; private ListViewAdapter mAdapter = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mListView = (ListView) findViewById(R.id.listView); mAdapter = new ListViewAdapter(this); // 서버에 있는 정보를 읽어다가 mAdapter.addItem 에 추가하는 과정
mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(listener); } OnItemClickListener listener= new OnItemClickListener() { // 해당 아이템을 클릭했을 때 @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListData mData = mAdapter.mListData.get(position); Toast.makeText(MainActivity.this, mData.name, Toast.LENGTH_SHORT).show(); } };
private class ViewHolder { public ImageView mImage; public TextView uid; public TextView name; public TextView mobile; }
private class ListViewAdapter extends BaseAdapter {
// ListData Class 는 별도 생성한다. public ListViewAdapter(Context mContext) { super(); this.mContext = mContext; } @Override public int getCount() { return mListData.size(); }
@Override public Object getItem(int position) { return mListData.get(position); }
@Override public long getItemId(int position) { return position; }
public class BackPressCloseHandler { private long backKeyPressedTime = 0; private Toast toast;
private Activity activity;
public BackPressCloseHandler(Activity context) { this.activity = context; }
public void onBackPressed() { // 2초 이내에 뒤로 가기 버튼을 두번 누르면 종료처리 if (System.currentTimeMillis() > backKeyPressedTime + 2000) { backKeyPressedTime = System.currentTimeMillis(); showGuide(); return; } if (System.currentTimeMillis() <= backKeyPressedTime + 2000) { activity.finish(); toast.cancel(); } }
public void showGuide() { toast = Toast.makeText(activity,"\'뒤로\'버튼을 한번 더 누르시면 종료됩니다.", Toast.LENGTH_SHORT); toast.show(); } }
if( !isNetworkConnected(this) ){ new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle("네트워크 연결 오류").setMessage("네트워크 연결 상태 확인 후 다시 시도해 주십시요.") .setPositiveButton("확인", new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { finish(); } }).show(); }
backPressCloseHandler = new BackPressCloseHandler(this); }
XmlPullParser 는 문서를 순차적으로 읽으며 이벤트를 발생시킨다. START_DOCUMENT 이벤트는 문서의 시작을
END_DOCUMENT 은 문서의 끝을
START_TAG는 태그의 시작을 (예 : <uid> )
END_TAG는 태그의 끝을 (예 : </uid> )
TEXT는 태그의 시작과 끝 사이에서 나타난다. (예 : <uid>여기서 TEXT 이벤트 발생</uid> )
// 서버에 있는 XML 데이터 읽어온다. URL url = new URL(uri); InputStream inputStream = url.openStream(); parser.setInput(inputStream, "utf-8"); // XmlPullParser에 XML 데이터와 인코딩 방식을 입력
int eventType = parser.getEventType(); Person person = null;
// 파싱한 xml 이 END_DOCUMENT(종료 태그)가 나올때 까지 반복한다. while(eventType != XmlPullParser.END_DOCUMENT) { switch(eventType) { case XmlPullParser.START_TAG: String startTag = parser.getName(); if(startTag.equals("items")) { person = new Person(); } else if(startTag.equals("uid")){ person.setUid(parser.nextText()); } else if(startTag.equals("name")){ person.setName(parser.nextText()); } else if(startTag.equals("mobile")){ person.setMobile(parser.nextText()); } break; case XmlPullParser.END_TAG: // End 태그를 만나면 String endTag = parser.getName(); if(endTag.equals("items")) { arrayList.add(person); } break; }
// 서버에 있는 XML 데이터 읽어온다. URL url = new URL(uri); InputStream inputStream = url.openStream(); parser.setInput(inputStream, "utf-8"); // XmlPullParser에 XML 데이터와 인코딩 방식을 입력
int eventType = parser.getEventType(); Person person = null;
// 파싱한 xml 이 END_DOCUMENT(종료 태그)가 나올때 까지 반복한다. while(eventType != XmlPullParser.END_DOCUMENT) { switch(eventType) { case XmlPullParser.START_TAG: String startTag = parser.getName(); if(startTag.equals("items")) { person = new Person(); } else if(startTag.equals("uid")){ person.setUid(parser.nextText()); } else if(startTag.equals("name")){ person.setName(parser.nextText()); } else if(startTag.equals("mobile")){ person.setMobile(parser.nextText()); } break; case XmlPullParser.END_TAG: // End 태그를 만나면 String endTag = parser.getName(); if(endTag.equals("items")) { arrayList.add(person); } break; }
ButtonActivity.java 와 button.xml 파일을 생성해서 테스트를 해봤더니 Intent 로 화면이 전환되는 것을 확인했다.
개념이 부족한 상태에서 화면을 복사해서 대략 붙여넣기하면서 수정을 했더니 뭔가 꼬인 부분이 있는거 같다.
아직 Layout 만들고 화면에 불러서 처리하는 부분에 대한 지식이 부족해서인가 보다.
.setJavaScriptEnabled - 웹뷰에서 자바 스크립트 사용 .setSupportZoom - 손으로 확대, 축소를 할 수 있도록 사용 .setBuiltInZoomControls - WebView 내장 Zoom 사용 .onPageStarted() - 웹뷰에서 url이 로드 될때 호출 되는 함수 .onPageFinished() - 웹뷰에서 url 로딩이 완료되면 호출 되는 함수 WebViewClient의 shouldOverrideUrlLoading 함수는 웹뷰 내에서 웹 페이지를 돌아다니기 위해서 사용 .onJSAlert() - 웹에서 띄우는 팝업 창 같은 것을 웹뷰에서 보여주기 위해 사용
프로젝트를 닫은 걸 열어서 작업하는 경우, 인터넷에서 다운로드 받은 프로젝트, USB에 담긴 프로젝트 파일이라고 보고 아래 절차대로 따라하면 된다.
몇번의 시행착오를 통해서 알게된 사실도 추가로 적어둔다.
강좌 사이트 샘플소스나 블로그에 첨부된 샘플소스를 추가하고자 할 경우에는 반드시 workspace 가 아닌 다른 폴더에 복사를 해두고(이름을 다르게 지정해도 됨) 파일을 인식시킨 후 Copy projects into workspace 를 체크해서 workspace 폴더로 복사를 하면 원래 이름으로 생성이 된다.
ListView는 사용자가 정의한 데이터 목록을 아이템 단위로 구성하여 화면에 출력하는 ViewGroup의 한 종류다. ListView에 표시되는 아이템은 Image, Button, CheckBox 등 여러 View의 조합으로 구성되는 Custom 형태가 일반적이다.
ListView 는 일반 위젯(TextView)가 아니라 선택위젯이기 때문에, Adapter 에서 만들어주는 getView() 를 이용해서 아이템을 표시한다. Adapter가 하는 역할은 사용자 데이터를 입력받아 View를 생성하는 것이며, Adapter에서 생성되는 View는 ListView 내 하나의 아이템 영역에 표시되는 것이다.
ListView 는 Adapter 를 사용하여 데이터를 표시하는 View 로 아래 설명은 Activity에 ListView를 추가할 때 사용하는 방법이다.
Fragment 에 ListView 를 사용하기 위해서는 LayoutInflater를 사용하여 Resource Layout을 View로 변환한 다음, 해당 View를 사용하여 findViewById()를 호출하는 방법으로 해야 한다.
2. list_item.xml 생성
이제 ListView 의 한 아이템에 표시될 Layout 을 정의해야 한다.
main.xml 파일에서 마우스 우클릭을 하여 Copy(Ctrl + C)를 한 다음에
layout 폴더에서 마우스 우클릭하여 붙여넣기를 눌러서 이름을 list_item.xml 로 수정한다.
URL
객체가 생성되면, URL 클래스의 openStream() 메소드를 이용하여 해당 URL의 자원을 얻어오는 InputStream 을
리턴 받을 수 있으며, getConnection 메소드를 사용하여 URLConnection 객체도 얻어올 수 있다. 기본적으로 네트워크 작업을 기본 UI Thread에서 처리하는 것을 금지하고 별도의 Thread를 생성해서 처리하도록 하고 있다.
// 서버 정보를 파싱하기 위한 변수 선언 String myJSON; private static final String TAG_RESULTS="result"; private static final String TAG_UID = "uid"; private static final String TAG_NAME = "name"; private static final String TAG_Mobile ="mobile";
GetDataJSON g = new GetDataJSON(); g.execute(string);
}
protected void showList() { // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력 try { JSONObject jsonObj = new JSONObject(myJSON); peoples = jsonObj.getJSONArray(TAG_RESULTS);
for(int i=0;i<peoples.length();i++){ JSONObject c = peoples.getJSONObject(i); String uid = c.getString(TAG_UID); String name = c.getString(TAG_NAME); String mobile = c.getString(TAG_Mobile); Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
class ViewHolder { public LinearLayout child_layout; public ImageView mImage; public Button childListBtn; public TextView name; public TextView mobile; }
private class ListViewAdapter extends BaseAdapter {
@Override public void onClick(View v) { // 연락처 저장 함수 호출 AlertDialog.Builder SaveContact = new AlertDialog.Builder(MainActivity.this); SaveContact.setMessage("전화번호를 저장하시겠습니까?"); DialogInterface.OnClickListener mail = new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) { int rawContactId = 0; Contacts phonebook = new Contacts(); // 전화번호부 객체 생성 ContentResolver cr = getContentResolver(); String strContactName = mData.name; String strMobileNO = mData.mobile; String strofficeNO =""; String strEmail =""; String strPhoto ="";
rawContactId = phonebook.ContactsIDExistCheck(cr, strContactName); if(rawContactId > 0){ // 기존 전화번호가 존재하면 삭제하고 새로 입력 phonebook.ContactsIDdelete(cr, context, rawContactId); }
public void remove(int position){ mListData.remove(position); mAdapter.notifyDataSetChanged(); }
}
// Back 버튼을 누르면 어플 종료여부 확인 처리 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( keyCode == KeyEvent.KEYCODE_BACK ) { new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { moveTaskToBack(true); // 본 Activity finish후 다른 Activity가 뜨는 걸 방지. finish(); //application 프로세스를 강제 종료 android.os.Process.killProcess(android.os.Process.myPid() ); } }).setNegativeButton( "No", null ).show();
function isConnectDb($db) { $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']); mysqli_set_charset($conn, "utf8"); // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨 if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); exit; } else { return $conn; } } ?>
==== get_json.php =====
<?php @extract($_POST); // POST 전송으로 전달받은 값 처리 if(!(isset($idx) && !empty($idx))) { // 안드로이드에서 넘어온 변수 체크 echo 0; exit; }
require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php'; $sql = "select uid, name, mobile from Person ";
$R = array(); // 결과 담을 변수 생성 $result = mysqli_query($dbconn,$sql); while($row = mysqli_fetch_object($result)) { array_push($R, $row); // 이 부분을 풀어서 처리하는 코드는 http://link2me.tistory.com/1275 참조 } echo json_encode(array("result"=>$R)); ?>
실행결과 Web 화면
PHP 초보자는 아래 작성된 내용은 안봐도 된다. 이런 방법도 있다는 걸 알고자 한다면 읽어도 좋다.
$conn = new MySQLDbClass(); // DB 함수 $dbconn = $conn->isConnectDb($db);
$result = mysql_query('select uid,name,mobile from Person');
// 1. XML 데이터 생성방법 $c = new xmlcreateClass(); $c->XML2View($result); // 화면출력 ?>
class xmlcreateClass {
// XML 데이터를 모니터 화면으로 출력 // $c->XML2View($result); // XML 데이터 화면 출력 function XML2View($result) { header('Content-type: text/xml'); echo $this->exportAsXML($result); }
// MySQL DB 자료를 column 개수, 게시물 총개수 만큼 자동으로 XML로 만들기 function exportAsXML($result){ $xml ='<?xml version="1.0" encoding="UTF-8" ?>'."\n"; $xml.='<ListInfo>'."\n"; while($row = mysql_fetch_array($result, MYSQL_ASSOC)) { $xml.="<items>"."\n";
function isConnectDb($db) { $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']); mysqli_set_charset($conn, "utf8"); // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨 if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); exit; } else { return $conn; } }
// DB Query result 함수
function getDbresult($table,$where,$column) { global $db; $result = mysqli_query($db,'select '.$column.' from '.$table.($where?' where '.$this->getSqlFilter($where):'')); return $result; }
//SQL필터링 function getSqlFilter($sql) { return $sql; }
}//end dbClass ?>
더 많은 코드는 KIMSQ RB 함수를 참조하여 응용해도 되고 Class 화하여 사용해도 된다.