본문 바로가기

수업 정리

81일차 수업정리(Android 연동)

MySQL Server : Spring

AndroidPortFolio : Android -> 192.168.0.200:8080

 

**이전에 만들었던 AndroidPortfolio(AndroidMySQL) 수정

    => TextView에 출력했던 데이터를 ListView로 출력 : itemname만 출력

    => ListView의 항목을 클릭하면 itemid를 Toast로 출력

    => ListView를 하단으로 스크롤시 데이터를 가져와서 업데이트

    => 애니메이션 적용

1. ITEM 테이블의 데이터를 표현하기 위한 DTO 클래스 생성

    => 클라이언트에서 DTO를 만들때는 속성을 public으로 설정하여 편리하게 사용할 수 있도록 해도 됨

public class Item {
    public int itemid;
    public String itemname;
    public int price;
    public String description;
    public String pictureurl;

    @Override
    public String toString() {
        return "Item{" +
                "itemid=" + itemid +
                ", itemname='" + itemname + '\'' +
                ", price=" + price +
                ", description='" + description + '\'' +
                ", pictureurl='" + pictureurl + '\'' +
                '}';
    }
}

 

2. Activity_main.xml 파일의 디자인 수정

    => ScrollView대신 Listview로 변경

<?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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Spinner
            android:id="@+id/searchtype"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2" />
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:id="@+id/value"
            android:hint="검색어 입력"/>
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="검색"
            android:id="@+id/btnsearch"/>
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="다음"
            android:id="@+id/btnnext"/>

    </LinearLayout>
    <!-- 변경된 내용 -->
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listvuew"/>

</LinearLayout>

 

3. MainActivity.java 파일에서 인스턴스 변수를 수정

    => Textview대신 ListView, Data, Adapter

    private ListView listView;
    private List<Item> list;
    private ArrayAdapter<Item> itemAdapter;

 

4. handler 코드 수정

    => TextView에 데이터를 출력하던 것을 ListView의 데이터를 업데이트 하는 코드로 수정

    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message message){
            //list.setText(result);
            
            //adapter를 이용해서 ListView에 데이터가 수정되면 재출력후 신호를 보냄
            //신호를 보내는 것을 프로그래밍에서 Notification이라고 함
            itemAdapter.notifyDataSetChanged();
        }
    };

 

5. ThreadEx 클래스의 json파싱 부분을 수정

try{
	//객체로 변환
	JSONObject object = new JSONObject((sb.toString()));
	//데이터의 갯수는 count에 숫자로 저장
	cnt = object.getInt("count");
	//list 키의 데이터를 배열로 가져오기
	JSONArray ar = object.getJSONArray("list");
	for(int i=0; i<ar.length(); i+=1){
		NArray temp = ar.getJSONArray(i);
		//result = result + temp.getString(1) + "\n;
		Item item = new Item();
		item.itemid = temp.getInt(0);
		item.itemname = temp.getString(1);
		item.price = temp.getInt(2);
		item.description = temp.getString(3);
		item.pictureurl = temp.getString(4);
	}
	}catch (Exception e){
		Log.e("파싱 예외", e.getMessage());
	}

 

6. MainActivity.java 파일의 onCreate에서 ListView 생성하는 작업을 수행

