본문 바로가기

수업 정리

80일차 수업정리(XML Parsing, Adapter)

**XML Parsing

1.XML

    => eXtensible Markup Language 의 약자

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

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

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

        - 요즈음은 HTML5 문법을 많이 사용

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

    => 자바스크립트에서 서버에 데이터를 비동기적으로 요청하여 일부분의 UI만 갱신할 필요성 등장, 이 때 채택한 기술이 AJAX(X=XML) 

    => 최근에는 REST API의 등장으로 json을 데이터 포맷으로 더 많이 이용

    => 순수하게 넘겨주는 데이터는 json을 많이 사용하지만 사람이 직접 설정하는 부분은 아직 대부분 XML

 

2. XML의 기본 문법

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



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

<Root 태그>
  데이터를 의미하는 태그와 내용
</Root>

    => XML을 생성하는 것은 직접 작성하지 않고 라이브러리를 이용하여 데이터만 설정하면 됨

 

3. XML 파싱

    => 2가지가 있는데 DOM Parsing과 SAX Parsing 2가지

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

  1) DOM(Document Object Model)

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

  2) DOM Parser

    => XML 내용을 메모리에 전부 펼쳐놓고 필요한 DOM을 찾아서 파싱

    => 처음부터 내용을 메모리에 전부 저장하므로 메모리 사용량이 많지만 속도는 빠름

    => 편집도 가능

    => Root까지 찾기

DocumentBuilderFactory factory = new DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(문자열.getBytes("인코딩 방식"));
Document document = builder.parse(inputStream);
Element root = document.getDocumentElement();

 

    => 파싱하고자 하는 태그 찾기

NodeList items = root.getElementsByTagName("태그명");
for(int i=0; i<item.length(); i += 1){
	Node item = items.get(i);
    Node text = items.getFirstChild();
    String content = text.getNodeValue();
}

 

  3) SAX Parser

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

    => 하나의 태그를 읽을 때 호출되는 콜백 메소드를 이용하여 파싱

    => 내용을 전부 메모리에 저장하지 않기 때무에 메모리 사용량이 적지만 속도는 느림

    => 편집도 불가능

    => DefaultHandlerfh부터 상속받는 클래스를 생성하여 메소드 재정의 후 사용

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

        - public void endDocument() : 

        - public void startElement(String uri, String localName, String qName, Attributes attrs)

          : 열리는 태그를 만나면 호출되는 메소드 - qName이 태그명이고 attrs가 태그의 속성들의 집합

        - public void endElement(String url, String localName, String qName)

          : 닫는 태그를 만나면 호출되는 메소드

        - public void characters(char[] chars, int start, int length) 

          : 여는 태그와 닫는 태그 사이의 문자열을 만나면 호출되는 메소드 - chars : 내용, start : 시작위치, length : 길이

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

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

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

    => 파싱 수행

        - SAXParserFactory factory = SAXParserFactory.newInstance();

        - SAXParser parser = factory.newSAXParser();

        - XMLReader reader = parser.getXMLReader();

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

        - Reader.setContentHandler(객체);

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

        - reader.parse(new InputSource(inputStream));  //핸들러 

 

4. 한겨레 신문사의 전체 기사를 파싱하여 title과 link의 내용을 가져와서 출력

    => 단 첫번째 title과 link는 제외

  1) 프로젝트 생성 - 실행가능한 Activity를 생성하거나 추가

 

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

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

    <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/hanidisplay"/>
    </ScrollView>

