빠르게 배우는 안드로이드 — 리스트뷰(ListView) -1

오늘은 어플리케이션을 만들때 가장 유용하게 쓰이는 리스트뷰에 대해서 
알아보겠습니다.

리스트뷰는 페이스북에서의 타임라인시 비슷한 형태의 칸이 반복해서 내려오는것을 볼수있는데 그 리스트의 위젯을 의미합니다.

페이스북 타임라인

우리는 다음과 같은 예제 어플리케이션을 제작할것입니다.

기본적인 게시판 형태인데요 유저의 이미지와, 닉네임,글 제목,시간,내용을 표시하는 리스트뷰를 이용한 예제를 만들어볼것입니다

리스트뷰는 기본적으로 3가지 요소를 필요로합니다. 
같은 포맷의 형태가 계속 반복되는것을 볼수있는데요 이 형태를 item이라고 보통 부릅니다. 이 item의 보이는 부분을 즉 xml 부분과 내용을 가진 javacode(class)로 만듭니다. 그래서 이 데이터를 반복하여 입력해주어야하는데요 그것을 도와주는 baseAdapter라는 class를 이용해야합니다.

listView

item.class

item.xml

BaseAdapter

이 한칸을 반복해서 내용을 바꾸어서 화면에 보여주는것이 리스트뷰입니다

이 것들의 조합으로써 리스트뷰를 구성하게됩니다. 
처음에는 잘 이해가 안될수있습니다. 하지만 3번 이상 예제를 보고 반복하면 어떤것인지 느낌을 받으실수 있을것입니다.

지금부터 예제를 만들어보도록하겠습니다.

우선 리스트뷰가 필요하므로 activity_main.xml에 listview를 추가하고
id를 my_listview로 지정하여줍니다.

이제 item을 구성하는 xml을 구현할것인데요 layout폴더에 item.xml을 추가하여줍니다.

item.xml 에 다음과 같은 형태로 layout을 구성하도록합시다.

imageview의 id는 profile_imageview
그 아래의 닉네임을 보여줄 textview의 id는 nickname_textview;
상단에는 제목을 표시할 texview의 id는 title_textview
제목 바로 밑에 오른쪽에는 
글쓴 시간을 표시해줄 texview의 id는 date_textview
그 아래에는 내용을 입력해줄 texview의 id는 
content_textview 로 id를 변경하였습니다.

이제 이 xml을 반복해서 안의 내용을 바꾸어서 보여주면 우리가 원하는 리스트뷰가 될것입니다. 그렇다면 안의 내용을 구성하는 부분이 있어야 겠네요

java->자신의패키지->list_item이라는 클래스를 만들어줍니다.

클래스 안에 다음과같은 변수를 추가하여줍니다.

public class list_item {
//추가한 변수
private int profile_image;
private String nickname;
private String title;
private Date write_date;
private String content;
}
profile_image를 String이 아닌 int로 한 이유는 이번 예제에서는 안드로이드 앱 제작시에 자신이 원하는 이미지를 프로젝트에 포함하여 그 이미지를 불러오는 것을 테스트 할것이라서 int로 하였습니다. 이후 추가강의에서 외부의 이미지를 불러오는것을 테스트할때 String으로 변경하도록하겠습니다.

private으로 되어있으므로 외부에서 접근할수있도록 메소드를 만들어주어야합니다. 보통 set,get의 네이밍으로 시작하는 메소드를 만드는데요 이 부분은 안드로이드 스튜디오에서 도와줍니다.

Alt+ insert키를 누르면 Generate 라는 팝업창이 뜨는데요 거기서 
Getter And Setter를 선택하도록합시다.

다음과 같은 화면에서 변수를 모두 선택하고 Ok혹은 Enter를 누릅니다.

public class list_item {


private int profile_image;
private String nickname;
private String title;
private Date write_date;
private String content;






public int getProfile_image() {
return profile_image;
}

public void setProfile_image(int profile_image) {
this.profile_image = profile_image;
}

public String getNickname() {
return nickname;
}

public void setNickname(String nickname) {
this.nickname = nickname;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}



public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public Date getWrite_date() {
return write_date;
}

public void setWrite_date(Date write_date) {
this.write_date = write_date;
}
}

