안드로이드&IOS 앱 개발자 양성

안드로이드&iOS 앱 개발자 양성(79일차)

HRuler 2020. 7. 29. 18:02
더보기

** XML Parsing
1. XML
2. XML의 기본 문법
3. XML 파싱
4. 한겨례 신문사의 RSS를 이용해서 전체 기사를 파싱한 후 title과 link의 내용을 가져와서 출력

** Adapter View

** 구동원리를 알기 위한 실습
1. 실행 가능한 Activity 추가 - ListVIewActivity
2. 화면 디자인
3. Acitivity.java 파일에 필요한 코드 작성

** 배열이나 List 대신에 array.xml 파일에 만든 리소스도 출력이 가능
1. res/values 디렉토리에 array.xml 파일을 추가하고 배열을 생성
2. Adapter 객체를 생성하는 코드를 변경

** AdapterView의 종류

** Adapter

** ListView

** 행을 선택했을 때 호출되는 Listener

** ListView의 항목을 편집할 수 있고 여러 개 선택해서 삭제할 수 있다.
1. 사용 가능한 Activity 추가 - MultiActivity
2. 레이아웃 디자인
3. Activity.java에 데이터를 출력
4. 선택 모드와 선 모양 변경
5. EditText에 입력을 하고 추가 버튼을 누르면 데이터가 삽입되는 코드를 작성
6. 삭제 버튼을 누르면 처리될 코드를 작성
7. SimpleAdapter
8. CursorAdapter

** 사용자 정의 항목 뷰 사용
1. Layout Inflastion
2. BaseAdapter로 부터 상속받는 클래스를 생성
3. Activity.java 파일에 인스턴스 변수 추가
4. Activity.java 파일의 onCreate에서 listView를 찾아서 대입하고 데이터 생성 코드 작성
5. 하나의 항목으로 사용할 뷰의 모양을 생성
6. Adapter 클래스 만들기
7. Activity.java 파일의 onCreate 수정

** 사용자 정의 셀 만들기

** XML Parsing

1. XML

- eXtensible Markup Language의 약자

- HTML이 구조적이지 못하고 해석을 웹 브라우저가 하는 문제 때문에 데이터 포맷으로 부적합하다.

- 구조적이고 엄격한 문법을 적용하고 해석을 브라우저가 아닌 곳에서 할 수 있는 문자열 포맷을 고안했는데 이 문자열 포맷이 XML이다.

- 최근에 사용되던 HTML은 HTML에 XML 문법을 적용한 XHTML이었고 이것을 HTML4.01이라고 했다.

   요즘은 HTML5 문법을 많이 사용한다.

- We이 대중화되던 시절에 RSS(Rich Site Summary, Really Simple Syndication - 실시간으로 데이터를 제공)가 등장했고 SOAP(Simple Object Access Protocol - 서버에서 클라이언트에게 객체를 단순하게 전달하는 프로토콜)가 등장했는데 이 때 사용한 데이터 표준이 XML이다.

- 자바스크립트에서 서버에게 데이터를 비동기적으로 요청을 해서 일부분의 UI만 갱신할 필요가 생겼는데 이 때 채택한 기술이 AJAX(Asyncronous Javascript And XML의 약자)

- 최근에서 REST API의 등장으로 json을 데이터 포맷으로 더 많이 사용한다.

- 순수하게 넘겨주는 데이터는 json으로 많이 사용하지만 사람이 직접 설정하는 부분은 아직까지 XML이다.

2. XML의 기본 문법

<xml 구조나 인코딩 방식> <!--없으면 에러인데 설정 파일의 경우는 프레임워크가 대신 추가해주기도 한다. 안드로이드에서 XML을 만들 때 이 부분을 생략해도 된다.-->

<DTD> <!--xml 파일의 내용을 해석해서 다른 코드로 변경해주는 위치를 설정 하는 것으로 설정 파일일 때는 필수이지만 데이터일 때는 생략-->

<Root 태그>

       데이터를 의미하는 태그와 내용

</Root 태그>