//list = (TextView)findViewById(R.id.list);
listView = (ListView)findViewById(R.id.listview);
list = new Stack<>();
itemAdapter = new ArrayAdapter<>(
         this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(itemAdapter);

 

7. ListView 항목을 선택했을 때 itemid를 출력해보고 옵션을 수정

    => onCreate 메소드의 setAdapter 메소드 하단에 작성

   //옵션 설정
        listView.setDivider(new ColorDrawable(Color.BLUE));
        listView.setDividerHeight(3);
        //항목을 선택했을 때 호출되는 이벤트 핸들러 작성
        listView.setOnItemClickListener(
                new ListView.OnItemClickListener(){
                    @Override
                    //adapterView는 이벤트가 발생한 뷰
                    //view는 선택한 항목 뷰
                    //i가 선택한 항목의 인덱스
                    //l은 선택한 항목 뷰의 id
                    public void onItemClick(AdapterView<?> adapterView,
                            View view, int i, long l) {
                        //선택한 항목의 데이터
                        Item item = list.get(i);
                        //토스트로 itemid를 출력
                        Toast.makeText(MainActivity.this,
                             item.itemid + "", Toast.LENGTH_LONG).show();
                    }
                });

 

**트윈 애니메이션

    => 출발점부터 종료점까지 일정한 간격으로 적용하는 애니메이션

    => 일반 뷰에 적용하는 애니메이션과 뷰 그룹에 적용하는 애니메이션으로 나뉘고, 일반 뷰에 적용되는 애니메이션은 뷰에 한꺼번에 적용되지만 뷰그룹에 적용하면 속한 뷰 각각에 적용됨

    => 뷰 그룹에 적용시는 AnicmationSet을 이용해서 애니메이션을 만들고 애니메이션을 가지고 AnimationController를 생성한 후, AdapterView나 Layout에 적용하면 됨

    => 안드로이드는 애니메이션을 잘 적용하지 않고, iOS는 애니메이션 사용을 권장

 

8. MainActivity.java 파일의 onCreate 메소드에 listView 설정 하단에 코드를 추가

		Animation rtl = new TranslateAnimation(
               Animation.RELATIVE_TO_SELF, 1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF,0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);
        rtl.setDuration(1000);
        set.addAnimation(rtl);

        Animation alpha = new AlphaAnimation(0.0f, 1.0f);
        alpha.setDuration(1000);
        set.addAnimation(alpha);

        //각각의 애니메이션을 설정하고 대기 시간을 추가해서 생성
        LayoutAnimationController controller =
                new LayoutAnimationController(set, 1.0f);
        //listView에 애니메이션을 설정
        listView.setLayoutAnimation(controller);

 

**Adapter View 의 업데이트

=>화면에 데이터를 재출력할 때는 연결된 Adapter의 setNotifyDataChanged()를 호출해주면 됩니다.

=>이벤트 설정을 할 때 스크롤을 가장 하단에서 하면 업데이트하는 경우가 있고 상단에서 일정 시간동안 아래쪽으로 끌어내리면 업데이트하는 경우도 있습니다.

보통은 하단에서 하는 경우에는 이전 데이터를 가져와서 추가하는 경우이고 상단에서 하는 경우는 최신의 데이터를 가져오는 경우입니다.

=>하단에서 하는 경우는 Scroll Event에서 가장 하단에서 Scroll 한 것인지 감지해서 작업을 하고 상단에서 하는 경우는 별도의 라이브러리를 제공합니다.

이 라이브러리를 pull to request 라고 하며 Refresh View를 제공합니다.

이러한 Refresh View Adapter View에서만 사용하도록 합니다.

 

=>ScrollEvent는 OnScrollListener가 처리하고 메소드는 onScrollStateChanged 와 onScroll 입니다.

onScroll은 스크롤을 하고 있는 도중에 호출되고 onScrollStateChanged는 스크롤이 끝나면 호출됩니다.

 

하단에서 스크롤 한 것인지 확인할 변수를 생성

onScroll에서 가장 하단에서 스크롤 하는 것인지 감시하다가 하단에서 스크롤하면 변수의 값을 변경

 

onScrollStateChanged 메소드에서 변수를 확인해서 실제 업데이트를 수행을 합니다.

 

9. MainActivity.java 파일에 추가

  1) 인트턴스 변수  선언

    //스크롤이 가장 아래에서 수행되었는지 확인할 변수

    boolean lastItemVisibleFlag = false;

 

2)onCreate 메소드 하단에 스크롤 이벤트 핸들러 작성

