반응형


이전 포스트의 ListView 예제들은

Data로 단순한 String 문자열을 사용했습니다.


하지만 실제 어플을 개발할 때 사용하는 ListView는

나열할 항목의 Data가 String 하나로 단순하지 않은 경우가 많죠.


그래서 이전 예제에서 사용했던

ArrayAdapter를 사용하기에는 다소 부족합니다.


그래서 실제로는 AdapterView의 일종인 ListView의 항목으로

보여줄 Data들을 적절한 View로 만들어주는

Adapter 객체를 사용자정의(Customize)해야 하는 경우가 많습니다.


이번 예제는 이를 위한 예제로서

대량의 데이터로 사용할 Data를 문자열2개와 이미지 하나로 할 겁니다.


실행결과를 먼저 보시는 것이 이해가 빠르겠네요.


예제는 요즘 유명한 비정상 회담 멤버들의 국적을

표시하는 예제입니다.


간단한 ListView이지만

각 항목마다 문자열2개, 이미지 하나를 보여줍니다.



실행화면


스크롤 되는 ListView

  

각 항목마다 이름, 국적, 국기 이미지를 표시합니다.


딱 봐도 어떤 구조인지 느껴지시죠??




이제 예제 소스를 소개하겠습니다.


먼저 프로젝트를 만들어

기본 Activity에서 보여줄 xml파일을 설계합니다.


activity_main.xml 레이아웃 파일

단순히 ListView 하나와 제목 TextView 하나입니다.

 

 activity_main.xml

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

    android:padding="5dp">

    

    <TextView 

        android:layout_width="match_parent"

        android:layout_height="30dp"

        android:text="비정상회담 멤버"

        android:textStyle="bold"

        android:gravity="center"

        android:background="#dddddd"/>


    <ListView 

        android:id="@+id/listview"

        android:layout_width="match_parent"

        android:layout_height="300dp"

        android:layout_marginTop="5dp"/>


</LinearLayout>

  


Java 소스파일은 잠시뒤에 소개하고

먼저 ListView의 한 항목으로 사용할 데이터인

이름, 국적, 국기 이미지 리소스는 하나의 멤버에 대한 정보인 만큼

하나의 클래스 객체로 관리하는 것이 용이하므로

이 3개의 값(이름,국적,이미지 리소스 아이디)을 멤버변수로 보관하는

데이터 클래스를 만들어 보겠습니다.



비정상회담 국기.zip


그전에 국기 이미지는 첨부파일에서 받아 다운받셔서

압축을 풀고

이 프로젝트의 res폴더>>drawable폴더 중 타겟 해상도의 폴더에 넣으세요.

저는 drawable-hdpi에 넣었습니다.

(제 에뮬레이터geny-motion이 hdpi 이기 때문에..)



이미지를 모두 넣었다면


이제 비정상 회담 멤버 한명 한명의 이름, 국적, 국기이미지를 저장할

데이터 클래스를 만들어 보겠습니다.


MainActivity.java 파일 안에 클래스를 선언하면

복잡해 보이기에 별도의 .java 소스 파일로 만듭니다.

코딩이 편하도록 같은 패키지안에 소스파일을 만듭니다.


이름, 국적을 저장하기 위한 String 2개,

국기 이미지 리소스 아이디를 저장하기위한 int형 변수 1개

(R.drawable.korea 같은 int형 ID 값)


 

 MemberData.java

//Member의 정보를 저장하기 위한 클래스.....