- XML을 생성하는 것은 직접 작성하지 않고 라이브러리를 이용해서 데이터만 설정하면 된다.

3. XML 파싱

- DOM Parsing과 SAX Parsing 2가지

- 거의 대다수의 프로그래밍 언어가 2가지 방법을 동일하게 지원한다.

1) DOM(Document Object Model)

- HTML이나 XML의 내용을 메모리에 펼쳐서 사용할 수 있도록한 것

2) DOM Parser

- XML의 내용을 메모리에 전부 펼쳐놓고 필요한 DOM을 찾아서 파싱하는 방식

- 처음부터 내용을 메모리에 전부 저장하기 때문에 메모리 사용량이 많지만 속도는 빠르다.

- 편집도 가능하다.

- Root까지 찾는 방법

   DocumentBuilderFactory factory = new DocumentBuilderFactory().newInstancer();

   DocumentBuilder builder = factory.newDocumentBuilder();

   InputStream inputStream = new ByteArrayInputStream(문자열.getBytes("인코딩 방식"));

   Document document = builder.parse(inputStream);

   Element root = document.getDocumentElement();

3) SAX Parser

- 태그 하나 하나를 읽어가면서 파싱하는 방식

- 하나의 태그를 읽을 때 호출되는 콜백 메소드를 이용해서 파싱한다.

- 내용을 전부 메모리에 저장하지 않기 때문에 메모리 사용량이 적지만 속도는 느리다.

- 편집도 불가능하다.

- DefaultHandler로 부터 상속받는 클래스를 생성해서 메소드를 재정의한 후 사용한다.

   public void startDocument() : 문서의 시작을 만나면 호출되는 메소드

   public void endDocument() : 문서의 끝을 만나면 호출되는 메소드

   public void startElement(String uri, String localName, String qName, Attributes attrs) : 열리는 태그를 만나면 호출되는 메소드 - qName이 태그이름이고 attrs가 태그 속성들의 집합

   public void endElement(String uri, String localName, String qName) : 닫는 태그를 만나면 호출되는 메소드

   public void characters(char [] chars, int start, int length) : 여는 태그와 닫는 태그 사이의 문자열을 만나면 호출되는 메소드 - chars는 내용이고 start는 시작 위치, length는 길이이다.

      태그 이름은 하나의 패킷에 전부 전송이 가능하지만 태그 안의 내용은 하나의 패킷을 초과하는 경우도 있다.

      이 메소드는 유일하게 하나의 태그에 여러 번 호출될 수 있다.

      긴 문자열의 데이터를 가져올 때는 이 메소드를 잘 사용해야 한다.

- 파싱 수행

   SAXParserFactory factory = SAXParser.newInstace();

   SAXParser parser = factory.newSAXParser();

   XMLReader reader = parser.getXMLReader();

   DefaultHandler클래스 객체 = new DefaultHandler클래스();

   Reader.setContentHandler(객체);

   InputStream inpuStream = new ByteArrayInputStream(문자열.getBytes("인코딩 방식");

   reader.parse(new InputSource(inputStream)); //핸들러 클래스의 메소드 호출

4. 한겨례 신문사의 RSS를 이용해서 전체 기사를 파싱해서 title과 link의 내용을 가져와서 출력

- 단 첫 번째 title과 link는 제외

1) 안드로이드 프로젝트 생성 - 기존 프로젝트에 실행 가능한 Activity를 추가해도 된다.

2) 웹에서 가져와야 하므로 인터넷 권한을 설정하고 프로토콜을 확인해서 Application에 설정 추가

- http://www.hani.co.kr/rss/

- AndroidManifest.xml 파일에 추가

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

- application 태그에 설정 추가

android:usesCleartextTraffic="true"

3) activity_main.xml 파일에 화면 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/handidisplay"/>
    </ScrollView>

</LinearLayout>

4) MainActivity 클래스에서 디자인한 뷰를 전부 찾아오기

- 인스턴스 변수를 선언

- onCreate 메소드에 뷰를 찾아오는 코드를 작성

package com.example.android0729;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    TextView haniDisplay;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        haniDisplay = (TextView)findViewById(R.id.handidisplay);
    }
}

