'안드로이드'에 해당되는 글 453건

728x90

eclipse 가 실행되어 있는 걸 깜빡하고 eclipse 버튼을 또 누르면 workspace1 을 새로 생성할거냐고 물어본다. 아무 생각없이 Yes 를 누르면 완전 새로운 화면이 뜨면서 그동안 작성했던 내용은 하나도 안보인다. ㅠㅠㅠ


몇번의 시행착오를 거치고 나서야 workspace 를 변경해서 사용할 수 있다는 걸 알았다.

workspace1 을 새로 생성하면 모든 환경설정 상태가 초기화가 되어 버린다.


workspace_work 폴더를 수동으로 생성한 다음에 workspace 에 있는 파일 전부를 workspace_work 로 복사를 한다.

workspace 를 변경해서 해당 폴더를 선택한다.

그러면 원래 workspace 에서 저장했던 모든 환경설정 정보가 그대로 남아 있다.


workspace 를 변경하는 방법은 간단하다. 아래 그림처럼 해주기만 하면 된다.



여기서 중요한 점!!!!

폴더채 복사를 했기 때문에 환경설정 정보가 기존 폴더 정보를 가리키고 있을 수도 있다.

혹시나 하고 삭제를 했더니 workspace_work 폴더에 있는 프로젝트 파일이 삭제되는게 아니라 workspace 에 있는 프로젝트 파일이 삭제되는 걸 경험했다.

그러므로 열린 파일을 하나하나 모두 닫아준다.




프로젝트 파일 삭제를 선택하지 않고 그냥 OK를 눌러서 프로젝트만 삭제한다.

해당 폴더 workspace_work 에서 수동으로 불필요한 파일을 전부 삭제한다.

불필요한 파일이란 workspace 에는 있어야 하고 workspace_work 에는 없어야 하는 파일이다.


이렇게 프로젝트를 전부 닫은 상태에서 프로젝트를 다시 열기를 하면 된다.



폴더별로 원하는 형태로 관리를 하면 편리한 거 같다.

기존에는 이걸 몰라서 업무용 프로젝트와 연습하는 프로젝트를 같이 혼용해서 사용했었다.


개발을 하다보면 다른 폴더에 구분해 놓은 프로젝트를 참조할 경우가 생긴다.

그러면 해당 프로젝트를 import 로 읽어들이면 된다. (http://link2me.tistory.com/1021)

블로그 이미지

Link2Me

,
728x90

안드로이드 애플리케이션은 크게 '자바코드'와 '리소스'로 구성된다.

자바코드는 애플리케이션의 흐름과 기능을 정의하는 부분이고

리소스는 Layout 이나 이미지와 같이 주로 애플리케이션이 사용자에게 보이는 부분이다.

Manifest는 설치된 애플리케이션의 구성요소나 권한을 정의한다.


Layout 은 ViewGroup 클래스로부터 상속을 받으며, Layout 안에 존재하는 위젯(버튼, 텍스트뷰,에디트뷰 등)을 배치한다. 화면에 구성하려면 Layout 에 대해 이해를 해야 한다.

스마트폰의 크기가 다양하므로 절대 픽셀 위주로 Layout을 잡는 것은 대단히 위험하다.

그래서 본 내용에서는 절대 Layout 은 다루지 않는다.


 공통

 android:layout_width="match_parent" // 무조건 남아있는 여유공간을 채움
 android:layout_width="wrap_content" // View의 내용물의 크기에 따라 결정되도록 함
 android:layout_margin="10dp"  // 위젯과 여유 공간 사이의 여백 설정
 android:padding="20dp"  // 위젯 내부 여백 설정

LinearLayout

 간단하고 직관적으로 이해하기 쉬워서 많아 사용한다.

 자식(child) View 들을 수평 또는 수직으로 배치한다.

 android:orientation="vertical"  // 수직으로 배치(수직으로 차곡차곡 쌓겠다)

 android:gravity="top" // 상단 (View의 정렬방향을 설정)

 android:gravity="left|top" // 왼쪽 상단

 android:gravity="center_horizontal|center_vertical"  // 정중앙

 android:gravity="right|bottom"  // 오른쪽 하단

 Gravity 속성을 이용하여 자식(child) View 를 상단, 하단, 중앙에 배치할 수 있다.

 android:layout_weight="1" // View 내부 child끼리의 크기 비율 지정(공간 가중치) 

 child View 의 가중치를 지정하지 않으면 0 으로 간주하고 확장하지 않는다.

 LinearLayout 을 여러개 사용하면 각 Layout의 크기를 지정해야 한다.

 내부 LinearLayout 에서 layout_weight="1" 로 모두 지정하면 비율이 동일하게 출력된다.

 layout_margin은 "값"만큼 띄어서 배치하겠다는 뜻

 android:layout_gravity=center

 gravity 속성 앞에 layout이 붙어 있으면 해당 위젯이 포함되어 있는 Layout 위젯의

 정렬방식을 의미한다.

 

 RelativeLayout

 View 들의 위치가 상대적으로 정해진다.

 (기준 View 가 있고, 배치하고자 하는 View를 기준 View의 어디에 둘 것인지 설정)

 RelavtiveLayout 은 자신을 포함하고 있는 컨테이너나 다른 위젯을 기준으로 위젯을

 배치하는 레이아웃(layout)이다.

 부모 컨테이너나 다른 View(위젯)와의 상대적 위치를 이용해 화면을 구성한다.

 android:layout_below="@id/topButton"  <-- topButton 기준으로 하단에 위치

 android:layout_above="@id/bottomButton" <-- bottomButton 기준 위에 위치


  자신을 포함하는 컨테이너를 기준으로 해당 위젯을 배치하는 속성들

 layout_alignParentTop : 부모 컨테이너의 위쪽과 뷰의 위쪽을 맞춤
 layout_alignParentBottom : 부모 컨테이너의 아래쪽과 뷰의 아래쪽을 맞춤
 layout_alignParentLeft : 부모 컨테이너의 왼쪽 끝과 뷰의 왼쪽 끝을 맞춤
 layout_alignParentRight : 부모 컨테이너의 오른쪽 끝과 뷰의 오른쪽 끝을 맞춤
 layout_centerHorizontal : 부모 컨테이너의 수평방향 중앙에 배치
 layout_centerVertical : 부모 컨테이너의 수직 방향 중앙에 배치
 layout_centerInParent : 부모 컨테이너의 수평과 수직 방향 중앙에 배치


 다른 위젯을 기준으로 해당 위젯을 배치하는 속성들

 layout_above : 지정한 뷰의 위쪽에 배치
 layout_below : 지정한 뷰의 아래쪽에 배치
 layout_toLeftOf : 지정한 뷰의 왼쪽에 배치
 layout_toRightOf : 지정한 뷰의 오른쪽에 배치
 layout_alignTop : 지정한 뷰의 위쪽과 맞춤
 layout_alignBottom : 지정한 뷰의 아래쪽과 맞춤
 layout_alignLeft : 지정한 뷰의 왼쪽과 맞춤
 layout_alignRight : 지정한 뷰의 오른쪽과 맞춤
 layout_alignBaseline : 지정한 뷰와 내용물의 아래쪽 기준선(baseline)을 맞춤


  android:layout_centerVertical="true"


 FrameLayout

 여러 Layout 을 중첩시켜, 원하는 Layout 만 볼 수 있도록 해준다.

 자식 View 들은 등장하는 순서대로 화면에 중첩되어 표시된다. 

 기준점은 좌측 상단이 된다.

 TableLayout

 격자모양의 배열을 이용하여 화면을 구성한다.

 자식 View 들을 테이블 형태로 배치한다.

 layout_span 은 열을 합쳐서 표시하라는 의미.

 layout_span="3" 은 현재 셀부터 3개를 합쳐서 표시하라는 의미




@ : 리소스에서 참조한다는 의미

+ : 새로 생성한다는 의미

id : 식별자를 나타내는 패키지

layout1 : 식별자


이 id 를 이용하여 activity 의 onCreate() 메소드에서 findViewById()를 호출하여 layout1 을 찾을 수 있다.

java 코드에서 참조할 일이 없으면 id를 만들지 않아도 된다.


protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main); // layout 폴더에 있는 main.xml 파일을 찾는다.

        // 화면에 나타낼 View를 지정하고, xml Layout의 내용을 메모리상에 객체화한다.

        // 이 두줄의 코드 위에 정보를 넣으면 객체화되지 않아서 강제종료 처리가 된다.

        layout1 = (LinearLayout) findViewById(R.id.layout1);  // id 가 layout1  인 것을 찾는다.


background 속성

- #RRGGBB 값을 지정한다. R:red, G:green, B:blue

- #AARRGGBB 방식인 경우 AA:투명도를 나타내며 00 : 완전투명, FF:완전불투명, 77 : 반투명


Android 폰의 해상도와 관계없이 잘 작동하는 화면을 만들 때 dp 단위를 많이 사용한다.


padding 속성

- 자신의 내부에 들어있는 위젯과 자신의 경계선과의 간격을 지정

- padding은 상하좌우 모두에 지정하는 속성

- 따로 지정하고 싶으면 paddingTop, paddingBottom, paddingLeft, paddingRight를 이용


layout_margin

- 자신의 부모 Layout 이나 위젯과의 간격, 주위의 다른 위젯과의 간격을 지정한다.



'안드로이드 > Layout' 카테고리의 다른 글

Android Fragment 기본 예제  (0) 2018.09.11
FloatingActionButton(FAB)  (0) 2018.08.15
LinearLayout weight  (0) 2018.03.01
Android ViewFlipper(뷰플리퍼)  (0) 2017.05.02
Fragment 화면 이동  (0) 2017.03.26
블로그 이미지

Link2Me

,
728x90

안드로이드 앱 프로그래밍 책과 인터넷 검색을 통해서 안드로이드 앱 프로그램을 만드는 연습을 하다보니 한계에 부딪치기 시작했다.

인터넷에 올라온 소스코드를 가지고 테스트를 해보니 전화번호부 가져오기를 하다보니 원하지 않게 값이 저장된다. 거의 유사한 코드들이 복사해서 돌아다니고 있다.

그래서 자바 책을 들여다보면서 기본적인 사항을 내 나름대로 조금씩 정리를 해둔다.

처음부터 문법만 접하면 어떻게 활용해야 하는지를 몰라서 성취감이 없다.

목표를 정하고 만들어야겠다는 걸 하기 위해 기능을 하나씩 익히고 부족하면 문법사항을 들여다보면서 배우는게 초보자인 나에게는 효율적이다.

부족하지만 그동안 작성해두었던 게시물 중심으로 샘플을 활용하여 작성해둔다.


주석문

// 한줄 주석


/*

여러줄 주석

*/ 


/**

 document 생성용 주석

*/



변수(Variable) : 프로그램이 실행되는 동안 데이터를 저장하기 위한 공간

- 메모리 공간 할당 + 메모리 공간의 이름

- int 와 같이 변수의 특성을 결정짓는 키워드를 가리켜 자료형이라 한다.

- 변수 이름의 제약 사항

  . 숫자로 시작 불가

  . $와_ 이외의 다른 특수문자는 사용불가

  . 키워드는 변수의 이름으로 사용 불가

- short 형 데이터는 int형 데이터로 자동 변환한다.

- 자바에서는 true 와 false는 그 자체로 저장이 가능한 데이터이다.

- 1과 '1'은 서로 다르다.

지역변수

 - Local Variable(지역변수)는 메서드 안에 선언된 변수를 말한다.

 - 사용자가 직접 초기화를 해주어야 한다. 초기화를 안하면 에러가 발생한다.

 - 지역변수 생성 시점은 해당 메서드가 수행될 때이다.

 - 메서드 수행이 끝나면 지역변수는 메모리에서 사라진다.

 멤버변수

 - Member Variable(멤버변수)는 클래스 안에 선언된 변수를 말한다.

 - 사용자가 초기화를 하지 않아도 시스템에 의해서 자동으로 초기화된다.

   하지만 프로그램의 정확성을 위해 직접 초기화하는 습관이 필요하다.

 - 멤버변수는 static 으로 선언된 변수와 인스턴스 변수가 있다.

 - static 변수는 객체를 생성하지 않고, 클래스 이름으로 참조할 수 있다.

 - 인스턴스 변수는 반드시 객체를 생성한 후 객체 레퍼런스를 통해서 참조해야 한다.



안드로이드앱 프로그래밍 동영상 강좌를 듣다보면 casting 이란 말이 엄청 나온다.

casting(형변환)은 변수 또는 리터럴의 타입을 다른 타입으로 변환하는 것을 말한다.

int score = (int) 88.3; // double 형의 값을 int 형으로 변환하여 88을 저장한다.


배열은 PHP 에서도 정말 많이 활용된다. C#에서도 배열의 필요성을 많이 알게되었다.

소스코드를 접하면서 내가 소홀하게 무심코 넘겼던 것을 알게되었고, 배열의 사용이 정말 중요하고 많더라.


배열(array)을 선언한 다음에는 배열을 생성해야 한다.

- 배열 선언 : 참조변수를 위한 공간 만들기

  int [] grades;

- 배열 생성 : new 키워드를 이용하여 배열을 생성한다. 데이터를 저장할 수 있는 공간이 만들어진다.

  grades = new int[5];

- 배열은 같은 타입의 데이터만 여러개 생성한다.

- 배열의 길이는 length 속성을 이용하면 알 수 있다.

- 배열의 크기를 키보드 입력 값을 받아서 지정할 수 있다.

  int n = 10;

  int [] grades = new int[n];

- 배열에서 최대값, 최소값을 구할 때 int max = 0; 이라는 초기값을 지정하는 것은 오류가 발생할 수 있다.

  최대값이 음수일 수도 있는데, 초기값을 0 으로 선언하면, 0보다 작으므로 원하지 않는 결과가 나온다.


배열의 복사

배열에서 조건에 맞는 데이터만 복사하고 싶은 경우가 있다.

이때 for 문으로 돌리면서 if 조건문으로 해당되는 데이터만 복사하면 된다.


배열의 단점

ㅇ 크기를 변경할 수 없다.

    - 크기를 변경할 수 없으므로 새로운 배열을 생성해서 데이터를 복사하는 작업이 필요하다.

    - 실행속도를 향상시키기 위해서는 충분히 큰 크기의 배열을 생성해야 하므로 메모리가 낭비된다.

ㅇ 비순차적 데이터의 추가 또는 삭제에 시간이 많이 걸린다.

    - 차례대로 데이터를 추가하고 마지막에서부터 데이터를 삭제하는 것은 빠르지만,

    - 배열의 중간에 데이터를 추가하려면, 빈자리를 만들기 위해 다른 데이터들을 복사해서 이동해야 한다.


equals()는 두 배열에 저장된 모든 요소를 비교해서 같으면 true, 다르면 false를 반환한다.

equals()는 일차원 배열에서만 사용 가능하다.

다차원 배열 비교는 deepEquals()를 사용해야 한다.



메서드 : 코드 블럭에 이름을 붙여 놓은 것을 말한다.

- return type : 메서드는 블록 안의 코드를 실행한 다음 결과를 반환할 수 있다.

- return 값이 필요없는 경우는 void 라고 적는다.

