본문으로 바로가기


안녕하세요. PEACE- 에요.

안드로이드 스터디의 [네 번째] 글이네요.


오늘은 커스텀 리스트뷰에 대해 포스팅하겠습니다.





1. ListView, CustomListView 무엇이 다른가?


안드로이드의 리스트뷰(ListView)는 주로 같은 분류에 속하는 데이터를 나열하거나거나 그 데이터에 대한 개별적 처리를 하기위해 사용합니다. 예를들면 내가 개발하는 앱 내부에 어떤 분류의 게시글이 존재하며 그 게시글을 누르면 상세 내용을 볼 수 있도록 UI를 설계했다고 생각해봅시다. 어떤 배치가 좋을까요? TextView와 같은 위젯을 사용한다면 추가해야하는 게시글이 많아질 수록 Layout의 소스는 길어지고 관리도 힘들어질겁니다. 하지만 이를 하나의 리스트뷰로 보여준다면 Layout의 소스도 줄어들 뿐만 아니라 관리도 쉬워질 것 입니다.

 

앞서 설명드린 리스트뷰(ListView)는 하나의 리스트에 한 가지의 컨텐츠(예:텍스트)만 존재하는 단순한 리스트뷰를 의미합니다. 요즘은 디자인의 중요도가 많이 높아졌기 때문에 단순 리스트뷰를 사용하기보다 하나의 리스트에 아이콘, 텍스트 등의 다양한 위젯을 담고있는 커스텀 리스트뷰(CustomListView)를 구현한 멋스럽고 기능적인 앱 개발이 많아지고 있습니다. 카카오톡을 하나의 예로 들수 있습니다.


아래 사진은 카카오톡의 프로필을 보여주는 UI입니다.


[그림 1] CustomListView 사용


[그림 2] ListView 사용



Layout에 무슨 차이가 있는지 눈에 딱 들어오나요?

첫 번째 사진은 다양한 위젯(이미지, 텍스트의 뷰 등)을 포함한 리스트뷰의 리스트를 의미하고

두 번째 사진은 단순 리스트뷰의 리스트 의미합니다. 밋밋하고 뭔가 채워넣고 싶은 마음이듭니다.


그렇다면 [그림 1]CustomListView를 구현하는 방법을 공부해보겠습니다.




2.  CustomListView 구현 방법



[그림 3] CustomListView 구현 및 작동 방식


[그림 3]을 보면 전체적으로 CustomListView가 어떻게 돌아가는지 알 수 있습니다.

우선 커스텀 리스트 뷰를 사용하기 위해서는


1. MainActivity 생성

2. MainActivity를 보여주는 Layout에 ListView 위젯 추가

3. Adapter Class 생성

4. MainActivity에 Adapter객체 생성

5. Item Class 생성

6. Adapter객체의 additem()메소드를 통해 Adapter클래스의 멤버 변수 itemArray에 아이템 세트를 add

7. 메인 레이아웃에 생성한 ListView를 MainActivity에서 Adapter객체를 인자로 setAdapter()


이 작업이 모두 수행되면 리스트의 뷰가 리턴되며 화면에 보여집니다.





3.  소스코드와 구현화면


# 구현화면






# 사용된 소스 리스트




icon.png : 이미지

activity_main.xml : 메인엑티비티의 레이아웃
MainActivity : 화면에 보여질 액티비티


listview_custom.xml : 리스트뷰에 들어갈 위젯을 구성해 놓은 시킬 레이아웃

MyAdapter : 어댑터

MyItem : 아이템 클래스





# 이미지 다운로드 (우클릭 - 다른이름으로 저장)






#소스코드


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="peace.listview.MainActivity">

<TextView
android:gravity="center"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="프로필 리스트" />

<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/textView"
android:layout_alignParentStart="true">
</ListView>

</RelativeLayout>



MainActivity.class

import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

private ListView mListView;

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

/* 위젯과 멤버변수 참조 획득 */
mListView = (ListView)findViewById(R.id.listView);

/* 아이템 추가 및 어댑터 등록 */
dataSetting();
}

private void dataSetting(){

MyAdapter mMyAdapter = new MyAdapter();


for (int i=0; i<10; i++) {
mMyAdapter.addItem(ContextCompat.getDrawable(getApplicationContext(), R.drawable.icon), "name_" + i, "contents_" + i);
}

/* 리스트뷰에 어댑터 등록 */
mListView.setAdapter(mMyAdapter);
}

}



listview_custom.xml

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

<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">

<ImageView
android:id="@+id/iv_img"
android:layout_width="wrap_content"
android:layout_height="match_parent" />

<LinearLayout
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="name.."/>

<TextView
android:layout_weight="1"
android:id="@+id/tv_contents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="contents.." />

</LinearLayout>

</LinearLayout>

</LinearLayout>



MyAdapter.class

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MyAdapter extends BaseAdapter{

/* 아이템을 세트로 담기 위한 어레이 */
private ArrayList<MyItem> mItems = new ArrayList<>();

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

@Override
public MyItem getItem(int position) {
return mItems.get(position);
}

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

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

Context context = parent.getContext();

/* 'listview_custom' Layout을 inflate하여 convertView 참조 획득 */
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.listview_custom, parent, false);
}

/* 'listview_custom'에 정의된 위젯에 대한 참조 획득 */
ImageView iv_img = (ImageView) convertView.findViewById(R.id.iv_img) ;
TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name) ;
TextView tv_contents = (TextView) convertView.findViewById(R.id.tv_contents) ;

/* 각 리스트에 뿌려줄 아이템을 받아오는데 mMyItem 재활용 */
MyItem myItem = getItem(position);

/* 각 위젯에 세팅된 아이템을 뿌려준다 */
iv_img.setImageDrawable(myItem.getIcon());
tv_name.setText(myItem.getName());
tv_contents.setText(myItem.getContents());

/* (위젯에 대한 이벤트리스너를 지정하고 싶다면 여기에 작성하면된다..) */


return convertView;
}

/* 아이템 데이터 추가를 위한 함수. 자신이 원하는대로 작성 */
public void addItem(Drawable img, String name, String contents) {

MyItem mItem = new MyItem();

/* MyItem에 아이템을 setting한다. */
mItem.setIcon(img);
mItem.setName(name);
mItem.setContents(contents);

/* mItems에 MyItem을 추가한다. */
mItems.add(mItem);

}
}



MyItem.class

import android.graphics.drawable.Drawable;

public class MyItem {

private Drawable icon;
private String name;
private String contents;

public Drawable getIcon() {
return icon;
}

public void setIcon(Drawable icon) {
this.icon = icon;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getContents() {
return contents;
}

public void setContents(String contents) {
this.contents = contents;
}

}




# 참고 사항

위의 방식은 ActivityFaragment에서 모두 동일하게 구현 가능합니다.

각 리스트의 위젯에 대한 리스너 구성은 여기 참고! -> http://mailmail.tistory.com/8



# 주의 사항

Adapter에서 getCount()를 재정의 할 때 리턴 값은 데이터의 수 만큼 해야 그 만큼 getView()가 호출된다.
















댓글공감은 환영입니다.