public class MemberData {

String name;    //이름 저장

String nation;   //국적 저장

int imgId;      //국기 이미지의 리소스 아이디

public MemberData(String name, String nation, int imgId) {

// TODO Auto-generated constructor stub

//생성자함수로 전달받은 Member의 정보를 멤버변수에 저장..

this.name= name;

this.nation=nation;

this.imgId=imgId;

}

//이 아래는 getter , setter 메소드듭입니다.

//OOP(객체 지향 프로그래밍)은 클래스 객체 외부에서 직접 엠머변수에 접근하는 것을 지양합니다.

//객체의 멤버변수 제어는 객체 스스로 하도록 해서 재사용성을 높인 방법이죠.

//getter, setter 멤버 메소드들은 그 목적으로 만들어 진 것이죠.

public void setName(String name) {

this.name = name;

}

public void setNation(String nation) {

this.nation = nation;

}

public void setImgId(int imgId) {

this.imgId = imgId;

}

public String getName() {

return name;

}

public String getNation() {

return nation;

}

public int getImgId() {

return imgId;

}


}


특별할 것 없는 데이터 클래스 입니다.



MainActivity.java 소스파일안에 이 데이터 객체들을 만드는 작업과

ListView 참조변수 작업만 먼저 수행하겠습니니다.

주석을 참고하시기 바랍니다.

 MainActivity.java

public class MainActivity extends Activity {

//이름,국적,이미지리소스아이디를 가지고 있는 MemberData 클래스의 객체를 

//배열로 보관하기 위한 ArrayList 객체 생성

//MemberData[] 이렇게 선언하는 일반배열은 배열 개수가 정해져 있어서 나중에 추가,삭제가 불편하죠.

//배열 요소의 개수를 유동적으로 조절할 수 있는 ArrayList 객체로 data 보관

ArrayList<MemberData> datas= new ArrayList<MemberData>();

//ListView 참조변수

ListView listview;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//이번 예제에서는 우선 직접 코딩으로 멤버정보를 입력하고

//다른 예제에서 추가/삭제 관련 작업을 해보겠습니다.

//비정상 회담(요즘 유명하기에 허락없이 도용합니다.-_-)멤버 정보 생성

datas.add( new MemberData("유세윤", "대한민국", R.drawable.korea));

datas.add( new MemberData("블레어", "호주", R.drawable.australia));

datas.add( new MemberData("기욤 패트리", "캐나다", R.drawable.canada));

datas.add( new MemberData("장위안", "중국", R.drawable.china));

datas.add( new MemberData("로빈", "프랑스", R.drawable.france));

datas.add( new MemberData("다니엘", "대한민국", R.drawable.germany));

datas.add( new MemberData("알베르토", "대한민국", R.drawable.italy));

datas.add( new MemberData("샘오취리", "대한민국", R.drawable.ghana));

//귀찮아서 다 못넣겠네요...-_-

//ListView 객체 찾아와서 참조

listview= (ListView)findViewById(R.id.listview);

}

}

 


이제 가장 중요한 작업입니다.


ListView는 AdapterView의 일종입니다.


즉, 대량의 데이터를 적절한 View 객체로 만들어주는

Adapter를 보유한 ViewGroup으로서 Adapter 객체가

만들어주는 View를 나열시켜주는 View Group입니다.


 이전 포스트들에서 소개한 ArrayAdapter는 단순하게

문자열을 나열할 때 손쉽게 사용했습니다.


하지만 지금 우리의 예제처럼

문자열 2개를 위아래로..

이미지 하나를 오른쪽에..

보여주도록 하는 View는 만들 수 없습니다.


그래서 이름, 국적, 국기이미지 아이디를 저장하고 있는

데이터를 가지고 앞에 보여드렸던

실행화면의 항목처럼 배치되도록 하는

View를 만들어내는 Adapter를 만들어야 겠습니다.

즉, 커스텀 Adapter(Custom Adapter)를 만들겠다는 겁니다.



먼저 커스텀 Adapter가 만들어낼 View(List의 항목하나)의

배치(레이아웃)을 설계하는 xml 레이아웃 파일을 만들겠습니다.



res폴더>>layout폴더>>list_row.xml 파일 생성

다들 알겠지만

노파심(?)에서 만드는 과정을 소개합니다.



  

 

마지막 사진이 우리가 만들 레이아웃 xml 파일의 Graphical Layout 입니다.

커스텀 Adapter가 만들어낼 View의 모양으로