- 반환한 값이 있다면 데이터의 유형에 따라 byte, short, int, long, float, double, char, boolean, 배열, 클래스 이름을 적는다.

- 매개변수 : 외부로부터 값을 전달받는데 사용된다.

- 메서드를 작성하는 가장 큰 이유는 반복적으로 사용하는 코드를 줄이기 위함이다.

- 하나의 메서드는 한가지 기능만 수행하도록 작성하는 것이 좋다.


Java에서 매개변수 전달방식은 call-by-value 이다.
Java 에서 문자열 비교는 compareTo
if(names[j].compareTo(names[j+1]) > 0){ }


method overloading : 한 클래스 내에 같은 이름의 메서드(함수)를 여러개 정의하는 것을 말한다.

- 메서드 이름이 같아야 한다.

- 매개변수의 개수 또는 타입이 달라야 한다.

- 매개변수는 같고 리턴타입이 다른 경우는 오버로딩이 성립되지 않는다.


public class Contacts {
    public void ContactsIDInsert(ContentResolver cr, Context context, String strName, String strMobileNO) {
        //코드 작성
    }

    public void ContactsIDInsert(ContentResolver cr, Context context, String strName, String strMobileNO, String strOfficeNO) {
        //코드 작성
    }

    public void ContactsIDInsert(ContentResolver cr, Context context, String strName, String strMobileNO, String strOfficeNO, String strEmail, String strPhoto) {
        //코드 작성
    }

}


오버라이딩(overriding)

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 한다.

overriding 의 조건

- 이름이 같아야 한다.

- 매개변수가 같아야 한다.

- 리턴타입이 같아야 한다.

- static, final, private 가 지정된 메소드는 오바라이딩 불가


try catch finally 문

try {

   예외 발생 가능성이 있는 문장들;

}
catch (예외타입 메개변수명){

   예외타입의 예외가 발생할 경우 처리 문장;

}
finally {

   항상 수행할 필요가 있는 문장들;

   // 필수 블록은 아니다

}

그냥 무조건 항상 실행되는 것이 아니라 finally 와 연결되어있는 try 블록으로 일단 진입을 하면,
무조건 실행되는 영역이 바로 finally 블록이다.
중간에 return 문을 실행하더라도 finally 블록이 실행된 다음에 메소드를 빠져나간다.


'안드로이드 > Java 문법' 카테고리의 다른 글

[Java] 정보은닉과 캡슐화  (0) 2017.10.16
자바 기본 데이터형과 크기  (0) 2017.10.11
Java 클래스(Class) ★★★★★  (0) 2017.05.04
ArrayList and HashMap  (0) 2017.03.06
Java foreach 문  (0) 2017.03.01
블로그 이미지

Link2Me

,
728x90

UI 디자인을 하고 자바코딩을 하고 나서 어플이 제대로 동작하는지 확인하는 과정이다.

Eclipse 를 처음 실행해 볼 때에는 이렇게 한다는 것도 몰랐었다.

초보자에겐 역시 어려운 과정인 셈이다.


1. 스마트폰을 USB 케이블로 컴퓨터에 연결한다.


2. Eclipse 에서 아래 그림과 같이 실행한다.

    폰에 앱을 설치하여 잘 동작하는지 확인해보고자 하는 과정으로

    해당 파일에서 마우스 우클릭을 하여 실행한다.



이제 휴대폰을 선택하고 OK를 누르면 내 스마트폰으로 테스트 어플을 구동시켜 볼 수 있다.

안드로이드 스마트폰으로 디버깅하는 방법이 가장 빠르게 원하는 결과를 알 수 있다.

에뮬레이터로 실행하면 시간이 많이 걸린다.



블로그 이미지

Link2Me

,
728x90

안드로이드와 PHP 간에 로그인 연동처리 방법을 보강했다.

네트워크 상태체크, 폰에 자동로그인 정보기록하기, POST로 로그인 처리 하는 부분까지는 이전 게시글에 있는데 본격적으로 서버에 있는 다른 자료를 검색하면서 SESSION 검사를 해서 비정상 접속은 차단하는 것까지 해야 안전하기 때문에, 세션정보까지 연동되도록 처리하는 과정을 추가했다.


처음에 세션이 생성되는지에 대한 로그, 생성된 세션이 다른 Activity 에서 제대로 받는지 여부에 대해서 확인하는 로그인데 동일한 세션 값이 넘어가는 걸 확인할 수 있다.


자료 검색해서 원하는 결과를 제대로 얻기까지 많은 블로그를 탐독하고 찾으면서 겨우 성공했다.

PHP 코드상에서 처리하는 사항은 잘 아는데 안드로이드 부분은 처음 접하다보니 어렵다.

Web에서는 세션정보가 잘 넘어가는데 안드로이드에서 세션처리를 해주어야만 넘어간다.


===== loginChk.php ======

<?php
session_start();
if(isset($_POST['loginID']) && !empty($_POST['loginID']) && isset($_POST['loginPW']) && !empty($_POST['loginPW'])) {
    $loginID = trim($_POST['loginID']);
    $loginPW = trim($_POST['loginPW']);
    $deviceID = trim($_POST['deviceID']);

    $deviceID = $deviceID ? $deviceID : '';  // 안드로이드 폰의 장치 ID (안드로이드 코드에서 확인)

    if(empty($deviceID)){
        require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php';
        require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';
        $c=new LoginClass();

        $row = $c->WebUserAuthCheck($loginID,$loginPW);
        if(is_array($row)) {
            if($row['code'] > 0) {
                $_SESSION['userID'] = $row['id'];
                $_SESSION['userPW'] = md5($loginPW);
                $_SESSION['code'] = $row['code'];
                $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
                $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

                echo("<meta http-equiv='Refresh' content='0; URL=mobile/list.php'>");
            }
        }

    } else {
        require_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
        require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
        $conn=new MySQLiDbClass();
        $DB_CONNECT = $conn->isConnectDb($DB);

        require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';
        $c=new LoginClass();

        $result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
        if($result > 0 ) {
            session_save_path('./_tmp/session');

            $_SESSION['userID'] = $loginID;
            $_SESSION['userPW'] = md5($loginPW);
            $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
            $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
            echo 'Login Success';
        } else if($result == '0') {
            echo 'Login Fail';
        } else {
            echo 'Phone Dismatch';
        }
    }
   
} else {
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>


===== get_json.php ======

<?php
session_start();
if(!($_SESSION['userID'] && $_SESSION['userPW'])) {
    exit;
}

$query = $_REQUEST['search'];

    include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/xmlClass.php';

    $conn = new MySQLiDbClass(); // DB 함수
    $DB_CONNECT = $conn->isConnectDb($DB);

    // 화면에 출력할 칼럼 발췌
    $sql = "select uid,name,mobile from Person ";
    if(!empty($query)) {
        $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'";
    }
    $result = mysqli_query($DB_CONNECT,$sql);

    // 1. JSON 데이터 생성방법
    $c = new jsonClass();
    echo $c->JSONEncode($result,'result');

?>


안드로이드 로그인 소스코드 부분이다.

IP주소 부분을 여러곳에서 사용해야 해서 별도로 Value.java 파일로 작성했다.


===== Value.java ====

import android.app.Activity;

public class Value extends Activity {
    public static final String IPADDRESS = "http://IP주소"; // SERVER IP
    public static final String VERSION = "1.0.0"; // App Version

}

===== Login.java =====

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class Login extends Activity {

    String getDeviceID; // 스마트기기의 장치 고유값
    ProgressDialog dialog = null;
    EditText etId;
    EditText etPw;
   
    String loginID;
    String loginPW;
    CheckBox autologin;
    Boolean loginChecked;
    List<NameValuePair> params;
    public SharedPreferences settings;
    CookieManager cookieManager;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
       
        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        // 네트워크 연결상태 체크
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }
       
        etId = (EditText) findViewById(R.id.login_id_edit);
        etPw = (EditText) findViewById(R.id.login_pw_edit);   
        autologin = (CheckBox) findViewById(R.id.autologinchk);
       
        settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
        loginChecked = settings.getBoolean("LoginChecked", false);
        if (loginChecked) {
            etId.setText(settings.getString("loginID", ""));
            etPw.setText(settings.getString("loginPW", ""));
            autologin.setChecked(true);
        }
       
        if(!settings.getString("loginID", "").equals("")) etPw.requestFocus();
       
        CookieSyncManager.createInstance(this);
        cookieManager = CookieManager.getInstance();
        CookieSyncManager.getInstance().startSync();
       
        Button submit = (Button) findViewById(R.id.login_btn);
        submit.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                dialog = ProgressDialog.show(Login.this, "", "Validating user...", true);
                 new Thread(new Runnable() {
                        public void run() {
                            login();                         
                        }
                       
                      }).start();                
            }
           
        });
       
    }

    void login() {
        try {
            loginID = etId.getText().toString().trim();
            loginPW = etPw.getText().toString().trim();
           
            // 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요
            TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            if (mTelephony.getDeviceId() != null){
                getDeviceID = mTelephony.getDeviceId();  // 스마트폰 기기 정보
            } else {
                getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
            }       
       
            String postURL = Value.IPADDRESS + "/loginChk.php";
            HttpPost post = new HttpPost(postURL);
           
            // 전달할 인자들
            params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("loginID", loginID));
            params.add(new BasicNameValuePair("loginPW", loginPW));
            params.add(new BasicNameValuePair("deviceID", getDeviceID));
           
            UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
            post.setEntity(ent);

            HttpClient httpclient = new DefaultHttpClient();
                              
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            final String responsePost = httpclient.execute(post, responseHandler);

            System.out.println("DeviceID : " + getDeviceID);
            System.out.println("Response : " + responsePost);
           
            runOnUiThread(new Runnable() {
                public void run() {
                    dialog.dismiss();
                }
            });
           
            if(responsePost.equalsIgnoreCase("Login Success")){ // 로그인 정보 일치
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(Login.this,"Login Success", Toast.LENGTH_SHORT).show();
                    }
                });
               
                List<Cookie> cookies = ((DefaultHttpClient)httpclient).getCookieStore().getCookies();
                if (!cookies.isEmpty()) {
                    for (int i = 0; i < cookies.size(); i++) {
                        String cookieString = cookies.get(i).getName() + "="
                                    + cookies.get(i).getValue();
                        Log.e("PHP_setCookie", cookieString);
                        cookieManager.setCookie(Value.IPADDRESS, cookieString);
                    }
                }
                Thread.sleep(500);
               
                startActivity(new Intent(this.getApplicationContext(), MainActivity.class));
                finish(); // finish()를 호출해서 Activity를 없애줌

            } else if(responsePost.equalsIgnoreCase("Phone Dismatch")){ // 등록된 단말기와 불일치
                deviceDismatch_showAlert();
            } else {
                showAlert();
            }
        } catch(Exception e) {
            dialog.dismiss();
            System.out.println("Exception : " + e.getMessage());
        }
       
    }
   
    public void onStop(){
        // 어플리케이션이 화면에서 사라질때
        super.onStop();       
        // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장     
        if (autologin.isChecked()) {
             settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
            
             editor.putString("loginID", loginID);
             editor.putString("loginPW", loginPW);
             editor.putBoolean("LoginChecked", true);
            
             editor.commit();
         } else {
             // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
             settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
              editor.clear(); // 모든 정보 삭제
             editor.commit();
         }
       
    }

    public void deviceDismatch_showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("등록단말 불일치");
                builder.setMessage("최초 등록된 단말기가 아닙니다.\n" + "관리자에게 문의하여 단말기 변경신청을 하시기 바랍니다.") 
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });                    
                AlertDialog alert = builder.create();
                alert.show();              
            }
        });
    }

    public void showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("로그인 에러");
                builder.setMessage("로그인 정보가 일치하지 않습니다.") 
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });                    
                AlertDialog alert = builder.create();
                alert.show();              
            }
        });
    }
   
    private void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.") 
               .setCancelable(false)
               .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       finish(); // exit
                           //application 프로세스를 강제 종료
                           android.os.Process.killProcess(android.os.Process.myPid() );
                   }
               });                    
        AlertDialog alert = builder.create();
        alert.show();        
       
    }
   
    private boolean NetworkConnection() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
        boolean isMobileAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();
        boolean isMobileConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();
        boolean isWifiAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable();
        boolean isWifiConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
       
        if ((isWifiAvailable && isWifiConnect) || (isMobileAvailable && isMobileConnect)){
            return true;
        }else{
            return false;
        }
    }
   
    // Back 버튼을 누르면 어플 종료여부 확인 처리
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    moveTaskToBack(true); // 본Activity finish후 다른 Activity가 뜨는 걸 방지.
                    finish();
                    //application 프로세스를 강제 종료
                    android.os.Process.killProcess(android.os.Process.myPid() );
                }
        }).setNegativeButton( "No", null ).show();
     
        return true;
      }
       
      return super.onKeyDown(keyCode, event);
     }
       
    @Override
    protected void onResume()
    {
        super.onResume();
        CookieSyncManager.getInstance().startSync();
    }
   
    @Override
    protected void onPause()
    {
        super.onPause();
        CookieSyncManager.getInstance().stopSync(); // 동기화 종료
    }
}