5) 데이터 다운로드를 위한 Thread 클래스를 생성 - MainActivity.java

    class ThreadEx extends Thread{
        //데이터를 다운받아 파싱할 스레드 클래스
        @Override
        public void run() {
            //웹 서버에서 문자열 다운로드 받기
            try{
                //다운받을 URL 생성
                URL url = new URL("http://www.hani.co.kr/rss/");
                //연결 객체 생성
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                //연결 옵션 설정
                con.setUseCaches(false);
                con.setRequestMethod("GET");
                con.setConnectTimeout(30000);
                con.setDoInput(true);
                con.setDoOutput(true);
                //파일 업로드하는 코드가 있으면 설정 추가

                //파라미터를 추가 - GET일 때는 url에 바로 추가해도 된다.

                //다운로드 받기 - 문자열 : BufferedReader, 파일 : BufferedInputStream
                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                //문자열을 가지고 + 연산을 하면 메모리 낭비가 발생할 수 있어서 StringBuilder를 이용한다.
                //그 이유는 문자열은 + 연산하면 현재 객체에 하는 것이 아니고 복사해서 수행하기 때문이다.
                StringBuilder sb = new StringBuilder();
                while(true){
                    String line = br.readLine();
                    if(line == null){
                        break;
                    }
                    //출력할 때 가독성을 위해 \n을 추가한다. 실제 서비스에서는 \n을 제거한다.
                    sb.append(line + "\n");
                }
                //정리
                br.close();
                con.disconnect();
                //다운로드 받은 내용을 문자열로 변환
                xml = sb.toString();
                Log.e("xml", xml);



            }catch (Exception e){
                //이 예외 출력 시 URL을 확인
                Log.e("다운로드 에러", e.getMessage());
            }
            try{
                if(xml != null) {
                    //DOM 파싱을 위한 준비
                    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder builder = factory.newDocumentBuilder();
                    InputStream inputStream = new ByteArrayInputStream(xml.getBytes("utf-8"));
                    Document document = builder.parse(inputStream);
                    Element root = document.getDocumentElement();

                    //title 태그 전부 가져오기
                    NodeList titles = root.getElementsByTagName("title");
                    NodeList links = root.getElementsByTagName("link");
                    for (int i = 1; i < titles.getLength(); i = i + 1) {
                        Node title = titles.item(i);
                        Node text = title.getFirstChild();
                        titleList.add(text.getNodeValue());

                        Node link = links.item(i);
                        text = link.getFirstChild();
                        linkList.add(text.getNodeValue());
                    }
                    //핸들러에게 출력 요청
                    Message message = new Message();
                    //전송할 데이터가 있으면 message.obj에 대입
                    handler.sendMessage(message);
                }
            }catch (Exception e){
                //이 예외 출력 시 파싱 알고리즘을 확인
                Log.e("데이터 파싱 에러", e.getMessage());
            }
        }
    }

6) 다운로드 받아서 파싱한 결과를 출력할 Handler를 생성 - MainActivity.java

- 여기서 화면 출력을 하지 않으면 Handler는 필요없다.

    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message message) {
            //titleList의 내용을 텍스트 뷰에 출력
            StringBuilder sb = new StringBuilder();
            for(String title : titleList){
                sb.append(title + "\n");
            }
            haniDisplay.setText(sb.toString());
        }
    };

7) MainActivity 클래스에 onResume 메소드를 재정의해서 스레드를 생성하고 시작

    @Override
    public void onResume(){
        super.onResume();
        //스레드 시작
        new ThreadEx().start();
    }

 

** Adapter View

- 여러 개의 데이터를 출력하기 위한 View

- 3개의 요소를 가지고 데이터를 출력

   Data : 배열이나 List - 출력되는 내용은 기본적으로 각 요소의 toString의 결과이다.

   View : 데이터를 출력하기 위한 View - 기본은 ListView

   Adapter : Data와 View를 연결해주기 위한 Controller이다.

 

** 구동원리를 알기 위한 실습

1. 실행 가능한 Activity 추가 - ListViewActivity

