81 Android Animation, AdapterView, ProgressBar
** 이전에 만들었던 AndroidPortfolio 수정
=> TextView에 출력했던 데이터를 ListView로 출력 : itemnamea 만 출력
=> ListView의 항목을 클릭하면 itemid를 Toast로 출력
=> ListView를 하단으로 스크롤하면 데이터를 가져와서 업데이트
=> 애니메이션 적용
1. ITEM 테이블의 데이터를 표현하기 위한 DTO 클래스 생성
=> 클라이언트에서 DTO를 만들 때는 속성을 public으로 설정해서 편리하게 사용할 수 있도록 해도 된다.
package com.example.androidportfolio;
public class Item {
public int itemid;
public String itemname;
public int price;
public String description;
public String pictureurl;
@Override
public String toString() {
return itemname;
}
}
2. Activity_main.xml 파일의 디자인 수정
=> ScrollView 대신에 ListView를 배치
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview" />
</LinearLayout>
3. MainActivity.java 파일에서 인스턴스 변수를 수정
=> TextView 대신에 ListView, Data, Adapter
public class MainActivity extends AppCompatActivity {
//콤보 박스 역할을 하는 위젯
private Spinner searchtype;
private EditText value;
private Button btnsearch, btnnext;
//private TextView list;
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=i+1){
JSONArray 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);
list.add(item);
}
//핸들러에게 출력을 요청
handler.sendEmptyMessage(0);
}catch(Exception e){
Log.e("파싱에러", e.getMessage());
}
6. MainActivity.java 파일의 onCreate 메소드에서 ListView 생성하는 작업을 수행
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchtype = (Spinner)findViewById(R.id.searchtype);
adapter = ArrayAdapter.createFromResource(
this,R.array.searchtype_array,
android.R.layout.simple_spinner_dropdown_item
);
searchtype.setAdapter(adapter);
value = (EditText)findViewById(R.id.value);
btnnext = (Button)findViewById(R.id.btnnext);
btnsearch = (Button)findViewById(R.id.btnsearch);
//list = (TextView)findViewById(R.id.list);
listView = (ListView)findViewById(R.id.listview);
list = new Stack<Item>();
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();
}
});
** 트윈 애니메이션
=> 출발점부터 종료점까지 일정한 간격으로 적용하는 애니메이션
=> 일반 뷰에 적용하는 애니메이션 과 뷰 그룹에 적용하는 애니메이션으로 나뉘며 일반 뷰에 적용되는 애니메이션은 뷰에 한꺼번에 적용되지만 뷰 그룹에 적용하면 속한 뷰 각각에 적용된다.
=> 뷰 그룹에 적용할 때는 AnimationSet을 이용해서 애니메이션을 만들고 애니메이션셋을 가지고 AnimationController를 생성한 후 AdapterView 나 Layout에 적용하면 된다.
=> 안드로이드는 애니메이션을 잘 적용하지 않고 iOS는 애니메이션 사용으르 권장한다.
8. MainActivity.java 파일에 onCreate 메소드에 listView 설정 하단에 코드를 추가
//ListView의 항목 애니메이션 설정
AnimationSet set = new AnimationSet(true);
//이동하는 애니메이션
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(10000);
set.addAnimation(rtl);
Animation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1000);
set.addAnimation(alpha);
//각각의 애니메이션을 설정하고 대기 시간을 추가해서 생성
LayoutAnimationController controller =
new LayoutAnimationController(set, 0.5f);
//listView에 애니메이션을 설정
listView.setLayoutAnimation(controller);
package com.example.androidportfolio;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LayoutAnimationController;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Stack;
public class MainActivity extends AppCompatActivity {
//콤보 박스 역할을 하는 위젯
private Spinner searchtype;
private EditText value;
private Button btnsearch, btnnext;
//private TextView list;
private ListView listView;
private List<Item> list;
private ArrayAdapter<Item> itemAdapter;
//Spinner에 데이터를 연결할 Adapter
private ArrayAdapter<CharSequence> adapter;
//페이지 번호와 페이지 당 데이터 개수를 저장할 변수
int pageNo = 1;
int size = 3;
//조건에 맞는 데이터 개수를 저장할 변수
int cnt;
//출력할 내용
String result = "";
//스레드가 다운로드 받아서 파싱한 결과를 출력할 핸들러
Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message message){
//list.setText(result);
//adapter를 이용해서 ListView 에 데이터가
//수정되었으니 다시 출력하고 신호를 보냄
//신호를 보내는 것을 프로그래밍에서는
//Notification이라고 합니다.
itemAdapter.notifyDataSetChanged();
}
};
//데이터를 다운로드 받아서 파싱하는 스레드
class ThreadEx extends Thread{
//다운로드 받은 문자열을 저장할 객체
StringBuilder sb = new StringBuilder();
public void run(){
try{
URL url = null;
//콤보 박스 선택한 항목 번호를 idx에 저장
int idx = searchtype.getSelectedItemPosition();
if(idx == 0){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?" +
"pageno=" + pageNo);
}else if(idx == 1){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=itemname&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}else if(idx == 2){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=description&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}else{
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=both&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}
HttpURLConnection con = (
HttpURLConnection)url.openConnection();
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
while(true){
String line = br.readLine();
if(line == null){
break;
}
sb.append(line + "\n");
}
br.close();
con.disconnect();
}catch(Exception e){
Log.e("다운로드 예외", e.getMessage());
}
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=i+1){
JSONArray 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);
list.add(item);
}
//핸들러에게 출력을 요청
handler.sendEmptyMessage(0);
}catch(Exception e){
Log.e("파싱에러", e.getMessage());
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchtype = (Spinner)findViewById(R.id.searchtype);
adapter = ArrayAdapter.createFromResource(
this,R.array.searchtype_array,
android.R.layout.simple_spinner_dropdown_item
);
searchtype.setAdapter(adapter);
value = (EditText)findViewById(R.id.value);
btnnext = (Button)findViewById(R.id.btnnext);
btnsearch = (Button)findViewById(R.id.btnsearch);
//list = (TextView)findViewById(R.id.list);
listView = (ListView)findViewById(R.id.listview);
list = new Stack<Item>();
itemAdapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
list);
listView.setAdapter(itemAdapter);
//옵션 설정
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();
}
});
//ListView의 항목 애니메이션 설정
AnimationSet set = new AnimationSet(true);
//이동하는 애니메이션
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(10000);
set.addAnimation(rtl);
Animation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1000);
set.addAnimation(alpha);
//각각의 애니메이션을 설정하고 대기 시간을 추가해서 생성
LayoutAnimationController controller =
new LayoutAnimationController(set, 0.5f);
//listView에 애니메이션을 설정
listView.setLayoutAnimation(controller);
btnnext.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
pageNo = pageNo + 1;
new ThreadEx().start();
}
});
btnsearch.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
pageNo = 1;
result = "";
new ThreadEx().start();
}
});
}
@Override
public void onResume(){
super.onResume();
new ThreadEx().start();
}
}
** Adapter View 의 업데이트
=> 화면에 데이터를 재출력할 때는 연결된 Adapter의 setNotifyDataChanged()를 호출해주면 된다.
=> 이벤트 설정을 할 때 스크롤을 가장 하단에서 하면 업데이트하는 경우가 있고 상단에서 일전 시간동안 아래쪽으로 끌어내리면 업데이트하는 경우도 있다.
보통은 하단에서 하는 경우에는 이전 데이터를 가져와서 추가하는 경우이고 상단에서 하는 경우는 최신의 데이터를 가져오는 경우이다.
=> 하단에서 하는 경우는 Scroll Event에서 가장 하단에서 Scroll 한 것인지 감지해서 작업을 하고 상단에서 하는 경우는 별도의 라이브러리를 제공한다.
이 라이브러리를 pull to request라고 하면 Refresh View를 제공한다.
이러한 Refresh View Adapter View에서만 사용하도록 한다.
=> ScrollEvent는 OnScrollListener가 처리하고 메소드는 onScrollStateChanged와 onScroll이다. onScroll은 스크롤을 하고 잇는 도중에 호출되고 onScrollStateChanged는 스크롤이 끝나면 호출된다.
하단에서 스크롤 한 것인지 학인할 변수를 생성
onScroll에서 가장 하단에서 스크롤 하는 것인지 감시하다가 하단에서 스크롤하면 변수의 값을 변경
onScrollStateChnged 메소드에서 변수를 확인해서 실제 업데이트를 수행한다.
9. MainAcitivity.java 파일에 추가
1) 인스턴스 변수 선언
public class MainActivity extends AppCompatActivity {
//스크롤이 가장 아래에서 수행되었는지 확인할 변수
boolean lastItemVisibleFlag = false;
2) onCreate 메 소드 하단에 스크롤 이벤트 핸들러 작성
** ExpandableListView
=> 한개의 1차원 배열과 한개의 2차원 배열을 이용해서 Accordian 형태로 구현하는 Adapter View
** Spinner
=> Combo box 여러 개 중에서 하나를 선택할 때 이용
=> 여러 개 중에서 하나를 서택하고자 할 때는 Spinner 를 이용하던가 아니면 ListView를 Dialog에 배치해서 사용
** Grid
=> 격자 모양으로 데이터를 출력하기 위한 View
=> 초창기 안드로이드의 사진 앱이 이 형태를 사용했는데 최근에는 Material Design으로 구현을 많이 한다.
** Spannable
=> 텍스트와 이미지를 같이 출력할 수 있는 텍스트
=> TextView 나 EditText는 문자열 전체에 동일한 포맷이 설정되어야 하지만 Spannable은 부분적으로 포맷을 설정할 수 있다.
** Progress Bar
=> 작업의 진행 상황을 알려주기 위한 View
=> 이전 API 에 ProgressDialog가 있는데 이 대화상자는 작업의 진행상황을 알려주는 대화장자였는데 최근의 API에서 Deprecated
이 대화상자가 Modal 대화상자라서 이 대화상자가 화면에 출력되면 다른 작업을 수행할 수가 없어서 UI 측면에서 좋지못하다고 해서 Deprecated
이 대안은 View에 Progress Bar를 배치해서 사용하는 것을 추천
1. 종류
=> 종료 시점을 알 수 있는 일반적인 ProgressBar
안드로이드의 ProgressBar는 2개의 막대가 존재
메인 막대는 progress 속성으로 값을 설정하고 보조막대는 secondaryProgress를 이용해서 값을 설정
ProgressBar는 일반적으로 Thread와 Handler 조합으로 사용
=> 종료 시점을 알 수 없는 ProgressBar(원형으로 돌아가는 ProgressBar - 기본값)
작업 시작 시점에 출력하고 작업 종료 시점에 숨기면 된다.
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.tuil.Calendar(java.util.GregorianCalendar)
=> 날짜와 시간을 원하는 포맷의 문자열로 변경하거나 문자열로 된 날짜 및 시간을 java.util.Date로 변환해주는 java.text.SimpleDateFormat
=> 현재 날짜 및 시간
new java.util.Date();
Calendar.getInstance();
new java.util.GregorianCalendar();
System.getCurrentTimeMillis() -> 1970년 1원 1일 자정 이후에 지나온 시간을 밀리초 단위로 리턴
2. Android에서 추가된 시간 관련 메소드
SystemClock.elapsedRealtime() : 부팅 후 경과된 시간 리턴
SystemClock.uptimeMillis() : 부팅 후 경과된 시간 리턴(sleep 한 시간 제외)
SystemClock.currentThreadTimeMillis() : 현재 스레드가 소비한 시간
3. 시계 위젯
=> DigitalClock, AnalogClock
=> 사용자의 입력을 받을 수는 없고 포맷 변경만 가능
4. DatePicker & TimePicker
=> 터치를 이용해서 날짜와 시간을 입력받는 위젯
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. Web App
=> 모바일 웹 사이트를 만들고 이 웹 사이트를 Web View나 브라우저에서 확인하는 방식
=> 하나의 Web Site만 구현하면 모든 운영체제의 기기에서 동일한 내용을 사용할 수 있다는 장점
=> 디바이스의 하드웨어 사용에 제약, 터치 기반의 애플리케이션을 구현하는데 한계가 있다.
=> 최근에는 WebView만으로 구성한 애플리케이션은 마켓에서 reject
3. Hybrid App
=> 프레임워크를 이용해서 애플리케이션을 1번만 제작하면 모든 운영체제에서 사용할 수 있도록 해주는 방식
빌드를 할 때 각 스마트폰 운영체제에 맞게 빌드를 해주는 방식
=> 여러 운영체제에서 동작하는 앱을 만드는 제작 기간이 짧고 하드웨어 기능도 일정부분 사용할 수 있다.
=> 프레임워크 자체가 너무 자주 업데이트 되고 API가 reject되는 경우도 발생하고 실행속도가 느리다.(프레임워크에 작성한 언어가 실제 Native Code를 호출해야 하기 때문)
=> javacript를 이용하는 방식으로 phone gap, ionic, react native 등이 있고 C#을 사용하는 방식으로 Xamarin 등이 있다.
=> 특수한 형태로 그래픽에 관련된 부분만 만들어주는 Unity, Unreal 이 있다.
** WebView
=> 웹 페이지의 URL을 출력해주는 위젯
=> 웹 페이지가 로컬 파일이 아니고 외부에 있는 것이라면 INTERNET 권한이 필요하고 http 프로토콜이면 application에 useClearTextTraffic 이라는 옵션을 true로 설정해 주어야 한다.
=> 안드로이드에서는 redirect되는 URL에 한해서는 안드로이드의 Chrome이 출력하도록 설정되어 있다.
redirect 되는 URL을 WebView에 출력하고자 할 때는 WebViewClient를 상속한 클래스의 객체를 setWebViewClient에 설정을 해주어야 한다.
=> goBack(), goForward() 메소드 소유
=> 로컬의 HTML 파일도 출력 가능
=> 외부에 있는 HTML 파일 경로를 출력하면 Web App이라고 하고 로컬에 있는 HTMl을출력하고 그 파일의 자바스크립트 코드가 Native App의 기능을 호출하면 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 파일을 작성
package com.example.android0730;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
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);
//웹 페이지 로드
webView.loadUrl("https://www.naver.com");
}
}
6. androidManifest.xml 파일에 인터넷 권한 부여
<uses-permission android:name="android.permission.INTERNET"/>
7. androidManifest.xml 파일에 http 크로토콜을 사용하는 웹 페이지에도 접속이 가능하게 설정 추가
android:usesCleartextTraffic="true"
8. webView.loadURL을 수정해서 로컬 파일을 읽어보기
webView.loadUrl("file:///android_assets/test.html");
package com.example.android0730;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
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");
//로컬의 파일 로드
webView.loadUrl("file:///android_asset/test.html");
}
}
** javaScript 기반의 HybirdAppFramework의 원리
=> 로컬에 HTML 파일을 생성해서 Web View를 이용해서 이를 출력하고 이벤트 처리하는 자바스크립트 함수를 호출하면 자바스크립트 함수가 Native Code를 호출하도록 만들어져 있다.
=> Hybrid App Framework를 사용하기 위해서는 HTML, JavaScript를 알아야하고 모바일 기기의 동작에 대해서 이해를 해야 한다.
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="hetght: 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>
=> URL: http://192.168.0.200:8080/mysqlserver
=> Native App: MyApp.showToastMessage(String)을 호출
=> Web App: showDisplayMessage(String)을 호출
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"
tools:context=".MainActivity"
android:orientation="vertical">
<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:hint="메시지 입력"
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>
2) Activity.java 파일 수정
package com.example.android0730;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private WebView webView;
private EditText msgInput;
private Button msgBtn;
//자바스크립트를 사용할 수 있도록 해주는 인터페이스 클래스 생성
public class AndroidJavaScriptInterface{
//화면 출력을 위한 2개의 변수
private Context context;
private Handler handler = new Handler();
//Context를 넘겨받기 위한 생성
public AndroidJavaScriptInterface(Context context){
this.context = context;
}
//웹에서 호출하기 위한 메소드
//웹에서 호출한 메소드 이름과 일치해야 합니다.
@JavascriptInterface
public void showToastMessage(final String message){
//매개변수로 넘어온 데이터를 Toast로 출력
handler.post(new Runnable(){
public void run(){
Toast.makeText(
context, message,
Toast.LENGTH_LONG).show();
}
});
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView)findViewById(R.id.webview);
msgInput = (EditText)findViewById(R.id.msginput);
msgBtn = (Button)findViewById(R.id.msgbtn);
//redirect 되는 웹 사이트도 웹 뷰로 출력하기 위한 설정
webView.setWebViewClient(new WebViewClient());
WebSettings set = webView.getSettings();
set.setJavaScriptEnabled(true);
set.setBuiltInZoomControls(true);
//자바스크립트 메소드 등록
webView.addJavascriptInterface(
new AndroidJavaScriptInterface(this),
"MYApp");
//로컬의 파일 로드
webView.loadUrl(
"http://192.168.0.200:8080/mysqlserver");
msgBtn.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View view){
//자바스크립트 함수 호출
webView.loadUrl(
"javascript:showDisplayMessage('"
+ msgInput.getText().toString() + "')");
}
});
}
}
데이터조회는 포워딩해도 된다. 그 이외의 작업은 리다이렉트해야 한다.