===== MainActivity.java =======

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private ListView mListView = null;
    private ListViewAdapter mAdapter = null;

    // 서버 정보를 파싱하기 위한 변수 선언
    String myJSON; 
    private static final String TAG_RESULTS="result"; 
    private static final String TAG_UID = "uid"; 
    private static final String TAG_NAME = "name"; 
    private static final String TAG_Mobile ="mobile"; 
 
    JSONArray peoples = null;   

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        mListView = (ListView) findViewById(R.id.listView); 
        mAdapter = new ListViewAdapter(this);       
       
        // 서버에 있는 정보를 읽어다가 mAdapter.addItem 에 추가하는 과정
        getDbData(Value.IPADDRESS + "/mobile/get_json.php");
       
        mListView.setAdapter(mAdapter);
       
        TextView searchView = (TextView) findViewById(R.id.SearchView);
        TextView phonebookView = (TextView) findViewById(R.id.PhonebookView);       

        searchView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "검색화면으로 이동합니다", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(MainActivity.this, Search_Item.class);
                startActivity(intent);
            }           
        });
       
        phonebookView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {               
                Toast.makeText(getApplicationContext(), "내 폰의 전화번호부를 가져옵니다", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(MainActivity.this, PhonebookActivity.class);
                startActivity(intent);
            }
           
        });

    }

   
    private void getDbData(String string) {
        class GetDataJSON extends AsyncTask<String, Void, String>{ 
           
            @Override 
            protected String doInBackground(String... params) { 
 
                String uri = params[0];
               
 
                BufferedReader bufferedReader = null; 
                try { 
                    URL url = new URL(uri); 
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    // 세션 쿠키 전달
                    String cookieString = CookieManager.getInstance().getCookie(Value.IPADDRESS);
                   
                    StringBuilder sb = new StringBuilder(); 
 
                    if(conn != null){ // 연결되었으면
                        //add request header
                        conn.setRequestMethod("POST");
                        conn.setRequestProperty("USER-AGENT", "Mozilla/5.0");
                        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                        conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
                        if (cookieString != null) {
                             conn.setRequestProperty("Cookie", cookieString);
                             Log.e("PHP_getCookie", cookieString);
                         }
                        conn.setConnectTimeout(10000);
                        conn.setReadTimeout(10000);
                        conn.setUseCaches(false);
                        conn.setDefaultUseCaches(false);
                        conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션
                        //conn.setDoInput(true);
                       
                        int responseCode = conn.getResponseCode();
                        System.out.println("GET Response Code : " + responseCode);       
                        if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                            bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                            String json;
                            while((json = bufferedReader.readLine())!= null){
                                sb.append(json + "\n");
                            }     
                        }
                        bufferedReader.close();
                    }
                    return sb.toString().trim(); 
 
                } catch(Exception e){ 
                    return new String("Exception: " + e.getMessage());
                }  
 
            } 
 
            protected void onPostExecute(String result){ 
                myJSON=result; 
                showList(); 
            }           
        } 
       
        GetDataJSON g = new GetDataJSON(); 
        g.execute(string);                
       
    }
   
    protected void showList() {
        // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력
        try { 
            JSONObject jsonObj = new JSONObject(myJSON); 
            peoples = jsonObj.getJSONArray(TAG_RESULTS);
           
            for(int i=0;i<peoples.length();i++){ 
                JSONObject c = peoples.getJSONObject(i); 
                String uid = c.getString(TAG_UID); 
                String name = c.getString(TAG_NAME); 
                String mobile = c.getString(TAG_Mobile); 
                Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
               
                mAdapter.addItem(myIcon,uid,name,mobile);
            }
           
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });
           
        } catch (JSONException e) { 
            e.printStackTrace(); 
        }

    } 

    class ViewHolder {
         public LinearLayout child_layout;
         public ImageView mImage;     
         public Button childListBtn;     
         public TextView name;
         public TextView mobile;
    }

    private class ListViewAdapter extends BaseAdapter {

        private Context mContext = null;
        private ArrayList<ListData> mListData = new ArrayList<ListData>();
       
        public ListViewAdapter(Context mContext) {
            super();
            this.mContext = mContext;
        }
       
        @Override
        public int getCount() {       
            return mListData.size();
        }

        @Override
        public Object getItem(int position) {       
            return mListData.get(position);
        }

        @Override
        public long getItemId(int position) {       
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            View view = convertView;
            if (view == null) {
                viewHolder = new ViewHolder();
         
                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.list_item, parent, false);
               
                view.setBackgroundColor(0x00FFFFFF);
                view.invalidate();
         
                viewHolder.child_layout = (LinearLayout) view.findViewById(R.id.child_layout);
                viewHolder.mImage = (ImageView) view.findViewById(R.id.mImage);
                viewHolder.childListBtn = (Button ) view.findViewById(R.id.childListBtn);
                viewHolder.name = (TextView) view.findViewById(R.id.name);
                viewHolder.mobile = (TextView) view.findViewById(R.id.mobile);
         
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }
           
            final ListData mData = mListData.get(position);
           
            if (mData.mImage != null) {
                viewHolder.mImage.setVisibility(View.VISIBLE);
                viewHolder.mImage.setImageDrawable(mData.mImage);
            } else {
                viewHolder.mImage.setVisibility(View.GONE);
            }
           
            viewHolder.childListBtn.setText(mData.uid);
            viewHolder.name.setText(mData.name);
            viewHolder.mobile.setText(mData.mobile);
           
            viewHolder.childListBtn.setOnClickListener(new Button.OnClickListener(){

                @Override
                public void onClick(View v) {
                   
                    Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    vibe.vibrate(50);
                   
                    AlertDialog showdialog = new AlertDialog.Builder(MainActivity.this)
                            .setTitle(mData.name)
                            .setMessage(mData.mobile + " 통화하시겠습니까?")
                            .setPositiveButton("예",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {

                                            Intent i = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+ mData.mobile));
                                            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                            startActivity(i);
                                        }

                                    })
                            .setNegativeButton(
                                    "아니오",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {
                                            dialog.dismiss();
                                        }
                                    }).create();
                    showdialog.show();
                }
               
            });
           
            return view;
        }

        public void addItem(Drawable icon, String uid, String name, String mobile){
            ListData addInfo = null;
            addInfo = new ListData();
            addInfo.mImage = icon;
            addInfo.uid = uid;
            addInfo.name = name;
            addInfo.mobile = mobile;
                    
            mListData.add(addInfo);
        }
       
        public void remove(int position){
            mListData.remove(position);
            mAdapter.notifyDataSetChanged();
        }
   
   
    }
   
    // Back 버튼을 누르면 어플 종료여부 확인 처리
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    moveTaskToBack(true); // 본 Activity finish후 다른 Activity가 뜨는 걸 방지.
                    finish();
                    //application 프로세스를 강제 종료
                    android.os.Process.killProcess(android.os.Process.myPid() );
                }
        }).setNegativeButton( "No", null ).show();
     
        return true;
      }
       
      return super.onKeyDown(keyCode, event);
     }

}


지금까지 단계적으로 배우면서 작성한 코드에 대한 정리본인 셈이라고 볼 수도 있다.

기존 포스팅 자료에 비해 내용이 점점 보강되어간다.


폰에 있는 전화번호 읽어오기는 생각보다 처리하는 걸 고민해야 하는 거 같다.

알아야 할 지식도 더 많은 거 같아서 자바 책을 좀 들여다보고 있다.

블로그 이미지

Link2Me

,
728x90

MySQLi 로 DB를 접속하는 경우에 로그인이 넘어가지 않는 문제점에 부딪쳤다.


<?php
include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
$conn=new MySQLiDbClass();
$DB_CONNECT = $conn->isConnectDb($DB);

require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';
$c=new LoginClass();

$result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
if($result > 0 ) {
    $_SESSION['userID'] = $loginID;
    $_SESSION['userPW'] = md5($loginPW);
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
    echo 'Login Success';
} else if($result == '0') {
    echo 'Login Fail';
} else {
    echo 'Phone Dismatch';
}
?>


로그인 함수를 객체를 생성하여 처리하면 화면이 제대로 넘어간다.

그런데 킴스큐에서 제공하는 함수를 이용하면 화면이 넘어가지 않고 에러 메시지를 뿌린다.

<?php
include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/function/db.mysql.func.php';
$DB_CONNECT = isConnectDb($DB);

require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';
$c=new LoginClass();

$result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
if($result > 0 ) {
    $_SESSION['userID'] = $loginID;
    $_SESSION['userPW'] = md5($loginPW);
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
    echo 'Login Success';
} else if($result == '0') {
    echo 'Login Fail';
} else {
    echo 'Phone Dismatch';
}
?>


ViewRootImpl
sendUserActionEvent() mView == null
ViewPostImeInputStage ACTION_DOWN


왜 이런 현상이 발생하는 걸까?

MySQL 접속하는 경우를 테스트해보면 킴스큐RB 함수를 이용해도 아무런 문제없이 잘 넘어간다.

Web 상에서 접속하면 MySQL, MySQLi 모두 정상적으로 접속이 잘 된다.

안드로이드폰(삼성 갤럭시 S4)에서만 발생하는 증상인가?

심지어 로그를 찍어보면 Response : Login Success 라고 정상적으로 접속이 되었다고 나오는데도 불구하고 다음 Activity 로 넘어가질 못한다.


구글링을 해보니

I also encuntered the same in S4. I've tested the app in Galaxy Grand , HTC , Sony Experia but got only in s4. You can ignore it as its not related to your app.

이런 메시지도 나온다.


삼성 갤럭시2로 테스트를 해보니 역시 마찬가지로 메시지를 뿌린다.

MySQLi 를 사용할 경우에는 객체를 생성해서 사용해야 하나 보다.




블로그 이미지

Link2Me

,
728x90

안드로이드와 PHP 연동하여 MySQL DB에서 결과를 회신받아 안드로이드에서 보는 방법이다.

안드로이드 폰에서 검색어를 입력하면 서버에서 해당 검색어에 대한 결과를 돌려주는 방식이다.

인터넷에 올라온 자료들은 참조하여 테스트를 하다보니 결과가 나오질 않는다.
뭐가 잘못된 것인지 찾기 위해 각종 게시글을 참조하면서 하나 하나 잘못된 점을 찾아나갔다.
틀린 점이 하나 발견되었다.
conn.setDoInput(true); // 서버로부터 응답 헤더와 메시지를 읽어들이겠다는 옵션
이 코드가 추가되면 결과값이 없는지 화면에 아무것도 없다. 이 한줄의 쓰임새는 아직 파악을 못한 상태다.


==== get_json.php ======

<?php
    include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/xmlClass.php';

    $conn = new MySQLDbClass(); // DB 함수
    $DB_CONNECT = $conn->isConnectDb($DB);

    // 화면에 출력할 칼럼 발췌
    $sql = "select uid,name,mobile from Person ";
    $result = mysql_query($sql);

    // 1. JSON 데이터 생성방법
    $c = new jsonClass();
    echo $c->JSONEncode($result,'result');
?>


참고

class jsonClass {
    function JSONEncode($result,$varname){
        $R = array(); // 결과 담을 변수 생성
        while($row = mysql_fetch_object($result)) { 
            array_push($R, $row);
        }
       
        return json_encode(array($varname=>$R)); //배열-문자열등을 json형식의 '문자열'로 변환
    }
// end of Class


이제 검색 기능을 추가했다.

안드로이드에서 검색 명령어를 입력하면 PHP에서 검색명령어를 인식해야 한다.

GET 방식과 POST 방식 모두 입력값을 인식하도록 $_REQUEST['search'] 를 적어줬다.

Where 조건절에 쿼리문은 인덱스를 타지 않는 LIKE 를 적용했다.

값이 제대로 넘어오는지만 확인하기 위한 목적이기 때문에....


<?php

if(!isset($_REQUEST['search']) || empty($_REQUEST['search'])){

echo '비정상접속';

exit;

}


$query = $_REQUEST['search'];

include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/xmlClass.php';

$conn = new MySQLDbClass(); // DB 함수
$DB_CONNECT = $conn->isConnectDb($DB);

// 화면에 출력할 칼럼 발췌
$sql = "select uid,name,mobile from Person ";
if(!empty($query)) {
    $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'";
}
$result = mysql_query($sql);

// 1. JSON 데이터 생성방법
$c = new jsonClass();
echo $c->JSONEncode($result,'result');
?>


안드로이드 코드 부문

===== MainActivity.java 추가 사항 =====

TextView searchView = (TextView) findViewById(R.id.SearchView);
searchView.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "검색화면으로 이동합니다", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(MainActivity.this, Search_Item.class);
        startActivity(intent);
    }           
});


===== Search_Item.java =====

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class Search_Item extends Activity {
   
    static String getEdit;
    EditText inputSearch;
    Button btnSearch;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_search);
       
        final Button btn = (Button) findViewById(R.id.back_btn);
        btn.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });

        Button homeBtn = (Button) findViewById(R.id.home_btn);
        homeBtn.setOnClickListener(new Button.OnClickListener() {

            public void onClick(View v) {
                Intent mn = new Intent(Search_Item.this, MainActivity.class);
                mn.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(mn);
            }
        });

        // title set
        TextView title = (TextView) this.findViewById(R.id.navibar_text); // title
        title.setText("연락처 검색");

        inputSearch = (EditText) findViewById(R.id.list_search_edit);       
        btnSearch = (Button) findViewById(R.id.list_search_btn);
        btnSearch.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                searchData();               
            }           
           
        });
    }
   
    private void searchData() {
        getEdit = inputSearch.getText().toString().trim();
        if(getEdit.equals("") ){
            Toast.makeText(Search_Item.this, "검색어를 입력하세요!", Toast.LENGTH_SHORT).show();
        } else {
            // 검색어 값을 전달
            Intent intent = new Intent(Search_Item.this, SearchActivity.class);
            intent.putExtra("search", getEdit);
            startActivity(intent);
            finish();
        }
       
    }
}