</LinearLayout>

 

 

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

    => 인스턴스 변수 선언

        -TextView hanidisplay;

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

        - haniDisplay = (TextView)findViewById(R.id.hanidisplay);

 

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

   //title 태그의 내용을 저장할 List
    ArrayList<String> titleList = new ArrayList<>();
    //link 태그의 내용을 저장할 List
    ArrayList<String> linkList = new ArrayList<>();
    
    //데이터를 다운로드 받아서 파싱할 스레드 클래
    class ThreadEx extends Thread{
        String xml;
        public void run(){
            //웹 서버에서 문자열 다운로드 받기
            try{
                //다운로드 받을 URL 생성
                URL url = new URL("http://www.hani.co.kr/rss/");
                
                //연결 객체 생성
                HttpURLConnection con = 
                        (HttpURLConnection)url.openConnection();
                
                //연결 옵션을 설정
                con.setRequestMethod("GET");
                con.setConnectTimeout(30000);
                con.setUseCaches(false);
                con.setDoOutput(true);
                con.setDoInput(true);
                //파일을 업로드하는 코드가 있으면 설정을 추가
                
                //파라미터를 추가 - GET 일 때는 url에 바로 추가해도 됩니다.
                
                //다운로드 받기 - 문자열 : BufferedReader, 파일 : BuffredInputStream
                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를 생성

    => 여기서 화면 출력하지않으면 handler는 필요 없음

//파싱한 결과를 받아서 출력할 핸들러 객체
    Handler handler = new Handler(Looper.getMainLooper()){
      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"
    android:orientation="vertical"
    tools:context=".ListViewActivity">
    
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listview"/>

</LinearLayout>

 

3. Activity.java 파일에 작성

public class ListViewActivity extends AppCompatActivity {
    //출력할 데이터
    String [] data;
    //출력할 뷰
    ListView listview;
    //연결할 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>
        <item>SQL</item>
    </string-array>
</resources>

 

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

    - ArrayAdapter.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(DB 커서 연결), SimpleAdapter

 

**ListView

    => BaseAdapter : 모든 종류를 이용하여 데이터를 출력하는 것이 가능

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

    => 행에 표시할 리소스 ID

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

        - android.R.layout.simple_list_item_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가 전달

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

        - AdpaterView가 notifyDataSetChanged()를 호출하면 됨

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

        - ListView의  get?Position()을 이용, 여러개 선택한 경우 getCheckedItemPosition()을 호출하여 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="30sp"
            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

ListView listView;
ArrayList<String> data;
ArrayAdapter<String> adapter;

 

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

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 = 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개 뷰에 대한 변수를 선언

    EditText iteminput;
    Button addbtn;

 

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

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

        addbtn.setOnClickListener(
                new Button.OnClickListener(){
            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 파일에 인스턴스 변수를 추가

    => Button delbtn;

  2) Activity.java 파일의 onCreate 메소드에 추가

delbtn = (Button)findViewById(R.id.delbtn);
delbtn.setOnClickListener(new Button.OnClickListener(){
	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, String>> 타입으로 구성

    => 생성시 : New SimpleAdapter(Context context, List<Map<String, String>> list, int resourceID, String[], int[])

        -  int resourceID - 모양, String[] - key의 배열, int[] - 출력할 뷰의 모양

 

8. CursorAdapter

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

        - New SimpleAdapter(Context context, int resourceID, Cursor cursor, String[], int[])

    => 데이터베이스.rawQuery(String select 구문)의 결과를 데이터로 설정하면 됨

 

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

1. Layout Inflation

    => 안드로이드는 뷰를 만드는 방법 중에 XML을 이용하는 방법이 있음

        - XML로 만든 뷰를 메모리에 할당하여 화면에 출력하는 것을 인프레이션이라고 함

    => Activity의 전체 뷰로 설정하고자 하는 경우 Activity.setContentView(int id)

    => 일부분으로 사용하는 경우 Context 객체의 getSystemService 메소드를 이용하여 LayoutInflater를 찾아와서 inflate(뷰의 아이디, 부모뷰, 뷰 그룹 여부)를 호출

 

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

    => 메소드를 재정의

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

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

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

        - getView(int position, View convertView, ViewGroup parent) : 여기서 리턴한 뷰가 ListView의 각 항목이 됨

          -> position : 각 항목의 인덱스

          -> convertView : 이전에 출력된 뷰로 처음 메소드가 호출될 때는 null, 두번째 부터는 null이 아님

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

 

**셀의 왼쪽에는 이미지, 가운데는 TextView, 오른쪽에는 버튼 배치, 클릭시 현재 선택한 항목을 선택했다고 Toast 에 출력

    => 데이터 구조 : Map을 이용, image키 - 출력할 이미지 id, title키 - TextView에 출력할 내용, content 키 - 클릭시 출력할 내용 저장

    => Map대신 DTO 클래스를 사용해도 됨

        - DTO 클래스를 만들거라면 인스턴스 변수를 public으로 선언하여 사용해도 됨

        - 서버는 안정성이나 신뢰성을 추구하고 여러 클라이언트가 동시에 사용하므로 인스턴스 변수를 getter, setter로 작성 데이터에 접근

        - 클라이언트는 편의성을 추구하고 여러 클라이언트가 동시에 사용할 이유가 없으므로 대부분의 변수를 public으로 하여 접근

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

 

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

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

</LinearLayout>

 

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

    private ListView listview;

    //출력할 데이터
    ArrayList<Map<String, String>> data;

 

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

        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", "SM");
        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);

 

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

    => 레이아웃 디렉토리에 layout을 추가 - icontext.xml

<?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로 부터 상속 받아서 메소드 재정의

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){
            //icontext.xml 파일을 전개해서 뷰로 생성
            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(){
            public void onClick(View view){
                String content = (String)list.get(
                        pos).get("content");
                Toast.makeText(
                        context, content, Toast.LENGTH_LONG)
                        .show();
            }
        });
        return view;
    }
}

 

7. Activity.java 파일의 onCreate 수정

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

 

**사용자 정의 셀 만들기

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

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

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