//listView의 Scroll 이벤트 처리
        listView.setOnScrollListener(
                new ListView.OnScrollListener(){
            //Scroll이 끝나면 호출되는 메소드
            @Override
            //첫번째 매개변수는 스크롤이 발생한 뷰
            //두번째는 현재 스크롤 상태로 OnScrollListener의 상수로 설정
            public void onScrollStateChanged(
                    AbsListView absListView,
                    int scrollState) {
                //스크롤이 끝나고 가장 하단에서 스크롤을 했다면 데이터 업데이트
                if(scrollState ==
                        ListView.OnScrollListener.SCROLL_STATE_IDLE
                        && lastItemVisibleFlag == true){
                    pageNo = pageNo + 1;
                    if(pageNo * 3 >= cnt){
                        Toast.makeText(MainActivity.this,
                                "더 이상 데이터가 없습니다.",
                                Toast.LENGTH_LONG).show();
                    }else{
                        new ThreadEx().start();
                    }
                }
            }

            //Scroll 도중에 호출되는 메소드
            //두번째 매개변수가 첫번째 보이는 아이템의 인덱스
            //세번째 매개변수가 현재 보여지고 있는 행의 개수
            //네번째 매개변수가 전체 출력된 행의 개수
            @Override
            public void onScroll(AbsListView absListView,
                                 int firstVisibleItem,
                                 int visibleItemCount,
                                 int totalItemCount) {
                //스크롤 하는 위치를 감시하다가 마지막인지 인지해서 값을 변경
                if(totalItemCount > 0 &&
                        firstVisibleItem + visibleItemCount
                                >= totalItemCount){
                    lastItemVisibleFlag = true;
                }

            }
        });

 

  3) 검색 버튼을 누를 때 ListView를 초기화하고 데이터를 다시 출력하도록 설정

    => 검색 버튼을 누를 때의 이벤트 처리 코드 작성

btnsearch.setOnClickListener(new Button.OnClickListener(){
    @Override
    public void onClick(View v) {
        pageNo = 1;
        //result = "";
        //list를 초기화하여 listView를 초기화
        list.clear();
        new ThreadEx().start();
    }
});

 

 

**ExpandableListView

    => 한개의 1차원 배열과 한개의 2차원 배열을 이용하여 Accordian 형태를 구현하는 Adapter View

 

**Spinner

    => Combo Box로 여러개 중에서 하나를 선택할 때 이용

    => 여러개 중에서 하나를 선택하고자 하는 경우 Spinner를 이용하거나 ListView를 Dialog에 배치하여 사용

 

**Grid

    => 격자 모양으로 데이터를 출력하기 위한 View

    => 초창기 안드로이드의 사진 앱이 이 형태를 사용했으나 최근에는 Material Design으로 많이 구현

 

**Spannable

    => 텍스트와 이미지를 같이 출력할 수 있는 View

    => TextView 나 EditText는 문자열 전체에 동일한 포맷이 설정되어야 하지만 Spannable은 부분적으로 포맷 설정 가능

 

**ProgressBar

    =>작업의 진행상황을 알려주기 위한 View

    => 이전 API에 ProgressDialog(작업의 진행상황을 알려주는 대화상자)가 있었으나 최근의 API에서 Deprecated 됨

        - Modal 대화상자라 이 대화상자가 화면에 출력되면 다른작업 수행이 불가능하므로 Deprecated

        - 이 대안으로 View에 PrograssBar를 배치하여 사용하는 것을 권장

1. 종료

    => 종료 시점을 알수 있는 일반적인 ProgressBar

        - 안드로이드의 PrograssBar는 2개의 막대가 존재

        - 메인 막대는 progress 속성, 보조 막대는 secondaryProgress를 이용하여 값을 설정

        - ProgressBar는 일반적으로 Thread, Handler 조합으로 사용

    => 종료 시점을 알수 없는 ProgressBar(원형으로 돌아가는 거)

        - 작업 시작지점에 출력하고, 종료시점에 숨기면 되므로 Thread, Handler 미사용

 

2. 특징

    => 막대 ProgressBar는 height를 설정해도 적용되지 않음

    => 원형 ProgressBar는 가로, 세로 모두 설정해도 적용되지 않음

    => 크기가 무조건 wrap_content

 

3. SeekBar

    => 값을 설정할 수 있는 ProgressBar

    => 볼륨 조절같은 것을 할때 많이 보이는 View

    => thumb을 이용해서 값을 조절할 수 있고, thumb의 이미지 변경도 가능

 

4. RatingBar

    => 별점을 설정하는 ProgressBar

 

**날짜와 시간