2. 화면 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ListViewActivity"
    android:orientation="vertical">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listview"/>
</LinearLayout>

3. Activity.java 파일에 필요한 코드 작성

package com.example.android0729;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class ListViewActivity extends AppCompatActivity {
    ListView listView;
    //출력할 데이터
    String [] data;
    //연결할 Adapter
    ArrayAdapter<String> adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);

        //데이터 생성
        data = new String[4];
        data[0] = "SI";
        data[1] = "SM";
        data[2] = "QA";
        data[3] = "DevOps";

        //출력할 View를 생성
        listView = (ListView)findViewById(R.id.listview);

        //adapter 생성    첫 번째는 출력을 위한 Context    두 번째는 ListView의 모양 - android.R.layout에 기본 모양이 제공
        //세 번째는 출력할 데이터
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);

        //뷰에 Adapter를 설정
        listView.setAdapter(adapter);
    }
}

 

** 배열이나 List 대신에 array.xml 파일에 만든 리소스도 출력이 가능

1. res/values 디렉토리에 array.xml 파일을 추가하고 배열을 생성

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="pl">
        <item>Machine Language</item>
        <item>Assembly</item>
        <item>C&amp;C++</item>
        <item>Java</item>
        <item>Python</item>
        <item>JavaScript</item>
        <item>C#</item>
        <item>R</item>
        <item>Objective-C</item>
        <item>Swift</item>
        <item>Kotlin</item>
        <item>Go</item>
        <item>Scala</item>
        <item>Ruby</item>
        <item>VB</item>
        <item>Closure</item>
        <item>Haskell</item>
    </string-array>
</resources>

2. Adapter 객체를 생성하는 코드를 변경

   ArrayAdapte.createFromResource(Context context, int resourceId, int 행모양)

 

** AdapterView의 종류

   ListView

   GridView

   Spinner

   Gallery

 

** Adapter

- Adapter : 항목들의 집합을 관리하는 기본적인 메소드를 소유한 클래스

- Adapter를 상속받은 ListAdapter : ListView와 연결에 필요한 메소드를 정의

- Adapter를 상속받은 SpinnerAdapter : Spinner와 연결에 필요한 메소드를 정의

- ListAdapter와 SpinnerAdapter를 상속받은 BaseAdapter : 앞의 2개의 인터페이스의 메소드 중 기본적인 것들을 구현해 놓음

- BaseAdapter를 상속받은 클래스 : ArrayAdapter(배열이나 List를 연결), CursorAdapter(데이터베이스 커서 연결), SimpleAdapter

 

** ListView

- BaseAdapter 모든 종류를 이용해서 데이터를 출력하는 것이 가능하다.

- ArrayAdapter는 배열이나 List 인터페이스를 구현한 클래스, array.xml 파일에 만들어진 리소스를 주입받을 수 있다.

- 행의 표시할 리소스 ID

   android.R.layout.simple_list_item_1 : 하나의 텍스트 뷰로 구성

   android.R.layout.simple_list_item_2 : 2개의 텍스트 뷰로 구성

   android.R.layout.simple_list_item_checked : 체크 박스가 만들어 짐

   android.R.layout.simple_list_item_single_choice : 라디오 버튼을 이용해서 하나의 행만 선택하도록 해준다.

   android.R.layout.simple_list_item_multiple_choice : 체크 박스를 이용해서 여러 행을 선택하도록 해준다.

- Adapter 연결 메소드

   setAdapter(Adapter adapter)

- 선택 모드 설정

   setChoiceMode(int id)

   CHOICE_MODE_NONE, CHOICE_MODE_SINGLE, CHOICE_MODE_MULTIPLE로 설정

   설정할 때는 적절한 행의 모양도 같이 설정해야 한다.

- 경계선 모양과 두께를 설정하는 메소드 - 모양을 반드시 먼저 설정해야 한다.

   setDivider(Drawable drawable)로 모양 설정

   setDividerHeight(int height)로 두께 설정

 