자동으로 setter와 getter를 generate해준것을 볼수있습니다.

입력시의 편의를 위해서 생성자도 만들어봅시다

alt+insert 를 다시눌러 constructor를 선택합니다.

모든 필드를 선택하고 Ok혹은 enter키를 누릅니다.

public list_item(int profile_image, String nickname, String title, Date write_date, String content) {
this.profile_image = profile_image;
this.nickname = nickname;
this.title = title;
this.write_date = write_date;
this.content = content;
}

생성자가 만들어진것을 볼수있습니다.

이제 내용을 구성하는 item클래스까지 구성을했습니다.

그렇다면 이 내용을 반복해서 보여주는데 도움을 주는 BaseAdapter를 만들도록하겠습니다. MyListAdpater클래스를 만들어줍니다.

우리는 배열 혹은 ArrayList에 item들을 담아서 순서대로 화면에 표시해줄것입니다. 따라서 다음 2가지의 멤버를 추가해줍니다.

public class MyListAdapter {
Context context;
ArrayList<list_item> list_itemArrayList;

이에 따른 생성자를 만들어줍니다. 생성자는 앞서 list_item class 에서 생성자를 만든것처럼 alt+insert 후 constructor로 생성하면됩니다.

public MyListAdapter(Context context, ArrayList<list_item> list_itemArrayList) {
this.context = context;
this.list_itemArrayList = list_itemArrayList;
}
public class MyListAdapter extends BaseAdapter

이후 다음과 같이 입력하여 BaseAdapter를 상속받습니다. 
그렇게 되면

필요한 부분을 implement 해야한다고 스튜디오에서 알려주는데요

이 역시 alt+insert를 누르고 Implement Methods를 누릅니다.

android.widget_Adapter에 있는 4가지 메소드를 모두 선택하고 Ok를 누릅니다.

다음과 같은 코드가 만들어지는것을 볼수있습니다.

public class MyListAdapter extends BaseAdapter {
Context context;
ArrayList<list_item> list_itemArrayList;
public MyListAdapter(Context context, ArrayList<list_item> list_itemArrayList) {
this.context = context;
this.list_itemArrayList = list_itemArrayList;
}
    @Override
public int getCount() {
return 0;
}

@Override
public Object getItem(int position) {
return null;
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}

이제 불러운 메소드에 조건을 하나하나 입력하면서 진행을하겠습니다.

@Override
public int getCount() {
return 0;
}
int getCount()는 이 리스트뷰가 몇개의 아이템을 가지고있는지를 알려주는 함수입니다. 우리는 arraylist의 size(갯수) 만큼 가지고있으므로
return 0 ; -> this.list_itemArrayList.size();
으로 변경합니다.
@Override
public int getCount() {
this.list_itemArrayList.size();
}
@Override
public Object getItem(int position) {
return list_itemArrayList.get(position);
}
Object getItem(int position)은 현재 어떤 아이템인지를 알려주는 부분으로 우리는 arraylist에 저장되있는 객체중 position에 해당하는것을 가져올것이므로
return null; ->return this.list_itemArrayList.get(position)으로 변경합니다.
@Override
public Object getItem(int position) {
return this.list_itemArrayList.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
은 현재 어떤 포지션인지를 알려주는 부분으로
return 0; -> return postion으로 변경합니다
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}

이 부분이 리스트뷰에서 아이템과 xml을 연결하여 화면에 표시해주는
가장 중요한 부분입니다. getView부분에서 반복문이 실행된다고 이해하시면 편하며 순차적으로 한칸씩 화면을 구성해주는 부분입니다.
(여기서 부터는 이해가 가지않으시면 패턴을 암기하시면됩니다.)
우선 convertView라는 파라미터를 메소드에서 주는데요 이 부분에 우리가 만든 item.xml을 불러와야합니다. 여기는 엑티비티가 아니므로 불러오기위한 약간의 절차가 필요한데요 그 때문에 위에서 저희가 Context를 생성자를 통해 받은것입니다.
LayoutInflater 클래스를 이용하면 다른 클래스에서도 xml을 가져올수있는데요
LayoutInflater.from(context).inflate("레이아웃.xml",null);
하면 View 클래스를 리턴해줍니다.

따라서 이 메소드를 사용하여 다음과 같이 코드를 구성해줍니다.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView = LayoutInflater.from(context).inflate(R.layout.item,null);

}
return convertView;
}