===== SearchActivity.java =====

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class SearchActivity extends Activity {
   
    private ListView sListView = null;
    private ListViewAdapter sAdapter = null;

    // 서버 정보를 파싱하기 위한 변수 선언
    String searchJSON; 
    private static final String TAG_RESULTS="result"; 
    private static final String TAG_UID = "uid"; 
    private static final String TAG_NAME = "name"; 
    private static final String TAG_Mobile ="mobile"; 
 
    JSONArray peoples = null; 
    String squery = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_result);
       
        // ActionBar 제거하기  --> AnroidManifest.xml 파일을 수정했음. 화면이 까맣게 나오는 현상 때문에
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        final Button btn = (Button) findViewById(R.id.back_btn);
        btn.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });

        Button homeBtn = (Button) findViewById(R.id.home_btn);
        homeBtn.setOnClickListener(new Button.OnClickListener() {

            public void onClick(View v) {
                Intent mn = new Intent(SearchActivity.this, MainActivity.class);
                mn.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(mn);
            }
        });

        // title set
        TextView title = (TextView) this.findViewById(R.id.navibar_text); // title
        title.setText("검색 결과");
       
        sListView = (ListView) findViewById(R.id.list_search); 
        sAdapter = new ListViewAdapter(this);       

        // 이전 Activity에서 전달한 값 받기
        Intent intent = getIntent();
        squery = intent.getExtras().getString("search");

        // 서버에 있는 정보를 읽어다가 mAdapter.addItem 에 추가하는 과정
        String url ="http://IP주소/mobile/get_json.php";
        getDbData(url);
       
        sListView.setAdapter(sAdapter);

    }
   
    private void getDbData(String string) {
        class GetDataJSON extends AsyncTask<String, Void, String>{ 

            @Override 
            protected String doInBackground(String... params) { 
 
                String uri = params[0];
 
                BufferedReader bufferedReader = null; 
                try { 
                    URL url = new URL(uri); 
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    String post_data = "search=" + Uri.encode(squery);

                    StringBuilder sb = new StringBuilder(); 
 
                    if(conn != null){ // 연결되었으면
                        conn.setConnectTimeout(10000); // milliseconds 연결 타임아웃시간

                        //add request header
                        conn.setUseCaches(false);
                        conn.setDefaultUseCaches(false);
                        //conn.setDoInput(true); // 서버로부터 응답 헤더와 메시지를 읽어들이겠다는 옵션
                        // 이 코드를 활성화하면 응답된 내용이 전혀 없다. 비활성화하면 정상이다.
                        conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션

                        conn.setRequestMethod("POST");

                        // 서버에게 웹에서 <Form>으로 값이 넘어온 것과 같은 방식으로 처리하라고 알려준다

                        conn.setRequestProperty("USER-AGENT", "Mozilla/5.0");
                        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                        conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
 
                        // Send POST request (서버로 값 전송)
                        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
                        wr.writeBytes(
post_data);
                        wr.flush();
                        wr.close();
                       
                        int responseCode = conn.getResponseCode();
                        System.out.println("GET Response Code : " + responseCode); //
200 : 정상연결                      
                        if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                            bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                            String json;
                            while((json = bufferedReader.readLine())!= null){ 
// 라인단위로 읽는다

                               sb.append(json + "\n"); // View에 표시하기 위해 라인 구분자 추가

                            }     
                        }
                        bufferedReader.close();
                    }
                    return sb.toString().trim(); 
 
                } catch(Exception e){ 
                    return new String("Exception: " + e.getMessage());
                }  
 
            } 
 
            protected void onPostExecute(String result){ 
                searchJSON=result; 
                showList(); 
            }           
        } 
       
        GetDataJSON g = new GetDataJSON(); 
        g.execute(string);                
       
    }
   
    protected void showList() {
        // 서버에서 읽어온 정보를 sAdapter 에 저장하고 화면에 출력
        try { 
            JSONObject jsonObj = new JSONObject(searchJSON); 
            peoples = jsonObj.getJSONArray(TAG_RESULTS);
           
            for(int i=0;i<peoples.length();i++){ 
                JSONObject c = peoples.getJSONObject(i); 
                String uid = c.getString(TAG_UID); 
                String name = c.getString(TAG_NAME); 
                String mobile = c.getString(TAG_Mobile); 
                Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
               
                sAdapter.addItem(myIcon,uid,name,mobile);
            }         


            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    sAdapter.notifyDataSetChanged();
                }
            });
           
        } catch (JSONException e) { 
            e.printStackTrace(); 
        }

    } 

    class ViewHolder {
         public LinearLayout child_slayout;
         public ImageView mImage;     
         public Button ListBtn;     
         public TextView name;
         public TextView mobile;
    }

    private class ListViewAdapter extends BaseAdapter {

        private Context sContext = null;
        private ArrayList<ListData> sListData = new ArrayList<ListData>();
       
        public ListViewAdapter(Context mContext) {
            super();
            this.sContext = mContext;
        }
       
        @Override
        public int getCount() {       
            return sListData.size();
        }

        @Override
        public Object getItem(int position) {       
            return sListData.get(position);
        }

        @Override
        public long getItemId(int position) {       
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            View view = convertView;
            if (view == null) {
                viewHolder = new ViewHolder();
         
                LayoutInflater inflater = (LayoutInflater) sContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.search_item, parent, false);
               
                view.setBackgroundColor(0x00FFFFFF);
                view.invalidate();
         
                viewHolder.child_slayout = (LinearLayout) view.findViewById(R.id.child_searchlayout);
                viewHolder.mImage = (ImageView) view.findViewById(R.id.mImage);
                viewHolder.ListBtn = (Button ) view.findViewById(R.id.search_ListBtn);
                viewHolder.name = (TextView) view.findViewById(R.id.search_name);
                viewHolder.mobile = (TextView) view.findViewById(R.id.search_mobile);
         
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }
           
            final ListData sData = sListData.get(position);
           
            if (sData.mImage != null) {
                viewHolder.mImage.setVisibility(View.VISIBLE);
                viewHolder.mImage.setImageDrawable(sData.mImage);
            } else {
                viewHolder.mImage.setVisibility(View.GONE);
            }
           
            viewHolder.ListBtn.setText(sData.uid);
            viewHolder.name.setText(sData.name);
            viewHolder.mobile.setText(sData.mobile);
           
            viewHolder.ListBtn.setOnClickListener(new Button.OnClickListener(){

                @Override
                public void onClick(View v) {
                   
                    Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    vibe.vibrate(50);
                   
                    AlertDialog showdialog = new AlertDialog.Builder(SearchActivity.this)
                            .setTitle(sData.name)
                            .setMessage(sData.mobile + " 통화하시겠습니까?")
                            .setPositiveButton("예",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {

                                            Intent i = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+ sData.mobile));
                                            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                            startActivity(i);
                                        }

                                    })
                            .setNegativeButton(
                                    "아니오",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {
                                            dialog.dismiss();
                                        }
                                    }).create();
                    showdialog.show();
                }
               
            });
           
            return view;
        }

        public void addItem(Drawable icon, String uid, String name, String mobile){
            ListData addInfo = null;
            addInfo = new ListData();
            addInfo.mImage = icon;
            addInfo.uid = uid;
            addInfo.name = name;
            addInfo.mobile = mobile;
                    
            sListData.add(addInfo);
        }
   
    }

}


===== AndroidManifest.xml =====

아래 순서는 Login.java --> MainActivity.java --> Search_Item.java  --> SearchActivity.java 파일 순으로 실행된다.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.phpmysqljson"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="23" />
   
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.MODIFY_PHONE_READ"/>
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.VIBRATE" />
       
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".Login"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:launchMode="singleTop"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop" />
       
        <activity
            android:name=".Search_Item"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.NoTitleBar" />

        <activity
            android:name=".SearchActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:launchMode="singleTop" />
       
    </application>

</manifest>

블로그 이미지

Link2Me

,
728x90

안드로이드와 PHP 연동하여 MySQL DB에서 결과를 회신받아 안드로이드에서 보는 방법에 대해 포스팅을 했었다.

검색어를 입력하고 전달한 검색어를 통해 결과를 회신받는 방법중 하나인 GET 방식부터 테스트를 해봤다.

인터넷에 있는 자료들은 참조하여 일일이 테스트를 하면서 부족한 부분은 보강하고 완벽하게 동작되는 걸 확인하고 적어둔다.


==== get_json.php ======

<?php
    include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/xmlClass.php';

    $conn = new MySQLDbClass(); // DB 함수
    $DB_CONNECT = $conn->isConnectDb($DB);

    // 화면에 출력할 칼럼 발췌
    $sql = "select uid,name,mobile from Person ";
    $result = mysql_query($sql);

    // 1. JSON 데이터 생성방법
    $c = new jsonClass();
    echo $c->JSONEncode($result,'result');
?>

이제 검색 기능을 추가했다.

안드로이드에서 검색 명령어를 입력하면 PHP에서 검색명령어를 인식해야 한다.

GET 방식과 POST 방식 모두 입력값을 인식하도록 $_REQUEST['search'] 를 적어줬다.

Where 조건절에 쿼리문은 인덱스를 타지 않는 LIKE 를 적용했다.

값이 제대로 넘어오는지만 확인하기 위한 목적이기 때문에....


<?php
$query = $_REQUEST['search'];

include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/xmlClass.php';

$conn = new MySQLDbClass(); // DB 함수
$DB_CONNECT = $conn->isConnectDb($DB);

// 화면에 출력할 칼럼 발췌
$sql = "select uid,name,mobile from Person ";
if(!empty($query)) {
    $sql .= "where name LIKE '%".$query."%' or mobile LIKE '%".$query."%'";
}
$result = mysql_query($sql);

// 1. JSON 데이터 생성방법
$c = new jsonClass();
echo $c->JSONEncode($result,'result');
?>



안드로이드 코드 부문

===== MainActivity.java 추가 사항 =====

TextView searchView = (TextView) findViewById(R.id.SearchView);
searchView.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "검색화면으로 이동합니다", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(MainActivity.this, Search_Item.class);
        startActivity(intent);
    }           
});


===== Search_Item.java =====

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class Search_Item extends Activity {
   
    static String getEdit;
    EditText inputSearch;
    Button btnSearch;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_search);
       
        final Button btn = (Button) findViewById(R.id.back_btn);
        btn.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });

        Button homeBtn = (Button) findViewById(R.id.home_btn);
        homeBtn.setOnClickListener(new Button.OnClickListener() {

            public void onClick(View v) {
                Intent mn = new Intent(Search_Item.this, MainActivity.class);
                mn.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(mn);
            }
        });

        // title set
        TextView title = (TextView) this.findViewById(R.id.navibar_text); // title
        title.setText("연락처 검색");

        inputSearch = (EditText) findViewById(R.id.list_search_edit);       
        btnSearch = (Button) findViewById(R.id.list_search_btn);
        btnSearch.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                searchData();               
            }           
           
        });
    }
   
    private void searchData() {
        getEdit = inputSearch.getText().toString().trim();
        if(getEdit.equals("") ){
            Toast.makeText(Search_Item.this, "검색어를 입력하세요!", Toast.LENGTH_SHORT).show();
        } else {
            // 검색어 값을 전달
            Intent intent = new Intent(Search_Item.this, SearchActivity.class);
            intent.putExtra("search", getEdit);
            startActivity(intent);
            finish();
        }
       
    }
}


===== SearchActivity.java =====

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class SearchActivity extends Activity {
   
    private ListView sListView = null;
    private ListViewAdapter sAdapter = null;

    // 서버 정보를 파싱하기 위한 변수 선언
    String searchJSON; 
    private static final String TAG_RESULTS="result"; 
    private static final String TAG_UID = "uid"; 
    private static final String TAG_NAME = "name"; 
    private static final String TAG_Mobile ="mobile"; 
 
    JSONArray peoples = null; 
    String squery;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_result);
       
        // ActionBar 제거하기 

        // android:theme="@android:style/Theme.NoTitleBar" 에 추가했기 때문에 반드시 비활성화해야 함
        //ActionBar actionbar = getActionBar();
        //actionbar.hide();

        final Button btn = (Button) findViewById(R.id.back_btn);
        btn.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });

        Button homeBtn = (Button) findViewById(R.id.home_btn);
        homeBtn.setOnClickListener(new Button.OnClickListener() {

            public void onClick(View v) {
                Intent mn = new Intent(SearchActivity.this, MainActivity.class);
                mn.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(mn);
            }
        });

        // title set
        TextView title = (TextView) this.findViewById(R.id.navibar_text); // title
        title.setText("검색 결과");
       
        sListView = (ListView) findViewById(R.id.list_search); 
        sAdapter = new ListViewAdapter(this);       

        // 이전 Activity에서 전달한 값 받기
        Intent intent = getIntent();
        squery = intent.getExtras().getString("search");

        // 서버에 있는 정보를 읽어다가 mAdapter.addItem 에 추가하는 과정
        String url ="http://IP주소/mobile/get_json.php?search=" + Uri.encode(squery); // 한글검색도 처리
        getDbData(url);
       
        sListView.setAdapter(sAdapter);

    }
   
    private void getDbData(String string) {
        class GetDataJSON extends AsyncTask<String, Void, String>{ 

            @Override 
            protected String doInBackground(String... params) { 
 
                String uri = params[0]; 
 
                BufferedReader bufferedReader = null; 
                try { 
                    URL url = new URL(uri); 
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
                    StringBuilder sb = new StringBuilder(); 
 
                    if(conn != null){ // 연결되었으면
                        conn.setConnectTimeout(10000); // milliseconds 연결 타임아웃시간
                        conn.setUseCaches(false);
                        conn.setDefaultUseCaches(false);

                        int responseCode = conn.getResponseCode();
                        System.out.println("GET Response Code : " + responseCode);                       
                        if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                            bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                            String json;
                            while((json = bufferedReader.readLine())!= null){
                                sb.append(json + "\n");
                            }     
                        }
                        bufferedReader.close();
                    }
                    return sb.toString().trim(); 
 
                } catch(Exception e){ 
                    return new String("Exception: " + e.getMessage());
                }  
 
            } 
 
            protected void onPostExecute(String result){ 
                searchJSON=result; 
                showList(); 
            }           
        } 
       
        GetDataJSON g = new GetDataJSON(); 
        g.execute(string);                
       
    }
   
    protected void showList() {
        // 서버에서 읽어온 정보를 sAdapter 에 저장하고 화면에 출력
        try { 
            JSONObject jsonObj = new JSONObject(searchJSON); 
            peoples = jsonObj.getJSONArray(TAG_RESULTS);
           
            for(int i=0;i<peoples.length();i++){ 
                JSONObject c = peoples.getJSONObject(i); 
                String uid = c.getString(TAG_UID); 
                String name = c.getString(TAG_NAME); 
                String mobile = c.getString(TAG_Mobile); 
                Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
               
                sAdapter.addItem(myIcon,uid,name,mobile);
            }         


            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    sAdapter.notifyDataSetChanged();
                }
            });
           
        } catch (JSONException e) { 
            e.printStackTrace(); 
        }

    } 

    class ViewHolder {
         public LinearLayout child_slayout;
         public ImageView mImage;     
         public Button ListBtn;     
         public TextView name;
         public TextView mobile;
    }

    private class ListViewAdapter extends BaseAdapter {

        private Context sContext = null;
        private ArrayList<ListData> sListData = new ArrayList<ListData>();
       
        public ListViewAdapter(Context mContext) {
            super();
            this.sContext = mContext;
        }
       
        @Override
        public int getCount() {       
            return sListData.size();
        }

        @Override
        public Object getItem(int position) {       
            return sListData.get(position);
        }

        @Override
        public long getItemId(int position) {       
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            View view = convertView;
            if (view == null) {
                viewHolder = new ViewHolder();
         
                LayoutInflater inflater = (LayoutInflater) sContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.search_item, parent, false);
               
                view.setBackgroundColor(0x00FFFFFF);
                view.invalidate();
         
                viewHolder.child_slayout = (LinearLayout) view.findViewById(R.id.child_searchlayout);
                viewHolder.mImage = (ImageView) view.findViewById(R.id.mImage);
                viewHolder.ListBtn = (Button ) view.findViewById(R.id.search_ListBtn);
                viewHolder.name = (TextView) view.findViewById(R.id.search_name);
                viewHolder.mobile = (TextView) view.findViewById(R.id.search_mobile);
         
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }
           
            final ListData sData = sListData.get(position);
           
            if (sData.mImage != null) {
                viewHolder.mImage.setVisibility(View.VISIBLE);
                viewHolder.mImage.setImageDrawable(sData.mImage);
            } else {
                viewHolder.mImage.setVisibility(View.GONE);
            }
           
            viewHolder.ListBtn.setText(sData.uid);
            viewHolder.name.setText(sData.name);
            viewHolder.mobile.setText(sData.mobile);
           
            viewHolder.ListBtn.setOnClickListener(new Button.OnClickListener(){

                @Override
                public void onClick(View v) {
                    Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    vibe.vibrate(50);
                   
                    AlertDialog showdialog = new AlertDialog.Builder(SearchActivity.this)
                            .setTitle(sData.name)
                            .setMessage(sData.mobile + " 통화하시겠습니까?")
                            .setPositiveButton("예",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {

                                            Intent i = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+ sData.mobile));
                                            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                            startActivity(i);
                                        }

                                    })
                            .setNegativeButton(
                                    "아니오",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {
                                            dialog.dismiss();
                                        }
                                    }).create();
                    showdialog.show();
                }
               
            });
           
            return view;
        }

        public void addItem(Drawable icon, String uid, String name, String mobile){
            ListData addInfo = null;
            addInfo = new ListData();
            addInfo.mImage = icon;
            addInfo.uid = uid;
            addInfo.name = name;
            addInfo.mobile = mobile;
                    
            sListData.add(addInfo);
        }
   
    }
}


===== AndroidManifest.xml ====

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.phpmysqljson"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="23" />
   
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.MODIFY_PHONE_READ"/>
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.VIBRATE" />
       
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".Login"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:launchMode="singleTop"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop" />
       
        <activity
            android:name=".Search_Item"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.NoTitleBar" />

        <activity
            android:name=".SearchActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.NoTitleBar" />
       
    </application>

</manifest>


다음에는 POST 로 값을 넘기는 방법에 대해 테스트를 해보련다.

블로그 이미지

Link2Me

,
728x90

안드로이드에서 PHP 서버에 등록한 단말이 없으면 등록하고, 등록한 단말이 있으면 맞는 단말인지 체크하여 로그인하는 기능을 추가했다.


