웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.04.23

shine94 2020. 4. 23. 08:47

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&currentPageNo=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