2011년 03월 26일
Android Group List 만들기 (개선-아이폰 리스트처럼 만들자)
안녕하세요
오늘은 지난번에 만들었던 그룹 리스트를 개선해보겠습니다.
아이폰 앱을 보면 리스트에서 그룹 제목이 리스트 최 상단에 항상 붙어 있는 것을 볼수 있습니다.
리스트를 스크롤 해서 그룹이 바뀌게 되면 리스트 최 상단의 그룹 명도 같이 바뀌게 되구요
아이폰은 그 기능을 컴포넌트가 제공해 주고 있습니다만... 아쉽게도 안드로이드는 구현을 해 줘야 합니다.
아래 그림의 붉은색 박스에 있는 헤더가 스크롤시에도 계속 보이는거죠 그리고 내용은
스크롤되는 내용에 따라 바뀌게 되구요

그래서 지난번 그룹 리스트를 개선해서 위의 기능을 구현해보도록 하겠습니다.
아이폰과 완전히 같지는 않지만 그래도 비슷하게 폼을 낼 수는 있겠죠 ^^
위 기능을 구현하기 위해 참고로 한 소스는 구글 안드로이드 Apidemo 에 있는 리스트 9번
소스입니다.
먼저 레이아웃 xml 파일을 수정합니다.
수정된 레이아웃 파일을 보시면 아시겠지만 전체를 감싸는 레이아웃이
이전의 LinearLayout에서 RelativeLayout 으로 변경되었습니다.
RelativeLayout 을 쓴 이유는 이 레이아웃이 레이아웃 내부의
컴포넌트를 중첩시킬 수 있기 때문입니다.
아래 붉은 글씨 부분을 보시면 아시겠지만 리스트와 텍스트 뷰가 모두
같은 컴포넌트(llSearchBarLayout) 아래에 위치하고 있습니다.
이렇게 위치시키게 되면 먼저 위치시킨 LinearLayout 위로
TextView가 겹친 상태로 배치되게 됩니다.
*************************** main.xml *****************************
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:fillViewport="true" >
<!-- 검색 바 -->
<LinearLayout
android:id="@+id/llSearchBarLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="5px"
android:background="#E3E3E3"
android:layout_alignParentTop="true">
<EditText
android:id="@+id/etSearchTxt"
android:layout_width="wrap_content"
android:layout_height="45dip"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:textSize="18sp"
android:singleLine="true"/>
<ImageButton
android:id="@+id/ibtnSearch"
android:layout_height="45dip"
android:layout_width="45dip"
android:layout_gravity="right|center_vertical"
android:scaleType="centerCrop"
android:src="@drawable/btn_search"
/>
</LinearLayout>
<!-- 목록 -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:layout_weight="1"
android:layout_below="@+id/llSearchBarLayout"
android:visibility="visible">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:drawSelectorOnTop="false"
/>
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal|top"
android:textSize="20sp"
android:text="No Data"
/>
</LinearLayout>
<TextView
android:id="@+id/tvHeaderTitle"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:textSize="18sp"
android:textColor="#FFFFFF"
android:background="#8696A5"
android:paddingLeft="5dp"
android:gravity="left"
android:layout_below="@+id/llSearchBarLayout" />
</RelativeLayout>
******************************************************************
다음은 GroupList.java 파일을 수정해보겠습니다.
먼저 ListView.OnScrollListener 를 implements 받아야 합니다.
스크롤에 따라 리스트 최상단 그룹 제목이 변해야 하기때문입니다.
중요한 부분은 onscroll 이벤트를 처리하는 onScroll 메서드가 될것입니다.
GroupList.java의 전체 소스는 다음과 같습니다.
수정된 부분은 그리 많지 않습니다. onScroll 메서드가 추가되었고
나머지는 소소한 수정입니다.
onScroll 메서드에서는 스크롤이 발생할때 발생하는 이벤트인데
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
위와 같은 인자들을 넘겨 받습니다. 로그로 찍어 보시면 아시겠지만
firstVisibleItem 는 현재 스크롤 상태에서 리스트의 최 상단 아이템의
위치를 int 값으로 넘겨 받습니다. 이 위치값은 리스트의 전체 데이터에서의
절대 위치값이기때문에 이 위치값을 이용해 커서를 움직이면
실제로 데이터를 가져올 수 있게 됩니다.
우리는 이 firstVisibleItem 인자를 가지고 커서를 움직여서
현재 리스트의 최 상단에 나와야 할 그룹명을 찾을 것입니다.
************************* GroupList.java ***************************
package com.android.grouplist;
import com.android.grouplist.GroupListInfo.TbNote;
import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LayoutAnimationController;
import android.view.animation.TranslateAnimation;
import android.widget.AbsListView;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
public class GroupList extends ListActivity implements View.OnClickListener, ListView.OnScrollListener
{
private GroupListDAO mGroupListDAO;
private ImageButton mIbtnSearch;
private EditText mEtSearchTxt;
private TextView mTvHeaderTitle;
private Cursor mTbNoteCursor;
private NoteCursorAdapter mAdapter;
private ListView mListView;
private boolean mReady;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView = getListView();
mListView.setTextFilterEnabled(true);
mListView.setOnScrollListener(this);
mIbtnSearch = (ImageButton) findViewById(R.id.ibtnSearch);
mEtSearchTxt = (EditText) findViewById(R.id.etSearchTxt);
mTvHeaderTitle = (TextView) findViewById(R.id.tvHeaderTitle);
mIbtnSearch.setOnClickListener(this);
mGroupListDAO = new GroupListDAO(getApplicationContext());
}
@Override
protected void onResume()
{
super.onResume();
mReady = true;
}
@Override
protected void onPause()
{
super.onPause();
mReady = false;
}
@Override
protected void onDestroy()
{
super.onDestroy();
mReady = false;
try
{
if (mTbNoteCursor != null)
mTbNoteCursor.close();
mGroupListDAO.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
try
{
if (mReady)
{
mTbNoteCursor.moveToPosition(firstVisibleItem);
String firstString = mTbNoteCursor.getString(mTbNoteCursor.getColumnIndex(TbNote.DATE));
mTvHeaderTitle.setText(firstString);
}
}
catch (Exception e)
{}
}
public void onScrollStateChanged(AbsListView view, int scrollState)
{}
// 버튼의 OnClick 이벤트 처리
@Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.ibtnSearch: // Search Button
findNoteList();
break;
default:
break;
}
}
private void findNoteList()
{
String searchTxt = mEtSearchTxt.getText().toString();
mTbNoteCursor = mGroupListDAO.selectNoteList(searchTxt);
if (mTbNoteCursor != null)
Log.v("GroupList", "mTbNoteCursor.count >>>>>>>>>>>>>>>" + mTbNoteCursor.getCount());
mAdapter = new NoteCursorAdapter(this, R.layout.note_list_item_w_header, mTbNoteCursor, new String[] {TbNote.NOTE}, null);
setListAdapter(mAdapter);
AnimationSet set = new AnimationSet(true);
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(100);
set.addAnimation(animation);
animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
animation.setDuration(200);
set.addAnimation(animation);
LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f);
mListView.setLayoutAnimation(controller);
}
}
*******************************************************************
소스 코드는 위와 같고 이를 실행시키면 다음처럼 실행됩니다.
이제 스크롤을 해보면 최상단 그룹명(붉은색 박스)은 고정된 상태로
스크롤 되면서 고정된 헤더의 내용은 현재 데이터의 그룹명으로 계속
변경될 것입니다.

이상으로 GroupList를 개선해 보았습니다.
# by | 2011/03/26 02:36 | Android | 트랙백 | 덧글(4)





☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
정리 너무 잘해주셔서 이해하기도 쉽고
항상잘보고있어요 ㅋ 화이팅!!
또 필요한 정보가....ㅠㅠㅠㅠㅠㅠㅠ
많이 배워 갑니다ㅎㅎ