ListView의 한 항목에 해당하는 모습니다.


xml파일입니다.

 list_row.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="70dp"

    android:orientation="horizontal"

    android:padding="3dp"

    android:background="#dddddd" 

    android:weightSum="10">

    

    <LinearLayout 

        android:layout_width="0dp"

        android:layout_height="match_parent"

        android:layout_weight="7"

        android:orientation="vertical"

        android:weightSum="5">

        

        <TextView android:id="@+id/text_name"

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="3"

            android:text="NAME"

            android:textSize="18sp"

            android:textStyle="bold"

            android:gravity="center_vertical"

            android:background="#ffffff"

            android:padding="3dp"/>

        

        <TextView android:id="@+id/text_nation"

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="2"

            android:text="NATION"

            android:gravity="center_vertical"

            android:layout_marginTop="2dp"

            android:background="#ffffff"

            android:padding="3dp"/>

        

    </LinearLayout>

    

    <ImageView android:id="@+id/img_flag"

        android:layout_width="0dp"

        android:layout_height="64dp"

        android:layout_weight="3"

        android:src="@drawable/korea"

        android:background="#ffffff"

        android:layout_marginLeft="3dp"/>    


</LinearLayout>

  

이제 Adapter가 만들어낼 View의 모양을 만들었으니

실제로 대량의 Data인

비정상 회담 멤버의 이름,국적,국기이미지 데이터들을 저장하고 있는

ArrayList인 'datas'

지금 만든 list_row.xml 의 모양으로 View 객체를

만들어내는 Adapter를 만들겠습니다.


MainActivity.java 파일이 너무 복잡하니

별도의 .java 소스파일로 커스텀 Adapter class를 설계하겠습니다.


여기서 가장 중요한 것은

우리가 만들려는 커스텀 Adapter class

'BaseAdapter'를 상속받아 만든다는 겁니다.

이 클래스가 기본적인 Adapter의 기능들을 수행하도록 되어 있습니다.

이 기본 기능들을 우리의 목적에 맞게

오버라이딩(overriding)해서 원하는 동작이 되도록 할 겁니다.


혹시나 해서 또 노파심(?)에

상속받아 클래스를 만드는 과정을 보겠습니다.


   


  

마지막 그림처럼 Class 소스파일이 만들어지면 성공입니다.


이제

비정상 회담 멤버들의 정보를 View 객체로 만들어주는

MemberDataAdapter 클래스 파일을 만들어 냅니다.

주석으로 설명을 많이 해놔서

코드가 엄청 길어 보이지만..

주석을 제외한 검정 글씨만 보면 많지 않습니다.


 

 MemberDataAdapter.java

//클래스의 이름은 다른 이름이어도 상관없지만 상속만 BaseAdapter를 잘 받으면 됩니다.

public class MemberDataAdapter extends BaseAdapter {

ArrayList<MemberData> datas;

LayoutInflater inflater;

public MemberDataAdapter(LayoutInflater inflater, ArrayList<MemberData> datas) {

// TODO Auto-generated constructor stub

//객체를 생성하면서 전달받은 datas(MemberData 객체배열)를 멤버변수로 전달

//아래의 다른 멤버 메소드에서 사용하기 위해서.멤버변수로 참조값 전달

this.datas= datas;

//list_row.xml 파일을 View 객체로 생성(inflating) 해주는 역할의 객체

//이 MemberDataAdapter 클래스를 객체로 만들어내는 클래스에서 LayoutInflater 객체 전달해주야 함.

//이번 예제어세는 MainActivity.java에서 전달.

//xml 종이의 글씨를 부풀려서 객체로 메모리에 만들어 낸다고 해서 '부풀리다(inflate)'라는 표현 사용

this.inflater= inflater;

}


//ListView에서 이 MemberDataAdapter 객체에게 요청하는 값으로서

//MemberDataAdapter 객체가 만들어낼 View의 개수를 리턴하는 메소드

//ListView에 나열되는 목록의 개수를 의미함.

//이 예제에서는 datas라는 ArrayList의 개수를 지칭함.

//특별한 경우가 아니라면 보통 datas의 size 를 리턴함.

@Override

public int getCount() {

// TODO Auto-generated method stub

return datas.size(); //datas의 개수를 리턴

}

//MemberDataAdapter의 특정 위치(position)에 해당하는 Data를 리턴하는 메소드

//이 예제에서는 datas의 특정위치에 해당하는  MemberData 객체를 리턴.

//ListView의 position은 가장 위에 목록부터(0,1,2,3...)으로 지정됨.

@Override

public Object getItem(int position) {

// TODO Auto-generated method stub

return datas.get(position);//datas의 특정 인덱스 위치 객체 리턴.

}

//특정 위치(position)의 data(MemberData)을 지칭하는 아이디를 리턴하는 메소드

//특별한 경우가 아니라면 보통은 data의 위치를 아이디로 사용하므로

//전달받은 position를 그대로 리턴함.

@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

return position;

}

