1. a013_menu 모듈
1) Main3Activity 액티비티, activity_main3 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a013_menu">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main3Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main3Activity">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActionBar"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<Button
android:id="@+id/btnActionIcon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="액션바 아이콘 바꾸기" />
<Button
android:id="@+id/btnActionTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="액션바 타이틀로" />
</LinearLayout>
[추가] menu_main3 메뉴 레이아웃 만들기
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_refresh"
android:title="새로고침"
android:icon="@drawable/menu_refresh"
app:showAsAction="always"
/>
<!--
<item
android:id="@+id/menu_search"
android:icon="@drawable/menu_search"
android:title="검색"
app:showAsAction="always"
/>
-->
<item
android:id="@+id/menu_setting"
android:title="설정"
android:icon="@drawable/menu_settings"
app:showAsAction="always"
/>
<item
android:id="@+id/menu_alarm"
android:title="알람"
android:icon="@drawable/ic_alarm_black_18dp"
app:showAsAction="always"
/>
<item
android:id="@+id/menu_search"
android:title="검색"
app:showAsAction="always"
android:orderInCategory="102"
app:actionLayout="@layout/search_layout"
/>
<!--
android:actionLayout 은 동작 안한다
app:actionLayout 으로 하기..!!
-->
</menu>
[추가] search_layout 레이아웃 만들기
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="검색"
android:textSize="16sp"
android:textColor="#EFE5DD" />
<EditText
android:id="@+id/editText"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:inputType="text" />
</LinearLayout>
[추가] 액션바에 많이 사용되는 아이콘은 구글 개발자 페이지에서 무료 제공(https://material.io/resources/icons)
: 원하는 그림 SVG로 다운 > res > drawable > New > Vector Asset 클릭
> Path의 폴더 그림 클릭 > 다운 받았던 SVG 파일 선택 > 아래의 그림 참고하여 원하는대로 설정 후 Next
> Finish 클릭
> 아래의 그림처럼 drawable 폴더에 다운됨
[추가] SVG로 다운 받은 아이콘은 아래의 그림처럼 색상, 크기를 xml 파일로 직접 설정 가능
** Main3Activity 액티비티
package com.lec.android.a013_menu;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
// ActionBar = 타이틀바 + 옵션메뉴
public class Main3Activity extends AppCompatActivity {
ActionBar abar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
abar = getSupportActionBar(); // ActionBar 객체 가져오기
//abar.hide(); // 액션바 감추기
//abar.show(); // 액션바 보이기
Button btnActionIcon = findViewById(R.id.btnActionIcon);
Button btnActionTitle = findViewById(R.id.btnActionTitle);
btnActionIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
abar.setLogo(R.drawable.home); // 아이콘 바꾸기
abar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO);
}
});
/*
setDisplayOptions 의 디스플레이 옵션 상수
DISPLAY_SHOW_HOME : 홈아이콘 표시
DISPLAY_USE_LOGO : 홈아이콘 부분에 로고 아이콘 사용
DISPLAY_HOME_AS_UP : 홈아이콘에 뒤로가기 모향의 < 아이콘 같이 표시
DISPLAY_SHOW_TITLE : 타이틀을 표시하도록 함
*/
btnActionTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
abar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
}
});
} // end onCreate()
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main3, menu);
// #2 : 액션바의 EditText 로 입력 가능.
View v = menu.findItem(R.id.menu_search).getActionView();
if(v != null){
EditText editText = v.findViewById(R.id.editText);
if(editText != null){
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Toast.makeText(getApplicationContext(), v.getText() + ":입력", Toast.LENGTH_LONG).show();
return false;
}
});
}
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
showInfo(item);
return super.onOptionsItemSelected(item);
}
public void showInfo(MenuItem item){
int id = item.getItemId(); // 옵션메뉴 아이템의 id 값
String title = item.getTitle().toString(); // 옵션 메뉴의 title
int groupId = item.getGroupId(); // 옵션 메뉴의 그룹아이디
int order = item.getOrder();
String msg = "id:" + id + " title:" + title + " groupid:" + groupId + " order:" + order;
Log.d("myapp", msg);
Toast.makeText(getApplicationContext(), title + " 메뉴 클릭", Toast.LENGTH_SHORT).show();
}
} // end Activity
[추가] 액션바 타이틀은 res > values > stings.xml에서 설정 가능
2) Main4Activity 액티비티, activity_main4 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a013_menu">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".Main4Activity"
android:label="Main">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main3Activity" />
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
<!--
UP Navigation 작동을 위한 액티비티 설정
4.1 이상: <activity> 에 android:parentActivityName 를 적용
4.0 이하: meta-data 를 통해 설정.
-->
<activity
android:name=".ChildActivity1"
android:label="Main > Child1"
android:parentActivityName=".Main4Activity"></activity>
<activity
android:name=".ChildActivity2"
android:label="Main > Child1 > Child2"
android:parentActivityName=".ChildActivity1"></activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main4Activity">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Up Navigaton"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<Button
android:id="@+id/btnChild1"
android:layout_width="match_parent"
android:layout_height="73dp"
android:text="Child 1" />
</LinearLayout>
package com.lec.android.a013_menu;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
/*
액션바 (Action Bar)
사용자에게 친숙한 인터페이스 제공
안드로이드 3.0 에서 추가, (3.0 이전버젼은 Support Library 로 가능)
[기능]
- 앱 로고 / 앱에서 사용자의 현재 위치
- 탭이나 드롭다운 메뉴 제공
- 사용자 예측 가능한 방식으로 가능한 작업 제공(ex: 검색, 만들기, 공유..)
[구성]
- 앱아이콘 : 로고 혹은 다른 이미지
- 뷰컨트롤 (View Control) : 뷰 전환 가능 영역
- 액션버튼 (Action Button)
- 액션오버플로우 (Action Overflow) : 액션바 공간이 부족하면 자동적으로 오버플로우 영역으로 이동.
※ 참고
액션바에 많이 사용되는 아이콘은 구글 개발자 페이지에서 무료 제공
https://material.io/resources/icons
*/
public class Main4Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
Button btnChild1 = findViewById(R.id.btnChild1);
btnChild1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), ChildActivity1.class);
startActivity(intent);
}
});
}
}
[추가] ChildActivity1 액티비티, activity_child1 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ChildActivity1">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Up Navigaton"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<Button
android:id="@+id/btnChild2"
android:layout_width="match_parent"
android:layout_height="70dp"
android:text="CHILD 2" />
</LinearLayout>
package com.lec.android.a013_menu;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class ChildActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child1);
Button btnChild2 = findViewById(R.id.btnChild2);
btnChild2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), ChildActivity2.class);
startActivity(intent);
}
});
}
}
[추가] ChildActivity2 액티비티, activity_child2 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ChildActivity2">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Up Navigaton"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
</LinearLayout>
package com.lec.android.a013_menu;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class ChildActivity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child2);
}
}
[추가] 아래 사진에 동그랗게 표시한 부분을 up caret이라고 부름
3) Main5Activity 액티비티, activity_main5 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a013_menu">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main5Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main4Activity"
android:label="Main" />
<activity android:name=".Main3Activity" />
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
<!--
UP Navigation 작동을 위한 액티비티 설정
4.1 이상: <activity> 에 android:parentActivityName 를 적용
4.0 이하: meta-data 를 통해 설정.
-->
<activity
android:name=".ChildActivity1"
android:label="Main > Child1"
android:parentActivityName=".Main4Activity"></activity>
<activity
android:name=".ChildActivity2"
android:label="Main > Child1 > Child2"
android:parentActivityName=".ChildActivity1"></activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main5Activity">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sub / PopUp Menu"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<Button
android:id="@+id/btnPopup"
android:layout_width="match_parent"
android:layout_height="88dp"
android:text="클릭하면 팝업메뉴가!"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
package com.lec.android.a013_menu;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.Toast;
// SubMenu & PopUp Menu
public class Main5Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
Button btnPopup = findViewById(R.id.btnPopup);
btnPopup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 팝업 메뉴 나오게 하기
// PopupMenu 는 API 11부터 제공
PopupMenu p = new PopupMenu(getApplicationContext(),
v); // anchor : 팝업을 띄울 기준이 될 뷰
getMenuInflater().inflate(R.menu.menu_main5, p.getMenu());
// 팝업메뉴 이벤트 처리
p.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
showInfo(item);
return false;
}
});
p.show(); // 팝업 메뉴 띄우기
}
});
} // end onCreate()
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main5, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
showInfo(item);
return super.onOptionsItemSelected(item);
}
public void showInfo(MenuItem item){
int id = item.getItemId(); // 옵션메뉴 아이템의 id 값
String title = item.getTitle().toString(); // 옵션 메뉴의 title
int groupId = item.getGroupId(); // 옵션 메뉴의 그룹아이디
int order = item.getOrder();
String msg = "id:" + id + " title:" + title + " groupid:" + groupId + " order:" + order;
Log.d("myapp", msg);
Toast.makeText(getApplicationContext(), title + " 메뉴 클릭", Toast.LENGTH_SHORT).show();
}
} // end Activity
[추가] menu_main5 메뉴 레이아웃 만들기
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- sub Menu 만들기 -->
<item android:id="@+id/w"
android:title="submenu 1">
<menu>
<item android:title="apple" />
<item android:title="banana" />
<item android:title="lemon" />
</menu>
</item>
<item android:id="@+id/d"
android:title="submenu 2">
<menu>
<item android:title="사과" />
<item android:title="배" />
<item android:title="수박" />
</menu>
</item>
<item android:id="@+id/c"
android:title="submenu 3">
</item>
</menu>
2. a015_web 모듈
1) MainActivity 액티비티, activity_main 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a015_web">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="HttpURLConnection"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<EditText
android:id="@+id/etUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="사이트 주소 입력"
android:inputType="textPersonName"
android:text="https://m.naver.com" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnWebView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="요청하기" />
<Button
android:id="@+id/btnBrowser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="지우기" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
package com.lec.android.a015_web;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/* HTTP 요청하기
- 메니페스트 설정 하기 : android.permission.INTERNET 권한
- <application> 에 추가 usesCleartextTraffic="true"
HTTP와 같은 cleartext 네트워크 트래픽을 사용할지 여부를 나타내는 flag로
이 플래그가 flase 로 되어 있으면, 플랫폼 구성 요소 (예 : HTTP 및 FTP 스택, DownloadManager, MediaPlayer)는
일반 텍스트 트래픽 사용에 대한 앱의 요청을 거부하게 됩니다. 이 flag를 설정하게 되면 모든 cleartext 트래픽은 허용처리가 됩니다
- URL 객체 만들기 -> HttpURLConnection 객체 만들기
setXXX() 메소르도 Conneciton 세팅
ex) setRequestMethod(method) : "GET" "POST " 등의 문자열
ex) setRequestProperty(field, value) :
- request 는 별도의 Thread 로 진행!
- 위 Thread에서 화면 UI 접근하려면 (당연히) Handler 사용
*/
// 즉, 인터넷 통신은 반드시 별도의 Thread 로 통신해야함...!!
public class MainActivity extends AppCompatActivity {
EditText etUrl;
TextView tvResult;
Button btnRequest, btnClear;
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etUrl = findViewById(R.id.etUrl);
tvResult = findViewById(R.id.tvResult);
btnRequest = findViewById(R.id.btnWebView);
btnClear = findViewById(R.id.btnBrowser);
btnRequest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String urlStr = etUrl.getText().toString();
// HTTp request 는 별도의 Thread 로 진행되어야 한다.!
new Thread(new Runnable() {
@Override
public void run() {
request(urlStr);
}
}).start();
}
});
btnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvResult.setText(""); // 내용 지우기
}
});
} // end onCreate()
public void request(String urlStr) {
final StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
HttpURLConnection conn = null;
try {
URL url = new URL(urlStr);
conn = (HttpURLConnection)url.openConnection();
if(conn != null) {
conn.setConnectTimeout(5000); // timeout 시간 설정
// 경과하면 SocketTimeoutException 발생
conn.setUseCaches(false); // 캐시 사용 안함
conn.setRequestMethod("GET"); // GET 방식 request
conn.setDoInput(true); // URLConnection 을 입력으로 사용 -> (true), (false) -> 출력용
int responseCode = conn.getResponseCode(); // response code 값, 성공하면 200
if(responseCode == HttpURLConnection.HTTP_OK) { // 200 HTTP_OK
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while(true) {
line = reader.readLine();
if(line == null) {break;}
sb.append(line + "\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null) {reader.close();}
if(conn != null) {conn.disconnect();}
} catch (IOException e) {
e.printStackTrace();
}
}
handler.post(new Runnable() {
@Override
public void run() {
tvResult.setText("응답 -> " + sb.toString());
}
});
} // end request()
} // end Activity
2) Main2Activity 액티비티, activity_main2 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a015_web">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="WebView"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<EditText
android:id="@+id/etUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="사이트 주소 입력"
android:inputType="textPersonName"
android:text="https://m.naver.com" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnWebView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="WebView" />
<Button
android:id="@+id/btnBrowser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="BROWSER" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/wv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
</ScrollView>
</LinearLayout>
package com.lec.android.a015_web;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
// #1 WebView 사용하여 웹 페이지 보여주기
// #2 묵시적 Intent 사용하여 웹 브라우져 띄우기
public class Main2Activity extends AppCompatActivity {
WebView wv;
EditText etUrl;
Button btnWebView, btnBrowser;
String url;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
etUrl = findViewById(R.id.etUrl);
wv = findViewById(R.id.wv);
btnWebView = findViewById(R.id.btnWebView);
btnBrowser = findViewById(R.id.btnBrowser);
// WebView 세팅
wv.getSettings().setJavaScriptEnabled(true); // JavaScript 사용 여부 : 디폴트 false
btnWebView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
url = etUrl.getText().toString().trim();
wv.loadUrl(url); // 웹페이지 url 로 읽어오기
wv.setWebChromeClient(new WebChromeClient()); // 안하면 alert() 같은 알림창 안뜸
wv.setWebViewClient(new WebViewClient()); // 안하면 html 내부에서 다른 페이지로 이동할 수 없음
}
});
// 브라우저 가동, 묵시적 Intent 사용
btnBrowser.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
url = etUrl.getText().toString().trim();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
});
} // end onCreate()
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && wv.canGoBack()) {
wv.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
} // end onKeyDown()
} // and Activity
3) Main3Activity 액티비티, activity_main3 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a015_web">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".Main3Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="XML,JSON"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:hint="역명을 입력해주세요"
android:text="서울" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnXML"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="xml보기" />
<Button
android:id="@+id/btnJSON"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="json보기" />
<Button
android:id="@+id/btnParse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="파싱" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="결과값" />
</ScrollView>
</LinearLayout>
package com.lec.android.a015_web;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/* Lec32, 11 패키지
* XML, JSON 파싱 연습
* ■서울시 지하철 역사 정보
* http://data.seoul.go.kr/dataList/datasetView.do?infId=OA-12753&srvType=A&serviceKind=1¤tPageNo=1
*
* 샘플url
*
* XML 버젼
* http://swopenAPI.seoul.go.kr/api/subway/704c4a6d7262696e33396a544e7551/xml/stationInfo/1/5/서울
*
* JSON 버젼
* http://swopenAPI.seoul.go.kr/api/subway/704c4a6d7262696e33396a544e7551/json/stationInfo/1/5/서울
*
*/
public class Main3Activity extends AppCompatActivity {
private TextView tvResult;
private EditText et;
private Button btnXML;
private Button btnJSON;
private Button btnParse;
// 웹사이트 주소를 저장할 변수
String reqType = "xml";
String reqService = "stationInfo";
int reqStartIndex = 0;
int reqEndIndex = 5;
String reqSearchStr = "";
String api_key = "";
String urlAddress;
Handler handler = new Handler(); // 화면에 그려주기 위한 객체
HttpURLConnection conn;
StringBuffer sb;
// XML 파싱
DocumentBuilderFactory dbFactory;
DocumentBuilder dBuilder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
// 웹에서 html 읽어오기
// 1. 인터넷 권한 얻어오기 AndroidManifest.xml
// 2. 쓰레드를 작성
// 3. Handler 객체를 통해야만 화면을 그릴수 있다
tvResult = findViewById(R.id.tvResult);
btnXML = findViewById(R.id.btnXML);
btnJSON = findViewById(R.id.btnJSON);
btnParse = findViewById(R.id.btnParse);
et = findViewById(R.id.editText);
api_key = getResources().getString(R.string.api_key);
try {
// DOM parser 객체 생성
dbFactory = DocumentBuilderFactory.newInstance();
dBuilder = dbFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
btnXML.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
reqType = "xml";
// EditText 에 입력된 역 이름의 공백제거.
reqSearchStr = et.getText().toString().trim().replaceAll(" ", "");
// url 에 한글이 들어가는 경우 URLEncode 를 해야 한다.
try {
reqSearchStr = URLEncoder.encode(reqSearchStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if( reqSearchStr.length() > 0) {
urlAddress = "http://swopenAPI.seoul.go.kr/api/subway/" +
api_key + "/" + reqType + "/" + reqService + "/" +
reqStartIndex + "/" + reqEndIndex + "/" + reqSearchStr;
sendRequest(); // 웹에서 html 읽어오기
} // end if
} // end onClick()
});
btnJSON.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
reqType = "json";
// EditText 에 입력된 역 이름의 공백제거.
reqSearchStr = et.getText().toString().trim().replaceAll(" ", "");
// url 에 한글이 들어가는 경우 URLEncode 를 해야 한다.
try {
reqSearchStr = URLEncoder.encode(reqSearchStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if( reqSearchStr.length() > 0) {
urlAddress = "http://swopenAPI.seoul.go.kr/api/subway/" +
api_key + "/" + reqType + "/" + reqService + "/" +
reqStartIndex + "/" + reqEndIndex + "/" + reqSearchStr;
sendRequest(); // 웹에서 html 읽어오기
} // end if
}
});
btnParse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvResult.setText("");
switch(reqType){
case "xml":
parseXML();
break;
case "json":
parseJSON();
break;
}
}
});
} // end onCreate()
void sendRequest() { // 웹에서 html 읽어오기
tvResult.setText("");
Thread t = new Thread(new Runnable() {
@Override
public void run() {
sb = new StringBuffer();
try {
URL url = new URL(urlAddress);
conn = (HttpURLConnection)url.openConnection();// 접속
if (conn != null) {
conn.setConnectTimeout(2000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode()
== HttpURLConnection.HTTP_OK){ // 200
// 데이터 읽기
BufferedReader br
= new BufferedReader(new InputStreamReader
(conn.getInputStream(),"utf-8"));//"utf-8"
while(true) {
String line = br.readLine();
if (line == null) break;
sb.append(line+"\n");
}
br.close(); // 스트림 해제
}else{
Log.d("text", "getResponseCode(): " + conn.getResponseCode());
}
conn.disconnect(); // 연결 끊기
}else{
Log.d("test", "conn NULL!");
}
// 값을 출력하기
Log.d("test", sb.toString());
handler.post(new Runnable() {
@Override
public void run() {
tvResult.setText(sb.toString());
} // end run()
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start(); // 쓰레드 시작
} // end sendRequest()
void parseXML()
{
InputSource is = new InputSource(new StringReader(sb.toString()));
// DOM parser 객체 생성
Document doc; // 곧바로 InputStream 으로부터 받아 파싱
try {
doc = dBuilder.parse(is);
Element element = doc.getDocumentElement();
element.normalize();
NodeList nList = doc.getElementsByTagName("row"); // 서울시 지하철은 <row>~</row> 로 구성됨
Log.d("myapp", "nList.getLength() = " + nList.getLength());
for(int i = 0; i < nList.getLength(); i++){
Node node = nList.item(i);
Element rowElement = (Element)node; // 원래는 node.getNodeType() == Node.ELEMENT_NODE 체크해봐야 한다
final String subwayId =
rowElement.getElementsByTagName("subwayId").item(0).getChildNodes().item(0).getNodeValue();
final String subwayNm =
rowElement.getElementsByTagName("subwayNm").item(0).getChildNodes().item(0).getNodeValue();
handler.post(new Runnable() {
@Override
public void run() {
tvResult.setText(tvResult.getText().toString() +
"--------------\nId:" + subwayId + "\n호선:" + subwayNm + "\n");
}
}); // end post
} // end for
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
} // end parseXML()
void parseJSON(){
try {
JSONObject obj = new JSONObject(sb.toString());
JSONArray stationArr = obj.getJSONArray("stationList");
for(int i = 0; i < stationArr.length(); i++){
JSONObject station = (JSONObject)stationArr.get(i);
final String subwayId = station.getString("subwayId");
final String subwayNm = station.getString("subwayNm");
handler.post(new Runnable() {
@Override
public void run() {
tvResult.setText(tvResult.getText().toString() +
"--------------\nId:" + subwayId + "\n호선:" + subwayNm + "\n");
}
}); // end post
}
} catch (JSONException e) {
e.printStackTrace();
}
} // end parseJSON()
} // end Activity
[추가] res > values > New > Values Resource File 클릭
> New Resource File 창 뜸 > 파일 이름 작성 후 OK 클릭
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="api_key">704c4a6d7262696e33396a544e7551</string>
</resources>
3. a016_sensor 모듈
1) MainActivity 액티비티, activity_main 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a016_sensor">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SensorList"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="센서 목록 결과창"
android:textAppearance="?android:attr/textAppearanceLarge" />
</ScrollView>
</LinearLayout>
package com.lec.android.a016_sensor;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.textView1);
// 센서 장치 목록 확인하기
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
List<Sensor> list = sm.getSensorList(Sensor.TYPE_ALL);
String str = "<센서목록>\n센서 총개수 : " + list.size();
for (int i = 0; i < list.size(); i++) {
Sensor s = list.get(i);
str += "\n\n" + i + ", 센서이름: " + s.getName()
+ "\n" + "센서전원: " + s.getPower() // 센서가 소모하는 전력량(mA)
+ "\n" + "resolution: " + s.getResolution() // 센서값의 resolution (센서별 단위 사용)
+ "\n" + "range: " + s.getMaximumRange(); // 센서값의 최대범위 (센서별 단위 적용)
}
tv.setText(str);
Log.d("myapp", str);
} // end onCreate()
} // end Activity
2) Main2Activity 액티비티, activity_main2 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a016_sensor">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
</application>
</manifest>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="방향센서 결과창"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
package com.lec.android.a016_sensor;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class Main2Activity extends AppCompatActivity implements SensorEventListener {
private TextView tv;
private SensorManager sm;
// TYPE_ORIENTATION <- 현재 deprecated 됨
Sensor accelerometer;
Sensor magnetometer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv = findViewById(R.id.textView1);
sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
// 위의 코드의 옛날 방식
//Sensor s = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
accelerometer = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magnetometer = sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
} // end onCreate()
// 화면 동작하기 직전에 센서 자원 획득
@Override
protected void onResume() {
super.onResume();
// 센서 값이 변경되었을때마다 콜백 받기 위한 리스너 등록
// SensorEventListener 객체
sm.registerListener((SensorEventListener)this,
accelerometer, // 콜백 원하는 센서
SensorManager.SENSOR_DELAY_UI // 지연시간, 2ms
);
sm.registerListener((SensorEventListener)this,
magnetometer,
SensorManager.SENSOR_DELAY_UI);
} // end onResume()
// 화면 빠져나가기 전에 센서 자원 반납!
@Override
protected void onPause() {
super.onPause();
// 센서에 등록된 리스너 해제
sm.unregisterListener(this); // 반납할 센서
} // end onPause()
float[] mGravity;
float[] mGeomagnetic;
// SensorEventListener 객체의 메소드들
// 센서 값이 변경될때마다 호출되는 콜백
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values; // 센서값들을 float[] 로 넘어옴
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
mGeomagnetic = event.values;
}
if (mGravity != null && mGeomagnetic != null) {
float[] R = new float[9];
float[] I = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success) {
float[] orientation = new float[3];
SensorManager.getOrientation(R, orientation);
float azimuth = orientation[0]; // z 축 회전방향
float pitch = orientation[1]; // x 축 회전방향
float roll = orientation[2]; // y 축 회전방향향
String str = String.format("%10s:%10s:%10s\n%10.2f:%10.2f:%10.2f\n",
"방위각", "피치", "롤",
azimuth, pitch, roll);
tv.setText(str);
Log.d("myapp", str);
}
}
} // end onSensorChanged()
// 센서의 정확도가 변경되었을때 호출되는 콜백
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
tv.setText("onAccuracyChanged");
Log.d("myapp", "onAccuracyChanged");
} // end onAccuracyChanged()
} // end Activity
4. a017_Location 모듈
1) MainActivity 액티비티, activity_main 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a017_location">
<!-- 위치 정보 권한 획득 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="LocationManager"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<TextView
android:id="@+id/tvSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="가용한 Location 제공자 목록"
android:textAppearance="?android:attr/textAppearanceLarge" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/tvResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="결과창"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
package com.lec.android.a017_location;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.TextView;
import java.util.List;
/** 위치기반 앱 : 사용자의 위치를 활용한 어플리케이션
*
* LocationManager (위치 관리자): 안드로이드에서 위치제공자(LocationProvider)를 얻어오기 위한 객체
*
* 안드로이드에서 사용가능한 위치제공자(LocationProvider)들 (즉, 사용자 위치 알아내는 방법들)
*
* 1. GPS : 위성에서 정보를 받아 삼각측량으로 위치를 계산,
* 정확하다(accuracy), 실내에서는 잘 안된다. 실외에서도 고층건물등 장애물에 따라 오차발생
* 배터리 소모 크다,
* 위치 정보 판독에 시간 걸린다. 특히 초기위치 결정시간 (TTFF : Time To First Fix) 가 많이 걸림
*
* 2. Network : 전화 기지국 이용 (셀룰러)
* WiFi 네트워크, Cell ID 위치 사용
* GPS 에 비해 부정확, TTFF 는 매우 빠름, 실내에서도 사용 가능
* GPS 에 비해 배터리 소모 적다
*
* 3. Passive : WiFi AP 사용
* 실내 위치 추적용, AP위치, AP와의 수신신호강도등을 계산하여 위치 계산
* 만약 다른 앱이 이미 위치서비스를 사용하고 있다면 그 위치 정보를 받아올수 있다.
* 추가적인 배터리 소모가 가장 적다.
*
*/
public class MainActivity extends AppCompatActivity {
TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvResult = findViewById(R.id.tvResult);
// 위치관리자 객체 얻어오기
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> list = lm.getAllProviders(); // 모든 가용한 위치 제공자 가져오기
String str = "[위치제공자] : 사용가능여부\n---------------------------\n";
for (String provider : list) {
str += "[" + provider + "] : " + lm.isProviderEnabled(provider) + "\n";
}
tvResult.setText(str);
} // end onCreate()
} // end Activity
2) Main2Activity 액티비티, activity_main2 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a017_location">
<!-- 위치 정보 권한 획득 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="GPS"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<TextView
android:id="@+id/tvResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="내 위치 결과"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ToggleButton
android:id="@+id/btnLocate"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginTop="5dp"
android:textOff="위치 미수신중"
android:textOn="위치 수신중"/>
</LinearLayout>
package com.lec.android.a017_location;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
/** 현재 위치 정보 가져오기 : GPS (Global Positioning System)
* 1. AndroidManifest.xml 위치정보 권한획득 선언 (API23 이전 버젼은 이걸로 충분)
* android.permission.ACCESS_FINE_LOCATION
* android.permission.ACCESS_COARSE_LOCATION
*
* 1-2 API23+ 에서는 (마시멜로): 새로운 권한 획득 방법 사용.
* Run-time 에 onRequestPermissionsResult() 사용 해야 한다!!!
* https://developer.android.com/training/permissions/requesting.html
* 2. LocationManager 획득
* 3. LocationProvider 지정
* 이전에 사용한 Provider 있는지 여부, 없으면 새로
*
* ※ GPS 는 에뮬레이터에서는 기본적으로 동작하지 않는다
* ※ 실내에서는 GPS_PROVIDER 를 요청해도 응답이 없거나 느리다. 특별한 처리를 안하면 아무리 시간이 지나도 응답이 없으면
* 해결방법은
* ① 타이머를 설정하여 GPS_PROVIDER 에서
* 일정시간 응답이 없는 경우 NETWORK_PROVIDER로 전환
* ② 혹은, 둘다 한꺼번헤 호출하여 우선 들어오는 값을 사용하는 방식.
*
* ※ 폰에 GPS 를 꺼놔도, LocationManager 가 최초 위치 정보 확인할때 GPS 켜지면서 초기화됨.
*
* 4. LocationListener 생성 : onLocationChanged() 에서 위치정보 (Location) 확인
*
*
* 안드로이드 좌표계: 경도(longitude), 위도(latitude) 사용
* 대한민국은 위도 36~38도, 경도 126~128도 사이 위치
* ex) 서울시청 : 37.566767, 126.978370 <-- 구글맵 등에서 위치 좌표 제공
* */
public class Main2Activity extends AppCompatActivity {
TextView tvResult;
ToggleButton btnLocate; // 토글 버튼!
String [] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
final int REQUEST_CODE = 101;
LocationManager manager = null;
MyListener mLocationListener = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tvResult = findViewById(R.id.tvResult);
btnLocate = findViewById(R.id.btnLocate);
// 위험권한 획득
if(Build.VERSION.SDK_INT >= 23){
if(checkSelfPermission(String.valueOf(permissions))
== PackageManager.PERMISSION_DENIED){
requestPermissions(permissions, REQUEST_CODE);
}
}
// LocationManager
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// LocationListener
mLocationListener = new MyListener();
btnLocate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(btnLocate.isChecked()){ // 토글버튼
// 위치정보 수신 시작
try{
// 주어진 위치제공자가 기억하는 가장 최근의 위치정보 획득, 없으면 null 리턴
Location location = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(location != null){
tvResult.setText("최근 위치\n" + locationInfo(location));
}
manager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, // 등록할 위치제공자
100, // 통지사이의 최소 시간간격
1, // 통지사이의 최소 변경거리 (m)
mLocationListener); // LocationListener
manager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, // 등록할 위치제공자
100, // 통지사이의 최소 시간간격
1, // 통지사이의 최소 변경거리 (m)
mLocationListener); // LocationListener
Toast.makeText(getApplicationContext(), "내 위치 확인 요청함", Toast.LENGTH_SHORT).show();
} catch(SecurityException e){
e.printStackTrace();
}
} else {
// 위치정보 수신 종료
manager.removeUpdates(mLocationListener);
}
}
});
} // end onCreate()
// LocationListener 구현
// LocationManager 로부터 전달되는 위치정보를 받기위해 정의
class MyListener implements LocationListener {
// 위치가 변경(확인) 되었을때 호출
// 위치값은 Location 형태로 전달
@Override
public void onLocationChanged(Location location) {
Log.d("myapp", "onLocationChanged, location: " + location);
tvResult.setText("내위치\n" + locationInfo(location));
Toast.makeText(getApplicationContext(), "위치정보갱신!", Toast.LENGTH_SHORT).show();
}
@Override // 위치제공자 가용하게 될때
public void onProviderEnabled(String provider) {
Log.d("myapp", "onProvideEnabled, provider: " + provider);
}
@Override // 위치제공자 가용하지 못하게 될때.
public void onProviderDisabled(String provider) {
Log.d("myapp", "onProviderDisabled, provider: " + provider);
}
// API 29부터 deprecated 됨
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
} // end MyListener
protected String locationInfo(Location location){
double longitude = location.getLongitude(); // 경도
double latitude = location.getLatitude(); // 위도
double altitude = location.getAltitude(); // 고도
float accuracy = location.getAccuracy(); // 정확도
String provider = location.getProvider(); // 위치 제공자
return "위치정보 : " + provider + "\n위도 : " + longitude + "\n경도 : " + latitude
+ "\n고도 : " + altitude + "\n정확도 : " + accuracy;
} // end locationInfo()
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "위치권한 획득!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "권한획득 실패!", Toast.LENGTH_LONG).show();
}
}
} // end onRequestPermissionsResult()
} // end Activity
'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
2020.04.27 (0) | 2020.04.27 |
---|---|
2020.04.24 (0) | 2020.04.24 |
2020.04.22 (0) | 2020.04.22 |
2020.04.21 (0) | 2020.04.21 |
2020.04.20 (0) | 2020.04.20 |