이제 convertView에 item.xml View를 불러왔습니다.
activity와 동일하게 findViewById를 이용하여 위젯의 id를 위젯 객체와 연결해주어야합니다. 따라서 findViewById를 하기전 멤버변수를 추가하여줍니다.

activity와 약간 다른점은 activity가 아니기때문에 convertView.findViewById(R.id.xxx)와 같은 형식으로 앞에 inflate받은 view를 적어주시면됩니다.

Context context;
ArrayList<list_item> list_itemArrayList;
TextView nickname_textView;
TextView title_textView;
TextView date_textView;
TextView content_textView;
ImageView profile_imageView;
@Override
public View getView(int position, View convertView, ViewGroup parent) {


if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.item,null);
nickname_textView = (TextView)convertView.findViewById(R.id.nickname_textview);
content_textView = (TextView)convertView.findViewById(R.id.content_texview);
date_textView = (TextView)convertView.findViewById(R.id.date_textview);
title_textView =(TextView)convertView.findViewById(R.id.title_textview);
profile_imageView = (ImageView)convertView.findViewById(R.id.profile_imageView);
}
     return convertView;
}

이제 setText 밑 setImage를 통해서 내용을 입력해줍니다.

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


if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.item,null);
nickname_textView = (TextView)convertView.findViewById(R.id.nickname_textview);
content_textView = (TextView)convertView.findViewById(R.id.content_texview);
date_textView = (TextView)convertView.findViewById(R.id.date_textview);
title_textView =(TextView)convertView.findViewById(R.id.title_textview);
profile_imageView = (ImageView)convertView.findViewById(R.id.profile_imageView);
}
nickname_textView.setText(list_itemArrayList.get(position).getNickname());
title_textView.setText(list_itemArrayList.get(position).getTitle());
content_textView.setText(list_itemArrayList.get(position).getContent());
date_textView.setText(list_itemArrayList.get(position).getWrite_date().toString());
profile_imageView.setImageResource(list_itemArrayList.get(position).getProfile_image());
return convertView;
}

이제 Adapter에 대한 구현은 끝났습니다. activity로 가서 listview와 Adpater를 연결하도록합시다.

MainActivity에 다음과 같은 멤버를 추가합니다.

ListView listView;
MyListAdapter myListAdapter;
ArrayList<list_item> list_itemArrayList;

listview를 findViewById를 통하여 연결합니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.my_listView);
}

arrayList 객체를 생성합니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.my_listView);
    list_itemArrayList = new ArrayList<list_item>();
}

이제 내용을 작성해줍니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.my_listView);
list_itemArrayList = new ArrayList<list_item>();
list_itemArrayList.add(
new list_item(R.mipmap.ic_launcher,"보라돌이","제목1",new Date(System.currentTimeMillis()),"내용1"));
list_itemArrayList.add(
new list_item(R.mipmap.ic_launcher,"뚜비","제목2",new Date(System.currentTimeMillis()),"내용2"));
list_itemArrayList.add(
new list_item(R.mipmap.ic_launcher,"나나","제목3",new Date(System.currentTimeMillis()),"내용3"));
list_itemArrayList.add(
new list_item(R.mipmap.ic_launcher,"뽀","제목4",new Date(System.currentTimeMillis()),"내용4"));
list_itemArrayList.add(
new list_item(R.mipmap.ic_launcher,"햇님","제목5",new Date(System.currentTimeMillis()),"내용5"));

}

이제 Adapter객체를 생성하고 listview 객체에 setAdapter를 통해
Adapter를 연결해줍니다.

myListAdapter = new MyListAdapter(MainActivity.this,list_itemArrayList);
listView.setAdapter(myListAdapter);

이제 프로젝트를 애뮬레이터에 실행해봅니다. 
다음과 같은 결과가 나오는것을 알수있습니다.

다음 시간에는 이 코드를 수정하여 조금더 최적화된 리스트뷰를 만드는과정을 알아보도록하겠습니다.

Like what you read? Give Hyeong Do Yun a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.