1. 안드로이드 Login.java

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class Login extends Activity {

    String getDeviceID; // 스마트기기의 장치 고유값
    ProgressDialog dialog = null;
    EditText etId;
    EditText etPw;
   
    String loginID;
    String loginPW;
    CheckBox autologin;
    Boolean loginChecked;
    List<NameValuePair> params;
    public SharedPreferences settings;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
       
        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        // 네트워크 연결상태 체크
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }
       
        etId = (EditText) findViewById(R.id.login_id_edit);
        etPw = (EditText) findViewById(R.id.login_pw_edit);   
        autologin = (CheckBox) findViewById(R.id.autologinchk);
       
        settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
        loginChecked = settings.getBoolean("LoginChecked", false);
        if (loginChecked) {
            etId.setText(settings.getString("loginID", ""));
            etPw.setText(settings.getString("loginPW", ""));
            autologin.setChecked(true);
        }
       
        if(!settings.getString("loginID", "").equals("")) etPw.requestFocus();
       
        Button submit = (Button) findViewById(R.id.login_btn);
        submit.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                dialog = ProgressDialog.show(Login.this, "", "Validating user...", true);
                 new Thread(new Runnable() {
                        public void run() {
                            login();                         
                        }
                       
                      }).start();                
            }
           
        });
       
    }

    void login() {
        try {
            loginID = etId.getText().toString().trim();
            loginPW = etPw.getText().toString().trim();
           
            // 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요
            TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            if (mTelephony.getDeviceId() != null){
                getDeviceID = mTelephony.getDeviceId();  // 스마트폰 기기 정보
            } else {
                getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
            }       
       
            String postURL = "http://IP주소/loginChk.php";
            HttpPost httppost = new HttpPost(postURL);
           
            // 전달할 인자들
            params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("loginID", loginID));
            params.add(new BasicNameValuePair("loginPW", loginPW));
            params.add(new BasicNameValuePair("deviceID", getDeviceID));
           
            UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
            httppost.setEntity(ent);

            HttpClient httpclient = new DefaultHttpClient();
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            final String responsePost = httpclient.execute(httppost, responseHandler);

            System.out.println("DeviceID : " + getDeviceID);
            System.out.println("Response : " + responsePost);
            runOnUiThread(new Runnable() {
                public void run() {
                    dialog.dismiss();
                }
            });
           
            if(responsePost.equalsIgnoreCase("Login Success")){ // 로그인 정보 불일치
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(Login.this,"Login Success", Toast.LENGTH_SHORT).show();
                    }
                });
               
                startActivity(new Intent(Login.this, MainActivity.class));
                finish(); // finish()를 호출해서 Activity를 없애줌

            } else if(responsePost.equalsIgnoreCase("Phone Dismatch")){ // 등록된 단말기와 불일치
                deviceDismatch_showAlert();
            } else {
                showAlert();
            }
        } catch(Exception e) {
            dialog.dismiss();
            System.out.println("Exception : " + e.getMessage());
        }
       
    }
   
    public void onStop(){
        // 어플리케이션이 화면에서 사라질때
        super.onStop();       
        // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장     
        if (autologin.isChecked()) {
             settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
            
             editor.putString("loginID", loginID);
             editor.putString("loginPW", loginPW);
             editor.putBoolean("LoginChecked", true);
            
             editor.commit();
         } else {
             // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
             settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
              editor.clear(); // 모든 정보 삭제
             editor.commit();
         }
       
    }

    public void deviceDismatch_showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("등록단말 불일치");
                builder.setMessage("최초 등록된 단말기가 아닙니다.\n" + "관리자에게 문의하여 단말기 변경신청을 하시기 바랍니다.") 
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });                    
                AlertDialog alert = builder.create();
                alert.show();              
            }
        });
    }

    public void showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("로그인 에러");
                builder.setMessage("로그인 정보가 일치하지 않습니다.") 
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });                    
                AlertDialog alert = builder.create();
                alert.show();              
            }
        });
    }
   
    private void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.") 
               .setCancelable(false)
               .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       finish(); // exit
                           //application 프로세스를 강제 종료
                           android.os.Process.killProcess(android.os.Process.myPid() );
                   }
               });                    
        AlertDialog alert = builder.create();
        alert.show();        
       
    }
   
    private boolean NetworkConnection() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
        boolean isMobileAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();
        boolean isMobileConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();
        boolean isWifiAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable();
        boolean isWifiConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
       
        if ((isWifiAvailable && isWifiConnect) || (isMobileAvailable && isMobileConnect)){
            return true;
        }else{
            return false;
        }
    }
   
    // Back 버튼을 누르면 어플 종료여부 확인 처리
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    moveTaskToBack(true); // 본Activity finish후 다른 Activity가 뜨는 걸 방지.
                    finish();
                    //application 프로세스를 강제 종료
                    android.os.Process.killProcess(android.os.Process.myPid() );
                }
        }).setNegativeButton( "No", null ).show();
     
        return true;
      }
       
      return super.onKeyDown(keyCode, event);
     }
       
}


2. PHP 서버 loginCheck.php

<?php
session_start();
if(isset($_POST['loginID']) && !empty($_POST['loginID']) && isset($_POST['loginPW']) && !empty($_POST['loginPW'])) {
    $loginID = trim($_POST['loginID']);
    $loginPW = trim($_POST['loginPW']);
    $deviceID = trim($_POST['deviceID']);

    require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';

    $c=new LoginClass();
    $deviceID = $deviceID ? $deviceID : '';
    if(empty($deviceID)){
        $row = $c->WebUserAuthCheck($loginID,$loginPW);
        if(is_array($row)) {
            if($row['code'] > 0) {
                $_SESSION['userID'] = $row['id'];
                $_SESSION['userPW'] = md5($loginPW);
                $_SESSION['code'] = $row['code'];
                $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
                $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

                echo("<meta http-equiv='Refresh' content='0; URL=mobile/list.php'>");
            }
        }

    } else {
        $result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
        if($result > 0 ) {
            echo 'Login Success';
        } else if($result == 0) {
            echo 'Login Fail';
        } else {
            echo 'Phone Dismatch';
        }
    }
   
} else {
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>


MobileUserAuthCheck($loginID,$loginPW,$deviceID) 함수 내용은 공개하지 않는다.

그냥 로직만 설명하면 ID, PW, deviceID 검사를 해서 deviceID가 없으면 필드(PhoneID)에 추가하고, 등록되어 있으면 현재 폰의 고유장치값과 서버에 등록된 값이 일치하는지 검사하여 틀리면 불일치 메시지를 화면에 뿌린다.

화면에 뿌린 echo 'Phone Dismatch'; 내용을 안드로이드 소스에서 인식하여 해당되는 메시지를 화면에 출력한다.


http://link2me.tistory.com/1032 게시물 내용과 달라진 사항을 비교해보면 도움이 된다.

로그인 처리 부분은 이 정도면 충분한 거 같으니 PHP JSON 데이터 검색하는 루틴을 추가해볼 것이다.


블로그 이미지

Link2Me

,
728x90

안드로이드 어플에서 userID, userPW 정보를 자동 저장해두고 다음부터는 편리하게 접속하는 기능이 뭘까?


이런 자동로그인 처리를 가능하게 해주는 것이 Preference 이다.

간단하게 내 폰 어딘가에 userID, userPW, 자동로그인 체크 정보를 기록하는 것이다.

Preference 저장 장소 : data/data/패키지명/Shared_prefs 라는 곳에 키 - 값 쌍으로 저장된다.

Shared Preference 는 데이터의 저장보다는 UI 상태를 저장하는데 사용된다.

Shared Preference는 데이터의 저장보다는 UI 상태를 저장하는데 사용

출처: http://androidhuman.com/193 [커니의 안드로이드 이야기]


저장하기

 SharedPreferences settings = getSharedPreferences("PrefName", MODE_PRIVATE);
 SharedPreferences.Editor editor =
settings.edit();
 editor.putString(KEY_MY_PREFERENCE, text);
 editor.commit();

 불러오기

 SharedPreferences settings = getSharedPreferences("PrefName", MODE_PRIVATE);
 String text =
settings.getString(KEY_MY_PREFERENCE, "");


getSharedPreferences("settings", Activity.MODE_PRIVATE); // MODE_PRIVATE : 읽기 쓰기 가능

getSharedPreferences("settings", 0); 으로 해도 같은 의미이다.



Preference 정보 저장하는 방법


public SharedPreferences settings; // 정의

String loginID;
String loginPW;
CheckBox autologin;
Boolean loginChecked;
EditText etId;
EditText etPw;

etId = (EditText) findViewById(R.id.login_id_edit);
etPw = (EditText) findViewById(R.id.login_pw_edit);   
autologin = (CheckBox) findViewById(R.id.autologinchk);

loginID = etId.getText().toString().trim();
loginPW = etPw.getText().toString().trim();

public void onStop(){
    // 어플리케이션이 화면에서 사라질때
    super.onStop();       
    // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장     
    if (autologin.isChecked()) {
        settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
       
         editor.putString("loginID", loginID);
         editor.putString("loginPW", loginPW);
         editor.putBoolean("LoginChecked", true);
        
         editor.commit();
    } else {
        // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
        settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.clear(); // 모든 정보 해제
        editor.commit();
    }
}


Preference 정보 가져오는 방법


public SharedPreferences settings;
String loginID;
String loginPW;
CheckBox autologin;
Boolean loginChecked;
EditText etId;
EditText etPw;

etId = (EditText) findViewById(R.id.login_id_edit);
etPw = (EditText) findViewById(R.id.login_pw_edit);   
autologin = (CheckBox) findViewById(R.id.autologinchk);

loginID = etId.getText().toString().trim();
loginPW = etPw.getText().toString().trim();

settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);

// 자동 로그인을 설정했다면 앱이 다시 시작하는 경우에도 입력했던 값이 유지되도록 하기 위한 코드

loginChecked = settings.getBoolean("LoginChecked", false);
if (loginChecked) {
    etId.setText(settings.getString("loginID", ""));
    etPw.setText(settings.getString("loginPW", ""));
    autologin.setChecked(true);
}



참고하면 도움이 될 내용

http://javaexpert.tistory.com/620

블로그 이미지

Link2Me

,
728x90

안드로이드와 PHP 간에 로그인 연동처리 방법을 적어둔다.


1. 안드로이드 단말에서 PHP 서버로 정보를 보낼 때

    - 안드로이드 단말에서 보내는 코드

    String postURL = "http://192.168.1.2/Logincheck.php";

    HttpPost httppost= new HttpPost(postURL);


    // 전달할 인자
    nameValuePairs = new ArrayList<NameValuePair>(2);   
    nameValuePairs.add(new BasicNameValuePair("loginID",et.getText().toString().trim()));
    nameValuePairs.add(new BasicNameValuePair("loginPW",pass.getText().toString().trim()));
    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));


    //Execute HTTP Post Request   
    httpclient=new DefaultHttpClient();
    ResponseHandler<String> responseHandler = new BasicResponseHandler();
    final String response = httpclient.execute(httppost, responseHandler);

    - 안드로이드 단말에서 보낸 정보를 PHP 에서 인식하는 코드 추가

      $userID = $_POST['loginID'];

      $password = $_POST['loginPW'];


2. PHP 서버의 정보를 안드로이드 단말로 가져올 때

    - 허니콤(3.0) 이후로 네트워크 접속시 별도의 스레드로 돌려야 오류가 발생하지 않는다.

      AsyncTask 를 주로 사용한다.



3. Android 소스 코드

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class Login extends Activity {

    String getDeviceID; // 스마트기기의 장치 고유값
    ProgressDialog dialog = null;
    EditText etId;
    EditText etPw;
   
    String loginID;
    String loginPW;
    CheckBox autologin;
    Boolean loginChecked;
    List<NameValuePair> params;
    public SharedPreferences settings;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
       
        // 네트워크 연결상태 체크
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }
       
        etId = (EditText) findViewById(R.id.login_id_edit);
        etPw = (EditText) findViewById(R.id.login_pw_edit);   
        autologin = (CheckBox) findViewById(R.id.autologinchk);
       

       // 폰에 저장된 로그인 정보 가져오기
        settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
        loginChecked = settings.getBoolean("LoginChecked", false);
        if (loginChecked) {
            etId.setText(settings.getString("loginID", ""));
            etPw.setText(settings.getString("loginPW", ""));
            autologin.setChecked(true);
        }
       
        if(!settings.getString("loginID", "").equals("")) etPw.requestFocus();
       
        Button submit = (Button) findViewById(R.id.login_btn);
        submit.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                dialog = ProgressDialog.show(Login.this, "", "Validating user...", true);
                 new Thread(new Runnable() {
                        public void run() {
                            login();                         
                        }
                       
                      }).start();                
            }
           
        });
       
    }

    void login() {
        try {
            loginID = etId.getText().toString().trim();
            loginPW = etPw.getText().toString().trim();
           
            // 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요
            TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            if (mTelephony.getDeviceId() != null){
                getDeviceID = mTelephony.getDeviceId();  // 스마트폰 기기정보
            } else {
                getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
            }       
       
            String postURL = "http://IP주소/loginChk.php";
            HttpPost httppost = new HttpPost(postURL);
           
            // 전달할 인자들
            params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("loginID", loginID));
            params.add(new BasicNameValuePair("loginPW", loginPW));
            params.add(new BasicNameValuePair("deviceID", getDeviceID));
           
            UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
            httppost.setEntity(ent);

            HttpClient httpclient = new DefaultHttpClient();
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            final String responsePost = httpclient.execute(httppost, responseHandler);

            System.out.println("DeviceID : " + getDeviceID);
            System.out.println("Response : " + responsePost);
            runOnUiThread(new Runnable() {
                public void run() {
                    dialog.dismiss();
                }
            });
           
            if(responsePost.equalsIgnoreCase("<meta http-equiv='Refresh' content='0; URL=mobile/list.php'>")){
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(Login.this,"Login Success", Toast.LENGTH_SHORT).show();
                    }
                });
               
                startActivity(new Intent(Login.this, MainActivity.class));
                finish(); // finish()를 호출해서 Activity를 없애줌
            } else {
                showAlert();               
            }
        } catch(Exception e) {
            dialog.dismiss();
            System.out.println("Exception : " + e.getMessage());
        }
       
    }
   
    public void onStop(){
        // 어플리케이션이 화면에서 사라질때
        super.onStop();       
        // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장     
        if (autologin.isChecked()) {
             settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
            
             editor.putString("loginID", loginID);
             editor.putString("loginPW", loginPW);
             editor.putBoolean("LoginChecked", true);
            
             editor.commit();
         } else {
             // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
             settings = getSharedPreferences("settings",    Activity.MODE_PRIVATE);
             SharedPreferences.Editor editor = settings.edit();
              editor.clear(); // 모든 정보 해제
             editor.commit();
         }
       
    }
   
    public void showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("Login Error.");
                builder.setMessage("로그인 정보가 일치하지 않습니다.") 
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });                    
                AlertDialog alert = builder.create();
                alert.show();              
            }
        });
    }

    private void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.") 
               .setCancelable(false)
               .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       finish(); // exit
                           //application 프로세스를 강제 종료
                           android.os.Process.killProcess(android.os.Process.myPid() );
                   }
               });                    
        AlertDialog alert = builder.create();
        alert.show();        
       
    }
   
    private boolean NetworkConnection() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
        boolean isMobileAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();
        boolean isMobileConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();
        boolean isWifiAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable();
        boolean isWifiConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
       
        if ((isWifiAvailable && isWifiConnect) || (isMobileAvailable && isMobileConnect)){
            return true;
        }else{
            return false;
        }
    }
   
}