1. Java의 날짜와 시간 관련 클래스

    => 날짜 : java.sql.Date

    => 시간 : java.sql.Time

    => 날짜와 시간 : java.util.Date, java.util.Calendar(java.util.GregorianCalendar)

    => 날짜, 시간을 원하는 포맷의 문자열로 변경 or 문자열로 된 날짜, 시간을 java.util.Date로 변환해주는 java.text.SimpleDateFormat 

    => 현재 날짜 및 시간

        - new java.util.Date();

        - Calendar.getInstance();

        - new java.util.GregorianCalendar();

        - System.getCurrentTimeMillis() -> 1970.1.1 00:00 이후에 지나온 시간을 밀리초 단위로 리턴

 

2. Android에서 추가된 시간 관련 메소드

    => SystemClock.elapsedRealtime() : 부팅 후 경과된 시간 리턴

    => SystemClock.uptimeMillis() : 부팅 후 경과된 시간 리턴(Sleep한 시간 제외)

    => SystemClock.currentThreadTimeMillis() : 현재 스레드가 소비한 시간

 

3. 시계 위젯

    => DigitalClock, AnalogClock

    => 사용자의 입력은 불가능하고, 포맷 변경만 가능

 

4. DatePicker & Time

    => 터치를 이용하여 날짜와 시간을 입력받는 위젯

 

5. DatePickerDialog & TimePickerDialog

    => 대화상자 형태로 날짜와 시간을 입력받는 View

 

6. Chronometer

    => StopWatch 기능을 해주는 위젯

 

7. CalendarView

    => 달력기능을 하는 위젯

 

**AutoCompleteTextView

    => adapter를 이용하여 자동완성을 제공해주는 View

 

**모바일 애플리케이션의 종류

1. Native App

    => 모바일 OS 제조회사에서 제공하는 SDK를 이용하여 개발하는 방법

    => 사용해야하는 언어가 결정되어 있음

    => iOS : Objective - C, Swift

    => Android : Java, Kotlin

    => Tizen, Web OS : JavaScript

  1) 장점 : 실행속도가 빠름, 기기의 모든 하드웨어 사용 가능

  2) 단점

    - 운영체제별로 다른 언어를 사용하므로 동일한 앱을 여러 운영체제에서 동작하도록 하기 위해 학습기간이 많이 소요

    - Market에 애플리케이션을 업로드하여 사용하므로 업데이트 적용기간이 소요

 

2. WebApp

    => 모바일 웹 사이트를 만들고 이 웹사이트를 WebView나  브라우저에서 확인하는 방식

  1) 장점 : 하나의 Web Site만 구현하면 모든 운영체제의 기기에서 동일한 내용을 확인 가능

  2) 단점

    - 디바이스의 하드웨어 사용에 제약, 터치 기반의 애플리케이션을 구현하는데 한계가 있음

    - 최근에는 Web View만으로 구성한 애플리케이션은 마켓에서 reject

 

3. Hybrid App

    => 프레임워크를 이용하여 애플리케이션을 1번만 제작하면 모든 운영체제에서 사용 가능하게 해주는 방식

        - 빌드시 각 스마트폰 운영체제에 맞게 빌드 해주는 방식

  1) 장점 : 제작 기간이 짧고, 하드웨어 기능의 일정부분은 사용 가능

  2) 단점 : 프레임워크 자체가 너무 자주 업데이트되고,  API가 reject되는 경우도 발생며, 실행속도가 느림

               (프레임워크에 작성한 언어가 실제 Native Code를 호출해야 하기 때문)

    => javascript를 이용하는 방식 : phone gap, ionic, react native등이 존재

    => C#을 이용하는 방식 : Xamarin등이 존재

    => 특수한 형태로 그래픽에 관련된 부분만 만들어주는 Unity, Unreal이 있음

 

**WebView

    => 웹페이지의 URL을 출력해주는 위젯

    => 웹 페이지가 로컬 파일이 나리고 외부에 있는 것이라면 INTERNET 권한이 필요하고 http 프로토콜이면 applicatoin에 useClearTextTraffic이라는 옵션을 true로 설정해 주어야 함

    => 안드로이드에서는 redirect되는 URL에 한해서는 안드로이드의 Chrome이 출력하도록 설정되어 있음

        - Redirect되는 URL을 WebView에 출력하는 경우 WebViewClient의 상속 클래스 객체를 setWebViewClient에 설정을 해주어야 함

    => goBack(), goForward() 메소드 소유

    => 로컬의 HTML 파일도 출력가능

    => 외부에 있는 HTML 파일 경로를 출력시 webapp이라고 하고, 로컬에 있는 HTML을 출력하고 그 파일의 자바스크립트 코드가 NativeApp의 기능을 호출시 Hybrid App이라고 함

    => HTML, JavaScript를 가지고 앱을 만든다고 함

 