//이 커스텀 Adapter 클래스 설게에서 가장 중요한 메소드로서 

//ListView에서 한 항목의 View 모양과 값을 설정하는 메소드

//AdapterView의 일종인 ListView는 설정된 Adapter(이 예제에서는 MemberDataAdapter)에게

//대량의 데이터들(datas : MemberData객체 배열)을 보여주기에 적절한 View로 만들고 

//해당 데이터의 값으로 만들어 내는 핵심 메소드로서 ListView를 위에 만든 getCount()메소드의 리턴값만큼

//getView를 요구하여  목록에 나열함.

//즉, 이 메소드의 리턴값인 View 가 ListView의 한 항목을 의미합니다.

//우리는 이 리턴될 View의 모양을 res폴더>>layout폴더>>list_row.xml 파일을 만들어 설계합니다.

//첫번째 파라미터 position : ListView에 놓여질 목록의 위치.

//두번째 파라미터 convertview : 리턴될 View로서 List의 한 함목의 View 객체(자세한 건 아래에 소개)

//세번째 파라미터 parent : 이 Adapter 객체가 설정된 AdapterView객체(이 예제에서는 ListView)

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

//크게 getView의 안에서는 2가지 작업이 이루어 집니다.

//1. ListView의 목록 하나의 모양을 담당하는 View 객체를 만들어 내는 'New View'

//2. 만들어진 View에 해당 Data를 연결하는 'Bind View'

//1.New View

//지문의 한계상 자세히는 설명하기 어려우니 세부사항은 다른 포스트를 참고하시기 바랍니다.

//가장 먼저 new View를 하기 위해서 convertView 객체가 null인지 확인합니다.

//null을 확인하는 이유는 자원 재활용때문인데..

//짧게 설명하자면.. ListView에서 보여줄 목록이 만약 100개라면... 

//데이터의 양이 많아 분명 동시에 보여질 수 있는 목록의 개수를 정해져 있을 겁니다.

//그래서 이전 예제에서 보았듯이 ListView는 개수가 많으면 자동으로 Scroll되도록 되어 있지요.

//여기서 조금 생각해보시면 간단한데요.. 한번에 만약 5개 밖에 못보여진다면...

//굳이 나머지 95개의 View를 미리 만들 필요가 없겠죠.. 어차피 한번에 보여지는게 5~6개 수준이라면..

//그 정보의 View만 만들어서 안에 데이터만 바꾸면 메모리를 아낄 수 있다고 생각한 겁니다.

//여기 전달되어 체크되고 있는 converView 객체가 재활용할 View가 있으면 null이 아닌값을 가지고 

//있다고 보시면 되고 converView가 null이면 새로 만들어야 한다고 보시면 됩니다.

if( convertView==null){

//null이라면 재활용할 View가 없으므로 새로운 객체 생성

//View의 모양은 res폴더>>layout폴더>>list.xml 파일로 객체를 생성

//xml로 선언된 레이아웃(layout)파일을 객체로 부풀려주는 LayoutInflater 객체 활용.

convertView= inflater.inflate(R.layout.list_row, null);

}