4. AndroidManifest.xml 파일 수정사항

   - 로그인(Login.java) 한 후, 메인(MainActivity.java) 가 실행되게 하는 순서에 맞게

     AndroidManifest.xml 파일 내용도 수정을 해줘야만 된다.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.phpmysqljson"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="23" />
   
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />
       
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".Login"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:launchMode="singleTop"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop" />
       
    </application>

</manifest>


5. PHP 로그인 코드

<?php
session_start();
if(isset($_POST['loginID']) && !empty($_POST['loginID']) && isset($_POST['loginPW']) && !empty($_POST['loginPW'])) {
    $loginID = trim($_POST['loginID']);
    $loginPW = trim($_POST['loginPW']);

    require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/loginClass.php';

    $c=new LoginClass();
    $row = $c->LoginUserAuthCheck($loginID,$loginPW);
    if(is_array($row)) {
        if($row['code'] > 0) {
            $_SESSION['userID'] = $row['id'];
            $_SESSION['userPW'] = md5($loginPW);
            $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
            $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

            echo("<meta http-equiv='Refresh' content='0; URL=mobile/list.php'>");
        }
    }
} else {
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>



이 로그인 연동처리하는 코드는 http://www.coderzheaven.com/2012/04/22/create-simple-login-form-php-android-connect-php-android/ 자료를 참조했으나 부족한 사항들을 상당히 더 추가했다.

http://www.coderzheaven.com/ 사이트에 좋은 예제들이 많이 있다.

블로그 이미지

Link2Me

,
728x90

AsyncTask

- 클래스 하나에 Thread 와 Handler 의 기능이 콜백 메서드로 모두 존재


AsyncTask 클래스는 Thread 나 Looper 등의 작동 원리를 몰라도 하나의 클래스에서 UI 처리 및 Background 작업들을 쉽게 할 수 있도록 안드로이드에서 제공하는 클래스다.

이 클래스를 활용하면 작업을 백그라운드에서 수행할 수 있으며, 핸들러나 Thread 를 건들 필요 없이, 백그라운드에서 수행한 결과를 UI Thread(Main Thread) 로 보낼 수 있게 된다.

이렇게 백그라운드에서 수행되는 비 동기적인 작업은 세 가지 generic 타입 - Params, Progress, Result -

과 네 가지 스텝 - onPreExecute, doInBackground, onProgressUpdate, onPostExecute 으로 구성된다.


AsyncTask<Params, Progress, Result> 와 같이 제네릭 인수로  3가지 타입을 전달받는데 사용하지 않는 타입은 Void 라고 적으면 된다.

- Params : 실행할 전달한 인수의 타입. 즉 execute(..) 메소드의 파라미터를 통해 넘겨주는 값의 타입을 지정

- Progress : onProgressUpdate()가 호출될 때 넘겨받을 인자를 뜻한다.

- Result : 작업의 결과로 리턴될 타입 (onPostExecute()가 호출될 때넘겨받을 인자)



@Override

protected void onPreExecuted() { ... }

 //  Background 작업 시작전에 호출되며 UI 쓰레드에서 실행된다.

      계산을 위한 초기화나 프로그래스 대화상자 등의 작업을 수행한다

@Override

protected String doInBackground(Params... params)  { ... }  // 필수 메소드

 // background 작업을 수행하며 분리된 작업 쓰레드에서 실행된다.

     execute 메소드로 전달한 작업거리가 params 인수로 전달되는데 여러 개의 인수를 전달 할 수 있으므로 배열 타입으로 되어있다. 하나의 인수만 필요하다면 params[0] 만을 사용하면 된다.  (가변인자는 JDK 5.0 부터 지원)

작업중에 publishProgress 메소드르 호출하여 작업 경과를 UI Thread로 보고 할 수 있다.

그리고 최종 작업된 결과를 Result 타입으로 반환하며, 결과 값은 onPostExcute() 로 전달된다.

이 쓰레드 안에서 UI를 조작하면 에러가 발생한다.


@Override

 protected void onPostExecute(String result) { .... }

//이 메소드는 doInBackground() 메소드가 완료된 후에 호출된다.
// doInBackground() 메소드 작업의 결과가 return 되어 이 메소드에 전달된다.

// 여기에서 UI Thread 로 보낼 UI 조작을 할 수 있다.


AsyncTask를 사용해서 스케줄링 할 수 있는 작업 수의 제한이 있고, 몇 초 정도의 짧은 작업에서만 이상적으로 동작한다는 한계가 있다.
또한, 안드로이드의 버전별로 병렬 처리 동작이 다르므로 허니콤 이후 버전에서 멀티 스레드로 병렬적인 동작을 원한다면 AsyncTask를 실행할 때 AsyncTask.THREAD_POOL_EXECUTOR 스케줄러를 지정해야 한다.




(Example)


private class DownloadFiles extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {

         // Background 작업 진행상태 표시할 때 호출
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }



사용법

DownloadFiles asyncTask = new DownloadFiles();
asyncTask.execute();

한번에 쓰면
new DownloadFiles().execute();


하지만 cancel 처리도 발생하는 걸 고려한다면
new DownloadFiles().execute(); 로 사용하면 안된다.


DownloadFiles asyncTask = new DownloadFiles();

asyncTask.execute();

asyncTask.cancel(true);

로 해야 된다.


Android Studio 실제 코드 구현시에는 try catch 를 해주어야 에러메시지가 나오지 않는다.

try catch는 Alt + Enter 눌러서 해당 항목 선택해주면 자동으로 코드가 추가된다.



블로그 이미지

Link2Me

,
728x90

책, 블로그를 탐독하고 실행해보면서 보니까 ListView 를 하나의 Activity 단위로 처리하는 것이 편하고 좋은가 보다.


eclipse 에서 자동으로 class를 생성하면 java 파일 단위로 생성된다.

이렇게 생성된 것은 파일이 여러개로 나뉘어져 있어서 가독성 면에서 좀 떨어지는거 같기도 해서 하나의 파일 단위로 생성한 것으로 수정해보기로 했다.


MainActivity.java 파일을 보면 아래 3개의 함수(자바에서는 메소드라고 칭함) 단위로 보이도록 만들어져 있다.


여기서 하단 2개는 지운다.



이제 필요한 함수를 추가한다.



이런 과정으로 만들어지는 거 같다.


서버에 있는 데이터를 가져와서 처리하는 부분에서 계속 개념이 잡히지 않아서 혼란스러웠다.


서버에 있는 정보를 읽어오는 방법은 JSON 연동과 XML 연동으로 처리한다.


MySQLi 접속방식으로 하는데 화면에 아무것도 나오지 않는다. ㅠㅠㅠ

이거 해결하느라고 5시간 소비했다.


알고봤더니 호스팅업체에서 APM 소스 설치해줄때 UTF8 로 기본세팅을 해야 하는데 EUC-KR로 세팅을 해서 변경하는 작업을 해도 UTF8로 인식이 잘 안되어서 생기는 증상이었다.


한글파일 깨짐증상 방지를 위한 MySQL 세팅 참조글 http://link2me.tistory.com/1029


MySQLi 접속하는 함수에서 고려해야 할 사항

class MySQLiDbClass {
    function isConnectDb($db)
    {
        $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
        mysqli_set_charset($conn, "utf8"); // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
        if (mysqli_connect_errno()) {
           printf("Connect failed: %s\n", mysqli_connect_error());
           exit();
        } else {
          return $conn;   
        }
    }
}


JSON 은 UTF8만 인식한다고 한다. 만약 한글이 EUC-KR로 되어 있으면 화면에 아무것도 반환을 하지 않는다.


첨부파일은 테스트에 사용된 파일입니다.

실행해보시고 도움되시면 공감 꾸욱 눌러주세요


MakeActivity.zip


블로그 이미지

Link2Me

,
728x90

2초 이내에 뒤로 가기 버튼은 두번 눌러서 어플을 종료하는 코드다.

나중에 요긴하게 사용할 거 같아서 적어둔다.


====== BackPressCloseHandler.java =======

import android.app.Activity;
import android.widget.Toast;

public class BackPressCloseHandler {
    private long backKeyPressedTime = 0;
    private Toast toast;

    private Activity activity;

    public BackPressCloseHandler(Activity context) {
        this.activity = context;
    }

    public void onBackPressed() {
        // 2초 이내에 뒤로 가기 버튼을 두번 누르면 종료처리
        if (System.currentTimeMillis() > backKeyPressedTime + 2000) {
            backKeyPressedTime = System.currentTimeMillis();
            showGuide();
            return;
        }
        if (System.currentTimeMillis() <= backKeyPressedTime + 2000) {
            activity.finish();
            toast.cancel();
        }
    }

    public void showGuide() {
        toast = Toast.makeText(activity,"\'뒤로\'버튼을 한번 더 누르시면 종료됩니다.", Toast.LENGTH_SHORT);
        toast.show();
    }
}



====== MainActivity.java 에 추가할 내용 ============

네트워크 연결 상태 확인 및 뒤로가기 관련 코드 추가


public class MainActivity extends Activity {
   
    private BackPressCloseHandler backPressCloseHandler;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE); // 타이틀바 없애기

        setContentView(R.layout.activity_main);

        if( !isNetworkConnected(this) ){
            new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setTitle("네트워크 연결 오류").setMessage("네트워크 연결 상태 확인 후 다시 시도해 주십시요.")
            .setPositiveButton("확인", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    finish();
                }
            }).show();
        }
       
        backPressCloseHandler = new BackPressCloseHandler(this);
    }

    // 어플 실행시 연결상태 확인하기
    public boolean isNetworkConnected(Context context){
        boolean isConnected = false;

        ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mobile = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

        if (mobile.isConnected() || wifi.isConnected()){
            isConnected = true;
        } else {
            isConnected = false;
        }
        return isConnected;
    }
   
    @Override
    public void onBackPressed() {
        //super.onBackPressed();
        backPressCloseHandler.onBackPressed();
    }
}


onBackPressed() 함수 대신에 아래 코드를 넣으면 백버튼을 누르면 종료할 것인지 여부를 물어본다.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    finish();
                }
        }).setNegativeButton( "No", null ).show();
     
        return true;
      }
       
      return super.onKeyDown(keyCode, event);
     }
   


// 네트워크 타입 확인
private String getNetworkTypeInfo(){
    ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    return networkInfo.getTypeName();
}

Toast.makeText(this, getNetworkTypeInfo()+" 네트워크에 연결되었습니다",Toast.LENGTH_LONG).show();


블로그 이미지

Link2Me

,
728x90

XML 방식으로 연동하기 위해서 구글링을 하고 테스트를 하고 적어둔다.

아직 ListView 처리 부분을 명확하게 이해하지 못해서 처리내역이 좀 어설퍼 보인다.


PHP 서버 파트는 http://link2me.tistory.com/1022 게시물을 참조하면 된다.

XML 데이터를 생성하는 부분도 추가해서 게시물을 보강했다.



XML 데이터 포멧은 위와 같다.


XML 데이터는 Local 에 있는 경우와 서버에 있는 경우 둘 다 테스트를 해봤다.

그래서 두가지 경우를 모두 하나의 파일로 만든 걸 적어둔다.



XML 파일은 res 폴더 하단에 만들어서 파일을 추가했다.

테스트에 사용한 파일 첨부

xml.zip


1. Activity_main.xml 파일 내용



2. person_item.xml 파일 작성



3. Person_Item.java 파일 생성


Person.java



4. 퍼미션 설정

안드로이드가 어떤 컴포넌트를 시작하려면 먼저 애플리케이션 안에 그 컴포넌트가 존재하는지를 알아야 한다. 애플리케이션안의 모든 컴포넌트들은 AndroidManifest.xml 안에 선언되어야 한다.

AndroidManifest.xml 파일에 선언되지 않은 Activity, Service, Content Provider 는 안드로이드 시스템에서 전혀 알 수가 없다. 결과적으로 실행할 수가 없다.

AndroidManifest.xml 파일에서 퍼머션 추가


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myxml"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="19" />
   
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

     ...
</manifest>


5. MainActivity.java 파일 코드 추가


MainActivity.java


XmlPullParser 는 문서를 순차적으로 읽으며 이벤트를 발생시킨다.

START_DOCUMENT 이벤트는 문서의 시작을
END_DOCUMENT 은 문서의 끝을
START_TAG는 태그의 시작을 (예 : <uid> )
END_TAG는 태그의 끝을 (예 : </uid> )
TEXT는 태그의 시작과 끝 사이에서 나타난다. (예 : <uid>여기서 TEXT 이벤트 발생</uid> )


서버, Local 둘다 있는 코드


서버파일 접속 코드만 별도 발췌



Local 파일 접속 코드만 별도 발췌


ListView 모양을 처리하는 부분을 아직 제대로 이해하지 못해서 이 부분은 나중에 알게되면 추가 정리를 할 생각이다.

일단은 PHP 를 통해서 MySQL 데이터를 XML 형태로 안드로이드 단말에서 볼 수 있다는 것 자체에 의미를 두련다.

블로그 이미지

Link2Me

,
728x90

Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 15
메시지가 뿌려지면서 에러를 출력한다.

컴파일 하려고 해도 안된다.


해결책을 찾아서 적어둔다. 대략적으로 표시되어 있어서 이해가 얼른 안되어서 아예 numbering을 하면서 적었다.

먼저 Windows --> Preferences 를 선택하면 아래 그림이 나온다.





이렇게 하고 나서 컴파일을 하면 컴파일이 잘된다.

블로그 이미지

Link2Me

,
728x90

연습하는 과정에서 중도에 이름을 변경해서 다시 하느라고 이름이 좀 다르다.

IntentBasedWebView 가 아니라 그냥 WebView 만드는 과정이다.

IntentBasedWebView 라는 항목이 나오는 걸 없애는 것도 알고 싶은데 아직은 모르겠다. (ActionBar 제거하는 메소드로 처리하면 됨)

티스토리, 네이버, 블로그 라는 글자만 누르면 하단에 내용이 바뀌도록 구현했다.

기능을 추가해보면서 발생하는 문제점 등이 뭔지 파악도 하면서 배우는 중이다.

내용은 처음 작성하고 나서 계속 학습하면서 내용을 보완하고, 하단에 추가하는 방식으로 작성하고 있다.

남들을 위한 강의가 아니라 내가 이걸 배워가는 과정을 기록하고 있으니까 ^^




1 . WebView를 보여줄 준비과정


2. Layout 구성 과정

LinearLayout 으로 변경했다.

완벽하게 내것으로 소화를 하려면 좀 더 연습이 필요하다.


첨부된 파일은 activity_main.xml 과 MainActivity.java 파일이다.

mywebview_example.zip