**Resource와 Asset의 차이

    => resource는 애플리케이션 시작시 메모리에 로드되는 자원, asset은 메모리에 로드되지 않고 보조기억장치에 존재하는 자원을 의미

    => 안드로이드에서는 resource는 ID를 이용하여 접근, asset은 경로를 이용하여 접근

 

**WebView를 통해서 웹 사이트를 출력해보고 로컬 파일을 출력해보고 웹 서버와 자바스크립트를 이용하여 통신

1. Application 생성

 

2. 프로젝트에 assets 디렉토리를 생성

 

3. assets 디렉토리에 test.html을 복사

 

4. activity_main.xml 파일에서 디자인을 수정

<?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"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webview"/>
</LinearLayout>

 

5. MainActivity.java 파일을 작성

public class MainActivity extends AppCompatActivity {
    private WebView webView;

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

        webView = (WebView)findViewById(R.id.webview);

        //redirect 되는 웹 사이트도 웹 뷰로 출력하기 위한 설정
        webView.setWebViewClient(new WebViewClient());
        //웹 페이지 로드
        webView.loadUrl("https://www.google.com");
    }
}

 

6. AndroidManifest.xml 파일에 권한 설정(인터넷 권한 + http프로토콜을 사용하는 페이지에 접속 가능하도록 설정)

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:usesCleartextTraffic="true"

 

7. webView.loadUrl을 수정하여 로컬 파일을 읽어보기

webview = (WebView)findViewById(R.id.webview);

//redirect되는 웹 사이트도 웹 뷰로 출력하기 위한 설정
webview.setWebViewClient(new WebViewClient());
WebSettings set = webview.getSettings();
set.setJavaScriptEnabled(true);
set.setBuiltInZoomControls(true);
//웹 페이지 로드
//webview.loadUrl("http://www.naver.com");

//로컬의 파일 로드
webview.loadUrl("file:///android_asset/test.html");

 

**javascript 기반의 Hybrid App Framework의 원리

    => 로컬에 HTML 파일 생성후 WebView를 이용해서 출력

    => 이벤트 처리하는 자바스크립트 함수 호출시, 자바스크립트 함수가 Native Code를 호출하도록 되어있음

    =>Hybrid App Framework를 사용하기 위해서는 HTML, JavaScript를 알아야 하고 모바일 기기의 동작에 대해서 이해를 해야 함

 

    => URL : http://192.168.0.200:8080/mysqlserver/

    => native App : MyApp.showToastMessage(String)을 호출

    => Web App : showDisplayNessage(String)을 호출

 

1. Web Page를 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>포트폴리오</title>
</head>
<body>
	<h3>[토스트 메시지 입력]</h3>
	메시지:<input type="text" id="toastmessageinput"/>
	<br/>
	<input type="button" id="showtoast" value="전송"/>
	<br/><br/>
	
	<h3>전달받은 메시지</h3>
	<div id="nativemessage" style="height:200px; overflow-y:auto;"></div>
</body>

<script>
	document.getElementById("showtoast").addEventListener("click", function(e){
			//NativeApp의 메소드를 호출하는 구문
			//NativeApp에 MyApp 이라는 이름을 등록하고
			//showToastMessage 라는 메소드를 생성 
		MYApp.showToastMessage(document.getElementById("toastmessageinput").value);
	});
	
	//Native App에서 호출할 메소드
	function showDisplayMessage(message){
		document.getElementById("nativemessage")
		.innerHTML = message + 
		document.getElementById("nativemessage")
		.innerHTML
	}
</script>
</html>

 

2. Android App 작성

  1) 레이아웃 수정

<?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">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webview"
        android:layout_weight="1"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="5"
        android:orientation="vertical">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="메시지 입력"
            android:id="@+id/msginput"/>
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="메시지 전송"
            android:id="@+id/msgbtn"/>
    </LinearLayout>
</LinearLayout>