** 행을 선택했을 때 호출되는 Listener

   AdapterView.OnItemClickListener의 onItemClick이라는 메소드가 존재하고 첫 번째 매개변수는 AdapterView가 전달되고 두 번째 매개변수로는 선택한 행의 뷰가 리턴되고 세 번째 매개변수는 행의 인덱스가 리턴되고 네 번째 매개변수로는 항목의 고유한 ID가 전달된다.

- 데이터가 변경된 경우 다시 출력을 요청하는 메소드

   AdapterView가 notifyDataSetChanged()로 호출하면 된다.

- 행의 선택 여부를 알려주는 메소드

   ListVIew의 get?Position()을 이용하는데 여러 개 선택한 경우 getChekcedItemPosition()을 호출해서 SparseBooleanArray로 리턴받을 수 있는데 이 배열은 ListView의 모든 항목 선택 여부를 Boolean 배열로 만들어 준 것이다.

 

** ListView의 항목을 편집할 수 있고 여러 개 선택해서 삭제할 수 있다.

1. 사용 가능한 Activity 추가 - MultiActivity

2. 레이아웃 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MultiActivity"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:hint="추가할 항목 입력"
            android:id="@+id/iteminput"/>
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:text="추가"
            android:textSize="20sp"
            android:id="@+id/addbtn"/>
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:text="삭제"
            android:textSize="20sp"
            android:id="@+id/delbtn"/>
    </LinearLayout>

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

</LinearLayout>

3. Activity.java에 데이터를 출력

1) 인스턴스 변수 3개 선언 - ListView, List, ArrayAdapter

2) onCreate 메소드에서 출력하는 코드를 작성

package com.example.android0729;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

public class MultiActivity extends AppCompatActivity {
    ListView listView;
    ArrayList<String> data;
    ArrayAdapter<String> adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multi);
        listView = (ListView)findViewById(R.id.listview);
        data = new ArrayList<>();
        data.add("Oracle");
        data.add("MySQL");
        data.add("MongoDB");
        data.add("MS-SQL Server");

        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
        listView.setAdapter(adapter);
    }
}

4. 선택 모드와 선 모양 변경

- Activity.java 파일의 onCreate 메소드에서 설정

		//여러 개 선택 가능한 모양으로 Adapter 생성
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice, data);
        listView.setAdapter(adapter);
        //선 모양 설정
        listView.setDivider(new ColorDrawable(Color.RED));
        listView.setDividerHeight(3);
        //선택 모드를 변경
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

5. EditText에 입력을 하고 추가 버튼을 누르면 데이터가 삽입되는 코드를 작성

1) Activity.java 파일에 2개 뷰에 대한 변수를 선언

2) Activity.java 파일의 onCreate 메소드에 작성

package com.example.android0729;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;

public class MultiActivity extends AppCompatActivity {
    ListView listView;
    ArrayList<String> data;
    ArrayAdapter<String> adapter;

    EditText iteminput;
    Button addbtn, delbtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multi);
        listView = (ListView)findViewById(R.id.listview);
        data = new ArrayList<>();
        data.add("Oracle");
        data.add("MySQL");
        data.add("MongoDB");
        data.add("MS-SQL Server");

        //여러 개 선택 가능한 모양으로 Adapter 생성
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice, data);
        listView.setAdapter(adapter);
        //선 모양 설정
        listView.setDivider(new ColorDrawable(Color.RED));
        listView.setDividerHeight(3);
        //선택 모드를 변경
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

        iteminput = (EditText)findViewById(R.id.iteminput);
        addbtn = (Button)findViewById(R.id.addbtn);
        delbtn = (Button)findViewById(R.id.delbtn);

        addbtn.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view) {
                //유효성 검사 수행
                String item = iteminput.getText().toString().trim();
                if(item.length() < 1){
                    Toast.makeText(MultiActivity.this, "입력을 하지 않았습니다.", Toast.LENGTH_LONG).show();
                    return;
                }
                data.add(item);
                //ListView를 재출력
                adapter.notifyDataSetChanged();
                Toast.makeText(MultiActivity.this, "데이터 추가 성공", Toast.LENGTH_LONG).show();
                InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(iteminput.getWindowToken(), 0);
                //입력 뷰를 초기화
                iteminput.setText("");
            }
        });
    }
}