//2. Bind View

//재활용을 하든 새로 만들었든 이제 converView는 View객체 상태임.

//이 convertView로부터 데이터를 입력할 위젯들 참조하기.

//이름을 표시하는 TextView, 국적을 표시하는 TextView, 국기이미지를 표시하는 ImageView

//convertView로 부터 findViewById()를 하시는 것에 주의하세요.

TextView text_name= (TextView)convertView.findViewById(R.id.text_name);

TextView text_nation= (TextView)convertView.findViewById(R.id.text_nation);

ImageView img_flag= (ImageView)convertView.findViewById(R.id.img_flag);

//현재 position( getView()메소드의 첫번재 파라미터 )번째의 Data를 위 해당 View들에 연결..

text_name.setText( datas.get(position).getName() );

text_nation.setText( datas.get(position).getNation() );

img_flag.setImageResource( datas.get(position).getImgId() );

//설정이 끝난 convertView객체 리턴(ListView에서 목록 하나.)

return convertView;

}


}



이제

MainActivity 소스파일에서 이 Adapter를 객체로 만들고

ListView에 설정하면 끝입니다.


완성된 MainActivity.java 소스파일입니다.

굵은 글씨가 새로 추가한 내용입니다.


 

 MainActivity.java

public class MainActivity extends Activity {

//이름,국적,이미지리소스아이디를 가지고 있는 MemberData 클래스의 객체를 

//배열로 보관하기 위한 ArrayList 객체 생성

//MemberData[] 이렇게 선언하는 일반배열은 배열 개수가 정해져 있어서 나중에 추가,삭제가 불편하죠.

//배열 요소의 개수를 유동적으로 조절할 수 있는 ArrayList 객체로 data 보관

ArrayList<MemberData> datas= new ArrayList<MemberData>();

//ListView 참조변수

ListView listview;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//이번 예제에서는 우선 직접 코딩으로 멤버정보를 입력하고

//다른 예제에서 추가/삭제 관련 작업을 해보겠습니다.

//비정상 회담(요즘 유명하기에 허락없이 도용합니다.-_-)멤버 정보 생성

datas.add( new MemberData("유세윤", "대한민국", R.drawable.korea));

datas.add( new MemberData("블레어", "호주", R.drawable.australia));

datas.add( new MemberData("기욤 패트리", "캐나다", R.drawable.canada));

datas.add( new MemberData("장위안", "중국", R.drawable.china));

datas.add( new MemberData("로빈", "프랑스", R.drawable.france));

datas.add( new MemberData("다니엘", "대한민국", R.drawable.germany));

datas.add( new MemberData("알베르토", "대한민국", R.drawable.italy));

datas.add( new MemberData("샘오취리", "대한민국", R.drawable.ghana));

//귀찮아서 다 못넣겠네요...-_-

//ListView 객체 찾아와서 참조

listview= (ListView)findViewById(R.id.listview);

//AdapterView의 일종인 ListView에 적용할 Adapter 객체 생성

//MemberData 객체의 정보들(이름, 국적, 이미지)를 적절하게 보여줄 뷰로 만들어주는 Adapter클래스 객체생성

//이 예제에서는 MemberDataAdapter.java 파일로 클래스를 설계하였음.

//첫번재 파라미터로 xml 레이아웃 파일을 객체로 만들어 주는 LayoutInflater 객체 얻어와서 전달..

//두번째 파라미터는 우리가 나열한 Data 배열..

MemberDataAdapter adapter= new MemberDataAdapter( getLayoutInflater() , datas);

//위에 만든 Adapter 객체를 AdapterView의 일종인 ListView에 설정.

listview.setAdapter(adapter);

}

}

 

여기까지 입니다.

다음 포스트에서는 ListView의 멤버를 추가하거나 삭제하는 예제를 소개하겠습니다.


반응형

+ Recent posts