<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"   
    android:layout_width="match_parent"
    android:layout_height="match_parent"   
    android:orientation="vertical" >   
   
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
      
         <TextView
             android:id="@+id/TextView01"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:gravity="center"
             android:padding="20dip"
             android:text="티스토리"
             android:textColor="#000000" />

         <TextView
             android:id="@+id/TextView02"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:gravity="center"
             android:padding="20dip"
             android:text="네이버"
             android:textColor="#000000" />

         <TextView
             android:id="@+id/TextView03"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:gravity="center"
             android:padding="20dip"
             android:text="블로그"
             android:textColor="#000000" />
       
    </LinearLayout>       
   
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        >     
   
        <WebView
            android:id="@+id/webview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"           
            android:scrollbars="none"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:adjustViewBounds="true"
            android:scaleType="fitStart"           
            />         
   
    </LinearLayout>
   
</LinearLayout>


3. 퍼미션 설정


4. WebView 코딩과정

package com.example.mywebview;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;

public class MainActivity extends Activity {
    private WebView mWebView;
    private TextView TextView01;
    private TextView TextView02;

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


        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();   


        setLayout("http://link2me.tistory.com"); // default webview       
       
        TextView01 = (TextView) findViewById(R.id.TextView01);
        TextView02 = (TextView) findViewById(R.id.TextView02);       
       
        TextView01.setOnClickListener(onclick1);
        TextView02.setOnClickListener(onclick2);
        findViewById(R.id.TextView03).setOnClickListener(onclick3);  // 이런 방법으로도 가능하다는 거      
    }
   
    OnClickListener onclick1 =new OnClickListener() {
        @Override
        public void onClick(View v) {
            String url = "http://www.tistory.com";
            setLayout(url);           
        }       
    };
   
    OnClickListener onclick2 =new OnClickListener() {
        @Override
        public void onClick(View v) {
            String url = "http://m.naver.com";
            setLayout(url);           
        }       
    };
   
    OnClickListener onclick3 =new OnClickListener() {
        @Override
        public void onClick(View v) {
            String url = "http://link2me.tistory.com";
            setLayout(url);           
        }       
    };

    @SuppressLint("SetJavaScriptEnabled")
    private void setLayout(String url) {
        mWebView = (WebView) findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient()); // 이걸 안해주면 새창이 뜸
        mWebView.loadUrl(url);
    }
       
}


화면 확대 축소 옵션

webView.getSettings().setBuiltInZoomControls(true);
webView.setDisplayZoomControls(false);


버튼 또는 TextView 를 클릭하면 다른 Activity 로 이동하게 하고 싶은 경우

이 부분에서 에러가 발생한다.

<check_framebuffer_attachment:854>: Invalid texture format! Returning error!

검색해도 해결책을 구할 수가 없다.


OnClickListener onclick2 =new OnClickListener() {
        @Override
        public void onClick(View v) {
            //switch(v.getId()){
                //case R.id.TextView02:
                    Toast.makeText(getApplicationContext(), "네이버 사이트로 이동합니다.", Toast.LENGTH_LONG).show();
                    Intent intent = new Intent(MainActivity.this, ButtonActivity.class);
                    startActivity(intent);
                    //break;
            //}
        }       
    };


이렇게 주석처리를 해가면서 해보니 Toast 메시지만 출력하는 것은 문제가 안된다.

그 다음 Intent 처리하는 부분에서 잘못된거 같은데 아직 왜 그러는지 원인을 모르겠다.


안드로이드 OnClick 메소드에 대해 설명이 잘된 사이트

http://nanstrong.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%95%EC%9D%9807-AndroidOnClick%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-4%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95



OnClickListener 메소드 부분은 아래와 같이 다시 보완했다.(2016-06-19 일)

ButtonActivity.java 와 button.xml 파일을 생성해서 테스트를 해봤더니 Intent 로 화면이 전환되는 것을 확인했다.

개념이 부족한 상태에서 화면을 복사해서 대략 붙여넣기하면서 수정을 했더니 뭔가 꼬인 부분이 있는거 같다.

아직 Layout 만들고 화면에 불러서 처리하는 부분에 대한 지식이 부족해서인가 보다.


.setJavaScriptEnabled - 웹뷰에서 자바 스크립트 사용
.setSupportZoom - 손으로 확대, 축소를 할 수 있도록 사용
.setBuiltInZoomControls - WebView 내장 Zoom 사용
.onPageStarted() - 웹뷰에서 url이 로드 될때 호출 되는 함수
.onPageFinished() - 웹뷰에서 url 로딩이 완료되면 호출 되는 함수
WebViewClient의 shouldOverrideUrlLoading 함수는 웹뷰 내에서 웹 페이지를 돌아다니기 위해서 사용
.onJSAlert() -  웹에서 띄우는 팝업 창 같은 것을 웹뷰에서 보여주기 위해 사용



블로그 이미지

Link2Me

,
728x90

처음에는 어떻게 파일을 인식시키는 줄도 몰랐다.

이제는 흐름이 어떻게 흘러가는지 감을 약간 잡고 있는 중이다.

이미 만들어진 프로젝트를 eclipse 에서 인식시키는 절차라고 보면 된다.

프로젝트를 닫은 걸 열어서 작업하는 경우, 인터넷에서 다운로드 받은 프로젝트, USB에 담긴 프로젝트 파일이라고 보고 아래 절차대로 따라하면 된다.




몇번의 시행착오를 통해서 알게된 사실도 추가로 적어둔다.

강좌 사이트 샘플소스나 블로그에 첨부된 샘플소스를 추가하고자 할 경우에는 반드시 workspace 가 아닌 다른 폴더에 복사를 해두고(이름을 다르게 지정해도 됨) 파일을 인식시킨 후 Copy projects into workspace 를 체크해서 workspace 폴더로 복사를 하면 원래 이름으로 생성이 된다.




블로그 이미지

Link2Me

,
728x90

eclipse 안드로이드를 처음 접하는 초보자 입장에서 프로젝트를 만드는 방법에 대해 작성했다.

http://webnautes.tistory.com/829 게시글을 토대로 다른 블로그 자료들을 참조하여 수정 보완했으며, PHP JSON 코드 부분은 내 방식대로 코딩을 했다.


※ listView 에 대한 처리 개념에 대한 설명이 잘 된 곳은 http://recipes4dev.tistory.com/42


테스트 환경 : eclipse LUNA, Mars.2 둘다 정상동작


준비를 위한 세팅과정


여기까지가 기본 세팅을 위한 준비과정이다.


ListView는 사용자가 정의한 데이터 목록을 아이템 단위로 구성하여 화면에 출력하는 ViewGroup의 한 종류다.
ListView에 표시되는 아이템은 Image, Button, CheckBox 등 여러 View의 조합으로 구성되는 Custom 형태가 일반적이다.

ListView 는 일반 위젯(TextView)가 아니라 선택위젯이기 때문에, Adapter 에서 만들어주는 getView() 를 이용해서 아이템을 표시한다.
Adapter가 하는 역할은 사용자 데이터를 입력받아 View를 생성하는 것이며, Adapter에서 생성되는 View는 ListView 내 하나의 아이템 영역에 표시되는 것이다.

ListView 는 Adapter 를 사용하여 데이터를 표시하는 View 로 아래 설명은 Activity에 ListView를 추가할 때 사용하는 방법이다.

Fragment 에 ListView 를 사용하기 위해서는 LayoutInflater를 사용하여 Resource Layout을 View로 변환한 다음, 해당 View를 사용하여 findViewById()를 호출하는 방법으로 해야 한다.



2. list_item.xml 생성

이제 ListView 의 한 아이템에 표시될 Layout 을 정의해야 한다.


main.xml 파일에서 마우스 우클릭을 하여 Copy(Ctrl + C)를 한 다음에

layout 폴더에서 마우스 우클릭하여 붙여넣기를 눌러서 이름을 list_item.xml 로 수정한다.

layout.zip


초보자라면 기존 코드 내용을 전부 지우고 아래 코드를 복사하여 붙여넣기 한다. (연습이니까)

LinearLayout 기반 xml 파일 구성을 할 줄 안다면 본인이 원하는 형식으로 작성한다.



다른 이미지를 drawable-hdpi 폴더에 복사하고 해당 파일명을 적어주면 된다.

이 부분은 나중에 사진 보여주기 부분으로 연동해볼 목적으로 표시한 영역이다.


3. main.xml 수정

ListView 가 표시될 위치를 결정한다.

안드로이드는 사용자 인터페이스를 XML를 사용하여 정의한다.

main Layout 에는 ListView만 넣어주면 된다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView" />

</LinearLayout>

4. ListView 아이템에 표시될 사용자 데이터 정의


public class Main extends Activity {

    // 서버에서 가져올 정보를 담을 변수 선언
    String myJSON;
    private static final String TAG_RESULTS="result";
    private static final String TAG_UID = "uid";
    private static final String TAG_NAME = "name";
    private static final String TAG_Mobile ="mobile";  



5. Apdater 생성후 ListView에 지정

사용자 데이터가 준비되었으니 해당 데이터를 입력받아 View로 만들어줄 Adapter를 생성해야 한다.


public class Main extends Activity {

     // 서버 정보를 담을 배열(ArrayList)
    ArrayList<HashMap<String, String>> personList;
 
    ImageView imageView1;
    ListView list;  

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      
        imageView1 = (ImageView) findViewById(R.id.imageView1);
        list = (ListView) findViewById(R.id.listView);
        personList = new ArrayList<HashMap<String,String>>();

    // 어댑터 생성, R.layout.list_item : Layout ID
    ListAdapter adapter = new SimpleAdapter(
        Main.this, personList, R.layout.list_item,
        new String[]{TAG_UID,TAG_NAME,TAG_Mobile},
        new int[]{R.id.uid, R.id.name, R.id.mobile}
    );

    list.setAdapter(adapter); // ListView 에 어댑터 설정(연결)

    }



6. AndroidManifest.xml 파일에 퍼미션 추가

안드로이드가 어떤 컴포넌트를 시작하려면 먼저 애플리케이션 안에 그 컴포넌트가 존재하는지를 알아야 한다. 애플리케이션안의 모든 컴포넌트들은 AndroidManifest.xml 안에 선언되어야 한다.

AndroidManifest.xml 파일에 선언되지 않은 Activity, Service, Content Provider 는 안드로이드 시스템에서 전혀 알 수가 없다. 결과적으로 실행할 수가 없다.


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





5. Main.java 코드 수정

※ 프로젝트 생성할 때 이름이 다르기 때문에 코드는 필요한 부분만 발췌하여 추가해야 한다.

   첨부된 파일을 다운로드 받아서 필요한 부분을 복사하여 붙여넣기 해보면 된다.

   

Main.java


URL은 특정 URL 주소를 다루기 위해 자바에서 제공되는 클래스다.

URL 객체가 생성되면, URL 클래스의 openStream() 메소드를 이용하여 해당 URL의 자원을 얻어오는 InputStream 을 리턴 받을 수 있으며, getConnection 메소드를 사용하여 URLConnection 객체도 얻어올 수 있다.
기본적으로 네트워크 작업을 기본 UI Thread에서 처리하는 것을 금지하고 별도의 Thread를 생성해서 처리하도록 하고 있다.

package com.example.phpmysql;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class Main extends Activity {

    // 서버에서 가져올 정보를 담을 변수 선언

    String myJSON; 
    private static final String TAG_RESULTS="result"; 
    private static final String TAG_UID = "uid"; 
    private static final String TAG_NAME = "name"; 
    private static final String TAG_Mobile ="mobile"; 
 
    JSONArray peoples = null; 
 

     // 서버 정보를 담을 배열(ArrayList)

    ArrayList<HashMap<String, String>> personList; 
 
    ImageView imageView1;
    ListView list;   

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        imageView1 = (ImageView) findViewById(R.id.imageView1);
        list = (ListView) findViewById(R.id.listView); 
        personList = new ArrayList<HashMap<String,String>>(); 
        getDbData("http://IP주소/mobile/get_json.php"); 
    }
   
    public void setImage(int resId) {
        imageView1.setImageResource(resId);
    }
   
    private void getDbData(String string) {
        class GetDataJSON extends AsyncTask<String, Void, String>{ 
           
            @Override 
            protected String doInBackground(String... params) { 
 
                String uri = params[0];   
                BufferedReader bufferedReader = null;


                try { 
                    URL url = new URL(uri); 
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 연결 객체 생성
                    StringBuilder sb = new StringBuilder();
                   
                    if(conn != null){ // 연결되었으면
                        conn.setConnectTimeout(10000);
                        conn.setUseCaches(false);
                        if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                            bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
                            String json; 
                            while((json = bufferedReader.readLine())!= null){ 
                                sb.append(json + "\n"); 
                            }      
                        }
                    }
                    return sb.toString().trim();   
                } catch(Exception e){ 
                    return new String("Exception: " + e.getMessage());
                }    
            } 

 

            protected void onPostExecute(String result){ 
                myJSON=result; 
                showList(); 
            } 
        } 
       
        GetDataJSON g = new GetDataJSON(); 
        g.execute(string);
       
    }
   
    protected void showList(){ 
        try { 
            JSONObject jsonObj = new JSONObject(myJSON); 
            peoples = jsonObj.getJSONArray(TAG_RESULTS); 
 
            for(int i=0;i<peoples.length();i++){ 
                JSONObject c = peoples.getJSONObject(i); 
                String uid = c.getString(TAG_UID); 
                String name = c.getString(TAG_NAME); 
                String mobile = c.getString(TAG_Mobile); 
 
                HashMap<String,String> persons = new HashMap<String,String>(); 
 
                persons.put(TAG_UID,uid); 
                persons.put(TAG_NAME,name); 
                persons.put(TAG_Mobile,mobile); 
 
                personList.add(persons); 
            } 
 
            // 어댑터 생성, R.layout.list_item : Layout ID
            ListAdapter adapter = new SimpleAdapter( 
                    Main.this, personList, R.layout.list_item, 
                    new String[]{TAG_UID,TAG_NAME,TAG_Mobile}, 
                    new int[]{R.id.uid, R.id.name, R.id.mobile
            ); 
 
            list.setAdapter(adapter); // ListView 에 어댑터 설정(연결) 
 
        } catch (JSONException e) { 
            e.printStackTrace(); 
        }
    }   
}



AsyncTask : http://link2me.tistory.com/1031

JsonArray 기본 개념 설명 : http://link2me.tistory.com/1247 참조


StringBuilder 부분은 자바 자료는 아직 못봤는데 C# 과 거의 같은거 같다.

http://link2me.tistory.com/824


HashMap 클래스는 키와 데이터 값의 한쌍으로 묶어서 관리하며 키의 중복을 허용하지 않는다.


JSON(JavaScript Object Notation) 파서가 라이브러리 형태로 제공되므로 직접 문자열을 파싱할 필요가 없다. 안드로이드 역시 JSON 파서를 기본 제공한다.

- JSONArray 클래스는 JSON  파일에서 배열을 읽어들인다.

- JSONObject 클래스는 JSON 파일에서 객체를 읽어들인다.

- JSONArray 클래스의 메소드와 JSONObject 클래스의 메소드의 목록은 동일하나 () 안에 멤버의 이름을 인수로 전달받는다는 점이 다르다.



6. 결과화면



MainActivity.java 파일 Update