6. 삭제 버튼을 누르면 처리될 코드를 작성

1) Activity.java 파일에 인스턴스 변수를 추가

2) Activity.java 파일에 onCreate 메소드에 처리할 코드 작성

	delbtn.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view) {
                //listview에서 각 행에 대한 선택여부를 가져오기
                SparseBooleanArray sba = listView.getCheckedItemPositions();
                for(int i = listView.getCount()-1; i >= 0; i = i - 1){
                    if(sba.get(i) == true){
                        data.remove(i);
                    }
                }
                //선택해제
                listView.clearChoices();
                adapter.notifyDataSetChanged();
            }
        });

7. SimpleAdapter

- 하나의 행에 여러 개의 데이터를 구분해서 출력하고자 할 대 사용하는 Adapter

- 데이터는 ArrayList<HashMap<String, Sting>> 타입으로 구성된다.

- 생성을 할 때는

   new SimpleAdapter(Context context, List<Map<String, String>> list, int resourceID - 모양, String [] - key의 배열, int [] - 출력할 뷰의 모양)

8. CursorAdapter

- SQLite의 검색 결과를 출력하기 위한 Adapter

   new CursorAdapter(Context context, int resourceID - 모양, Cursor cursor, String [] - key의 배열, int [] - 출력할 뷰의 모양)

- 데이터베이스.rawQuery(String select 구문)을 데이터로 설정하면 된다.

 

** 사용자 정의 항목 뷰 사용

1. Layout Inflation

- 안드로이드는 뷰를 만드는 방법 중에 XML을 이용하는 방법이 있다.

   XML로 만든 뷰를 메모리에 할당해서 화면에 출력하는 것을 인플레이션이라고 한다.

- Activity의 전체 뷰로 설정하고자할 때는 Activity.setContentView(int id)

- 일부분으로 사용해야 하는 경우에는 Context 객체의 getSystemService 메소드를 이용해서 LayoutInflater를 찾아와서 inflate(뷰의 아이디, 부모 뷰, 뷰 그룹 여부)를 호출하면 된다.

2. BaseAdapter로 부터 상속받는 클래스를 생성

- 메소드를 재정의

   getCount : 행의 개수를 설정하는 메소드 - 여기서 리턴한 값이 행의 개수가 된다.

   getItem : position 위치의 항목을 리턴하는 메소드

   getItemId : postion 위치의 항목 뷰에 아이디를 설정하는 메소드 - position을 리턴하는 것이 일반적이다

   getView(int postion, View converView, ViewGroup parent) : 여기서 리턴한 뷰가 ListView의 각 항목이 된다.

   position은 각 항목의 인덱스

   convertView는 이전에 출력된 뷰로 처음 메소드가 호출될 때는 null이지만 두 번째 부터는 null이 아니다.

   parent는 항목이 놓이게 되는 부모 뷰 - AdapterView

 

** 셀의 왼쪽에는 이미지를 그리고 가운데는 TextView를 그리고 오른쪽에는 버튼을 배치해서 버튼을 누르면 현재 선택한 항목의 내용을 선택했다고 Toast를 출력

- 데이터의 구조는 Map을 이용할 것이고 image 키에 출력할 이미지의 id를 title 키에 TextView에 출력할 내용을 content 키에 버튼을 누르면 출력할 내용을 저장

- Map 대신에 DTO 클래스를 사용해도 된다.

   DTO 클래스를 만들거라면 인스턴스 변수를 public으로 선언해서 사용해도 된다.

- 서버는 안정성이나 신뢰성을 추구하고 여러 클라이언트가 동시에 사용하기 때문에 인스턴스 변수를 public으로 만드는 경우는 거의 없고 getter & setter를 이용해서 데이터에 접근하지만 클라이언트는 편리성을 추구하고 여러 클라이언트가 동시에 사용할 이유가 없기 대문에 대부분 변수를 public으로 해서 접근한다.

1. 실행 가능한 Activity 생성 - CustomCellUseActivity

2. 레이아웃 파일에 ListView를 전체 크기로 배치

3. Activity.java 파일에 인스턴스 변수 추가

ArrayList<Map<String, Object>> data;

4. Acitivity.java 파일의 onCreate에서 listView를 찾아서 대입하고 데이터 생성 코드 작성

package com.example.android0729;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class CustomCellUseActivity extends AppCompatActivity {
    ListView listview;

    //출력할 데이터
    ArrayList<Map<String, Object>> data;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_cell_use);
        listview = (ListView)findViewById(R.id.listview);
        data = new ArrayList<>();

        Map<String, Object> map = new HashMap<>();
        map.put("image", R.mipmap.ic_launcher);
        map.put("title", "SI");
        map.put("content", "시스템 개발");
        data.add(map);

        map = new HashMap<>();
        map.put("image", R.mipmap.ic_launcher);
        map.put("title", "QA");
        map.put("content", "품질관리 및 테스트");
        data.add(map);

        map = new HashMap<>();
        map.put("image", R.mipmap.ic_launcher);
        map.put("title", "DevOps");
        map.put("content", "개발과 운영환경 구축");
        data.add(map);

        map = new HashMap<>();
        map.put("image", R.mipmap.ic_launcher);
        map.put("title", "IoT");
        map.put("content", "인터넷 되는 기기 내장 프로그램 - Embedded");
        data.add(map);

        //어댑터 생성
        JobAdapter adapter = new JobAdapter(this, data);
        listview.setAdapter(adapter);
    }
}

5. 하나의 항목으로 사용할 뷰의 모양을 생성

- layout 디렉토리에 layout 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:padding="5dp"
    android:orientation="horizontal">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:id="@+id/image"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:textSize="20sp"
        android:id="@+id/title"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:textSize="20sp"
        android:text="선택"
        android:id="@+id/content"/>

</LinearLayout>

6. Adapter 클래스 만들기

- BaseAdapter로 부터 상속을 받아야 한다.

package com.example.android0729;

import android.content.Context;
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.TextView;

import java.util.List;
import java.util.Map;
import java.util.zip.Inflater;

public class JobAdapter extends BaseAdapter {
    Context context;
    List<Map<String, Object>> list;
    //생성자
    public JobAdapter(Context context, List<Map<String, Object>> list){
        this.context = context;
        this.list = list;
    }

    //행의 개수를 설정하는 메소드       이 메소드에서 리턴한 값만큼 아래 메소드들을 호출
    @Override
    public int getCount() {
        return list.size();
    }

    //항목을 만들어주는 메소드
    @Override
    public Object getItem(int i) {
        Map<String, Object> map = list.get(i);
        return map.get("title").toString();
    }

    //각 행의 아이디를 설정하는 메소드
    @Override
    public long getItemId(int i) {
        return i;
    }

    //첫 번째 매개변수는 행 번호      두 번째 매개변수는 출력할 뷰        세 번째 매개변수는 항목이 출력되는 AdapterView
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        final int pos = i;
        if(view == null){
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.icontext, viewGroup, false);
        }
        //이미지 출력
        ImageView image = (ImageView)view.findViewById(R.id.image);
        int imageid = (Integer)list.get(i).get("image");
        image.setImageResource(imageid);

        //텍스트 뷰
        TextView title = (TextView)view.findViewById(R.id.title);
        String titleText = (String)list.get(i).get("title");
        title.setText(titleText);

        //버튼
        Button btn = (Button)view.findViewById(R.id.content);
        btn.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view) {
                String content = (String)list.get(pos).get("content");
            }
        });
        return view;
    }

}

7. Activity.java 파일의 onCreate 수정

//어댑터 생성
        JobAdapter adapter = new JobAdapter(this, data);
        listview.setAdapter(adapter);

 

** 사용자 정의 셀 만들기

- 셀로 사용할 뷰를 레이아웃 파일로 생성

- 데이터를 받아서 레이아웃에 출력할 Adapter 클래스를 생성

- ListView에 새로 만든 Adapter 클래스의 객체를 설정