배우면서 갱신된 내용을 기록해 둔다.


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private ListView mListView = null;
    private ListViewAdapter mAdapter = null;

    // 서버 정보를 파싱하기 위한 변수 선언
    String myJSON;
    private static final String TAG_RESULTS="result";
    private static final String TAG_UID = "uid";
    private static final String TAG_NAME = "name";
    private static final String TAG_Mobile ="mobile";

    JSONArray peoples = null;

    Context context;

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

        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        mListView = (ListView) findViewById(R.id.listView);
        mAdapter = new ListViewAdapter(this);

        // 서버에 있는 정보를 읽어다가 mAdapter.addItem 에 추가하는 과정
        getDbData(Value.IPADDRESS + "/mobile/get_json.php");

        mListView.setAdapter(mAdapter);

        TextView searchView = (TextView) findViewById(R.id.SearchView);
        TextView phonebookView = (TextView) findViewById(R.id.PhonebookView);

        searchView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "검색화면으로 이동합니다", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(MainActivity.this, Search_Item.class);
                startActivity(intent);
            }
        });

        phonebookView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "내 폰의 전화번호부를 가져옵니다", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(MainActivity.this, PhonebookActivity.class);
                startActivity(intent);
            }

        });

    }


    private void getDbData(String string) {
        class GetDataJSON extends AsyncTask<String, Void, String>{

            @Override
            protected String doInBackground(String... params) {

                String uri = params[0];
                BufferedReader bufferedReader = null;
                try {
                    URL url = new URL(uri);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    // 세션 쿠키 전달
                    String cookieString = CookieManager.getInstance().getCookie(Value.IPADDRESS);

                    StringBuilder sb = new StringBuilder();

                    if(conn != null){ // 연결되었으면
                        //add request header
                        conn.setRequestMethod("POST");
                        conn.setRequestProperty("USER-AGENT", "Mozilla/5.0");
                        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                        conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
                        if (cookieString != null) {
                             conn.setRequestProperty("Cookie", cookieString);
                             Log.e("PHP_getCookie", cookieString);
                         }
                        conn.setConnectTimeout(10000);
                        conn.setReadTimeout(10000);
                        conn.setUseCaches(false);
                        conn.setDefaultUseCaches(false);
                        conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션
                        //conn.setDoInput(true);

                        int responseCode = conn.getResponseCode();
                        System.out.println("GET Response Code : " + responseCode);
                        if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                            bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                            String json;
                            while((json = bufferedReader.readLine())!= null){
                                sb.append(json + "\n");
                            }
                        }
                        bufferedReader.close();
                    }
                    return sb.toString().trim();

                } catch(Exception e){
                    return new String("Exception: " + e.getMessage());
                }

            }

            protected void onPostExecute(String result){
                myJSON=result;
                showList();
            }
        }

        GetDataJSON g = new GetDataJSON();
        g.execute(string);

    }

    protected void showList() {
        // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력
        try {
            JSONObject jsonObj = new JSONObject(myJSON);
            peoples = jsonObj.getJSONArray(TAG_RESULTS);

            for(int i=0;i<peoples.length();i++){
                JSONObject c = peoples.getJSONObject(i);
                String uid = c.getString(TAG_UID);
                String name = c.getString(TAG_NAME);
                String mobile = c.getString(TAG_Mobile);
                Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );

                mAdapter.addItem(myIcon,uid,name,mobile);
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

    class ViewHolder {
         public LinearLayout child_layout;
         public ImageView mImage;
         public Button childListBtn;
         public TextView name;
         public TextView mobile;
    }

    private class ListViewAdapter extends BaseAdapter {

        private Context mContext = null;
        private ArrayList<ListData> mListData = new ArrayList<ListData>();

        public ListViewAdapter(Context mContext) {
            super();
            this.mContext = mContext;
        }

        @Override
        public int getCount() {
            return mListData.size();
        }

        @Override
        public Object getItem(int position) {
            return mListData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            View view = convertView;
            if (view == null) {
                viewHolder = new ViewHolder();

                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.list_item, parent, false);

                view.setBackgroundColor(0x00FFFFFF);
                view.invalidate();

                viewHolder.child_layout = (LinearLayout) view.findViewById(R.id.child_layout);
                viewHolder.mImage = (ImageView) view.findViewById(R.id.mImage);
                viewHolder.childListBtn = (Button ) view.findViewById(R.id.childListBtn);
                viewHolder.name = (TextView) view.findViewById(R.id.name);
                viewHolder.mobile = (TextView) view.findViewById(R.id.mobile);

                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            final ListData mData = mListData.get(position);

            if (mData.mImage != null) {
                viewHolder.mImage.setVisibility(View.VISIBLE);
                viewHolder.mImage.setImageDrawable(mData.mImage);
            } else {
                viewHolder.mImage.setVisibility(View.GONE);
            }

            viewHolder.childListBtn.setText(mData.uid);
            viewHolder.name.setText(mData.name);
            viewHolder.mobile.setText(mData.mobile);

            viewHolder.mImage.setOnClickListener(new ImageView.OnClickListener(){

                @Override
                public void onClick(View v) {
                    // 연락처 저장 함수 호출
                    AlertDialog.Builder SaveContact = new AlertDialog.Builder(MainActivity.this);
                    SaveContact.setMessage("전화번호를 저장하시겠습니까?");
                    DialogInterface.OnClickListener mail = new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            int rawContactId = 0;
                            Contacts phonebook = new Contacts(); // 전화번호부 객체 생성
                            ContentResolver cr = getContentResolver();
                            String strContactName = mData.name;
                            String strMobileNO = mData.mobile;
                            String strofficeNO ="";
                            String strEmail ="";
                            String strPhoto ="";

                            rawContactId = phonebook.ContactsIDExistCheck(cr, strContactName);
                            if(rawContactId > 0){
                                // 기존 전화번호가 존재하면 삭제하고 새로 입력
                                phonebook.ContactsIDdelete(cr, context, rawContactId);
                            }

                            phonebook.ContactsIDinsert(cr, context, strContactName, strMobileNO, strofficeNO, strEmail, strPhoto);
                        }
                    };

                    DialogInterface.OnClickListener cancel = new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {

                        }
                    };
                    SaveContact.setPositiveButton("저장", mail);
                    SaveContact.setNegativeButton("취소", cancel);
                    SaveContact.show();
                }

            });

            viewHolder.childListBtn.setOnClickListener(new Button.OnClickListener(){

                @Override
                public void onClick(View v) {

                    Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    vibe.vibrate(50);

                    AlertDialog showdialog = new AlertDialog.Builder(MainActivity.this)
                            .setTitle(mData.name)
                            .setMessage(mData.mobile + " 통화하시겠습니까?")
                            .setPositiveButton("예",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {

                                            Intent i = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+ mData.mobile));
                                            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                            startActivity(i);
                                        }

                                    })
                            .setNegativeButton(
                                    "아니오",
                                    new DialogInterface.OnClickListener() {

                                        public void onClick(DialogInterface dialog,int which) {
                                            dialog.dismiss();
                                        }
                                    }).create();
                    showdialog.show();
                }

            });

            return view;
        }

        public void addItem(Drawable icon, String uid, String name, String mobile){
            ListData addInfo = null;
            addInfo = new ListData();
            addInfo.mImage = icon;
            addInfo.uid = uid;
            addInfo.name = name;
            addInfo.mobile = mobile;

            mListData.add(addInfo);
        }

        public void remove(int position){
            mListData.remove(position);
            mAdapter.notifyDataSetChanged();
        }


    }

    // Back 버튼을 누르면 어플 종료여부 확인 처리
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    moveTaskToBack(true); // 본 Activity finish후 다른 Activity가 뜨는 걸 방지.
                    finish();
                    //application 프로세스를 강제 종료
                    android.os.Process.killProcess(android.os.Process.myPid() );
                }
        }).setNegativeButton( "No", null ).show();

        return true;
      }

      return super.onKeyDown(keyCode, event);
     }

}



Back 버튼 누르면 어플 종료 되는 제대로 된 코드는 http://link2me.tistory.com/1466 활용하라.


블로그 이미지

Link2Me

,
728x90

안드로이드에서 MySQL DB 에 직접 접속이 불가능하다.

웹을 통해서 DB 접속을 해야 하므로 PHP 를 통한 MySQL 접속을 한다.


1. MySQL DB 테이블 생성

CREATE TABLE IF NOT EXISTS `Person` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `mobile` varchar(16) NOT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


INSERT INTO `Person` (`uid`, `name`, `mobile`) VALUES
(1, '홍길동', '010-6123-0000'),
(2, '장정은', '010-0123-0001'),
(3, '김홍길', '010-0123-0002'),
(4, '최신형', '010-0123-0003'),
(5, '민들레', '010-0123-0004'),
(6, '김아정', '010-0123-0005'),
(7, '이순신', '010-0123-0006'),
(8, '이정민', '010-9999-8887');

위 테이블 구조를 phpMyAdmin 상에서 복사하여 붙여넣기하면 테이블이 만들어진다.

첨부파일 받아서 수정해서 사용해도 된다.


android_mysql_php.sql


MySQL DB 생성 방법을 모르면 http://link2me.tistory.com/431 참조

phpMyAdmin 사용법을 알고 싶다면 http://link2me.tistory.com/797 참조


※ 개인 PC에 설치해서 테스트 하는 경우에는 public IP 주소가 아니므로 스마트폰에서 인식을 못할 수 있음.


2. PHP 코드 작성

코드를 모듈화 해두면 코드가 간결해진다.

==== dbconnect.php ====

<?php
$db['host'] = "localhost";
$db['name'] = "address";
$db['user'] = "root";
$db['pass'] = "autoset";
$db['port'] = "3306";

$dbconn = isConnectDb($db);

function isConnectDb($db) {
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;
    }
}
?>


==== get_json.php =====

<?php
@extract($_POST); // POST 전송으로 전달받은 값 처리
if(!(isset($idx) && !empty($idx))) { // 안드로이드에서 넘어온 변수 체크
    echo 0;
    exit;
}

require_once $_SERVER['DOCUMENT_ROOT'].'/dbconnect.php';
$sql = "select uid, name, mobile from Person ";

$R = array(); // 결과 담을 변수 생성
$result = mysqli_query($dbconn,$sql);
while($row = mysqli_fetch_object($result)) {
    array_push($R, $row); // 이 부분을 풀어서 처리하는 코드는 http://link2me.tistory.com/1275 참조
}
echo json_encode(array("result"=>$R));
?>


실행결과 Web 화면



PHP 초보자는 아래 작성된 내용은 안봐도 된다. 이런 방법도 있다는 걸 알고자 한다면 읽어도 좋다.


좀 더 세분화한 함수를 만들어서 연동 처리하는 방법도 가능하다.

==== get_xml.php =====

<?php
    include_once $_SERVER['DOCUMENT_ROOT'].'/_var/db.info.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/_app/phpclass/dbClass.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/_app/phpclass/xmlClass.php';

    $conn = new MySQLDbClass(); // DB 함수
    $dbconn = $conn->isConnectDb($db);

    $result = mysql_query('select uid,name,mobile from Person');

    // 1. XML 데이터 생성방법
    $c = new xmlcreateClass();
    $c->XML2View($result); // 화면출력
?>


class xmlcreateClass {

    // XML 데이터를 모니터 화면으로 출력
    // $c->XML2View($result); // XML 데이터 화면 출력
    function XML2View($result) {
        header('Content-type: text/xml');
        echo $this->exportAsXML($result);
    }


    // MySQL DB 자료를 column 개수, 게시물 총개수 만큼 자동으로 XML로 만들기
    function exportAsXML($result){
        $xml ='<?xml version="1.0" encoding="UTF-8" ?>'."\n";
        $xml.='<ListInfo>'."\n";
        while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            $xml.="<items>"."\n";

            // 여기에 테이블에 필요한 칼럼을 적어준다.

            // 능력이 되면 자동으로 칼럼을 생성처리하면 된다.

            $xml.="</items>"."\n";
        }
        $xml.='</ListInfo>';
        return $xml;
    }
}


3. PHP 코드에 사용된 함수 설명

KIMSQ RB의 코드를 Class 화하여 필요한 코드를 추가하거나 수정해서 사용함.


==== db.info.php ======

<?php
$db['host'] = "localhost";
$
db['name'] = "DB명"; // root
$
db['user'] = "db_user"; // DB 접속권한을 가진 User명
$
db['pass'] = "db_password";
$
db['port'] = "3306";
?>


==== dbClass.php =====

<?php
class MySQLDbClass {

    function isConnectDb($db)
    {
        $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
        mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
        if (mysqli_connect_errno()) {
           echo "Failed to connect to MySQL: " . mysqli_connect_error();
           exit;
        } else {
          return $conn;  
        }
    }


    // DB Query result 함수

    function getDbresult($table,$where,$column) {
        global $db;
        $result = mysqli_query($db,'select '.$column.' from '.$table.($where?' where '.$this->getSqlFilter($where):''));
        return $result;
    }


    //SQL필터링
    function getSqlFilter($sql)
    {
        return $sql;
    }

}//end dbClass
?>


더 많은 코드는 KIMSQ RB 함수를 참조하여 응용해도 되고 Class 화하여 사용해도 된다.

MySQLi 를 사용하는 서버인 경우에는 코드를 수정할 부분이 있다.


$flddata ="uid, name, mobile"; // 화면에 출력할 칼럼 발췌
$where ='';
$result = $db->getDbresult('Person',$where,$flddata);


위 부분은 코드를 직관적으로 이해할 수 있도록 칼럼부분과 조건부분을 별도로 구분하여 처리했다.

테이블에 있는 모든 칼럼을 다 표시하는 * 보다는 출력하고픈 칼럼만 발췌하는 것이 속도면에서도 좋다.


만약 LIMIT 을 걸어서 테스트하고 싶다면 함수를 수정 보완하거나 그냥 간단하게

$result = mysql_query('select uid,name,mobile from Person LIMIT 3');

로 변경해서 테스트 해보면 된다.


함수로 만들어서 사용한다면

    function JSONEncode($result,$varname){
        $R = array(); // 결과 담을 변수 생성
        while($row = mysqli_fetch_object($result)) { 
            $R[] = $row;
        }
       
        return json_encode(array($varname=>$R));
    }



<?php
    include_once $_SERVER['DOCUMENT_ROOT'].'/db.info.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    require_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/jsonClass.php';

    $conn = new MySQLDbClass(); // DB 함수
    $dbconn = $conn->isConnectDb($db);

    // 화면에 출력할 칼럼 발췌
    $result = mysqli_query($db, 'select uid,name,mobile from Person LIMIT 3');

    $c = new jsonClass();
    echo $c->JSONEncode($result,'result');
?>


JSON_Encode 형태로 만들어서 출력(echo)하면 안드로이드에서 PHP 출력화면 데이터를 인식하여 스마트폰 화면에서 볼 수 있다.


https://www.androidhive.info/2012/05/how-to-connect-android-with-php-mysql/ 를 참조해도 도움된다.


eclipse Android Code 부분은  다음 게시글 http://link2me.tistory.com/1020 을 참조하면 된다.

Android Studio Code 부분은 http://link2me.tistory.com/1230 를 추가로 보고 틀려진 부분을 확인하여 코드를 약간 수정하면 된다.

블로그 이미지

Link2Me

,