반응형
출처 - http://jeonglim.net/blog/tag/82
리스트의 섹션과 인덱스(패스트스크롤)만들기
2010/10/17 23:00 under [개발]
이번에 안드로이드 SI를 하나 진행했었는데 개발중에 가장 어려웠던 부분은 아이폰에서는 인덱스 라고 부르는 페스트 스크롤의 구현이 아니었나 싶다. 사실 구현한 지금도 이게 정답이 아닌데 라는 생각이 머릿속에서 떠나지 않는다. 왠만한 부분은 거의다 구글링의 도움으로 좀 세련되게 해결할 수 있었는데 이제 입문한지 1년이 갓 지난 나에게는 늘 내가 짠 코드에대한 의심이 떠나지 않는다.
안드로이드에서 기본적으로 패스트 스크롤을 지원하긴 하는데 디자인이 아이폰에 비해 정말 구리다. 패스트 스크롤을 오바라이딩해서 수정하는 방법도 있을듯 한데 잘모르겠어서 그냥 새로 만들기로 했다.
인덱스의 코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 |
//IndexBar.java import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ListView; import android.widget.SectionIndexer; public class IndexBar extends View {
private char [] l ;
private SectionIndexer sectionIndexter = null ;
private ListView list;
public IndexBar(Context context) {
super (context);
init();
} public IndexBar(Context context, AttributeSet attrs) {
super (context, attrs);
init();
} private void init() {
l = new char []{
'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z'
}; //index에 표시될 문자들.
setBackgroundColor( 0x44FFFFFF );
} public IndexBar(Context context, AttributeSet attrs, int defStyle) {
super (context, attrs, defStyle);
init();
}
public void setListView(ListView _list){
list = _list;
sectionIndexter =(SectionIndexer) _list.getAdapter();
} //간단하게 터치위치를 계산해서 섹션의 위치를 가져온다.
public boolean onTouchEvent(MotionEvent event) {
super .onTouchEvent(event);
int size = getMeasuredHeight()/(l.length+ 1 );
int i = ( int )event.getY();
int idx = i/size;
if (idx >= l.length){
idx = l.length- 1 ;
} else if (idx < 0 ){
idx = 0 ;
}
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE){
if (sectionIndexter == null ){
sectionIndexter = (SectionIndexer) list.getAdapter();
}
int position = sectionIndexter.getPositionForSection(l[idx]);
if (position == - 1 ){
return true ;
}
list.setSelection(position);
}
return true ;
} //화면에 세로로 표시될 문자들을 그린다.
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor( 0xFFA6A9AA );
paint.setTextSize( 15 );
paint.setTextAlign(Paint.Align.CENTER);
float widthCenter = getMeasuredWidth()/ 2 ;
float size = getMeasuredHeight()/(l.length+ 1 );
for ( int i = 0 ; i < l.length ; i ++){
canvas.drawText(String.valueOf(l[i]),widthCenter , size+(i*size), paint);
}
super .onDraw(canvas);
} } |
인덱스를 그리면서 제일 어려웠던건 문자들의 중앙 정렬 이었는데 수학엔 약한지라 정확한 위치를 뽑기위해 여러모로 말도안되는 계산들을 잔뜩했지만 그위치를 찾는게 쉽지 않아서 정확한 중앙정렬을 시킨지못했다. 계산하는 귀찮음 이란 ....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
<!-- adapterlayout.xml --> <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content" >
< LinearLayout android:id = "@+id/section"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
/>
< TextView android:id = "@+id/textView"
android:layout_width = "wrap_content"
android:layout_height = "80sp"
android:textSize = "45sp"
/> </ LinearLayout > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 |
<p></p><p></p><p> //CustomAdapter.java import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.SectionIndexer; import android.widget.TextView; public class CustomAdapter extends BaseAdapter implements SectionIndexer {
private ArrayList<String> stringArray;
private Context context;
public CustomAdapter(Context _context,ArrayList<String> arr){
stringArray = arr;
context = _context;
}
public int getCount() {
return stringArray.size();
} public Object getItem( int arg0) {
return stringArray.get(arg0);
} public long getItemId( int arg0) {
return 0 ;
} public View getView( int position, View v, ViewGroup parent) {
LayoutInflater inflate = ((Activity) context).getLayoutInflater();
View view = (View)inflate.inflate(R.layout.adapterlayout, null );
LinearLayout header = (LinearLayout) view.findViewById(R.id.section);
String label = stringArray.get(position);
char firstChar = label.toUpperCase().charAt( 0 );
if (position == 0 ){
setSection(header, label);
} else {
String preLabel = stringArray.get(position- 1 );
char preFirstChar = preLabel.toUpperCase().charAt( 0 );
if (firstChar != preFirstChar){
setSection(header, label);
} else {
header.setVisibility(View.GONE);
}
}
TextView textView =(TextView)view.findViewById(R.id.textView);
textView.setText(label);
return view;
}</p><p> private void setSection(LinearLayout header, String label) {
TextView text = new TextView(context);
header.setBackgroundColor( 0xffaabbcc );
text.setTextColor(Color.WHITE);
text.setText(label.substring( 0 , 1 ).toUpperCase());
text.setTextSize( 20 );
text.setPadding( 5 , 0 , 0 , 0 );
text.setGravity(Gravity.CENTER_VERTICAL);
header.addView(text);
}</p><p> /*섹션에 표시될 문자의 아스키값을 받아 해당 아스키값을 첫문자로 가지는 데이터의 위치를 반환합니다.*/
public int getPositionForSection( int section) {
if (section == 35 ){ //섹션의 아스키값이 35일 경우 0번째를 반환합니다. 아마 특수문자 였던듯 ㅡ.ㅡ;
return 0 ;
}</p><p> //전체 데이터를 순회하면서 첫문자열을 비교합니다.
for ( int i = 0 ; i < stringArray.size();i++){
String l = stringArray.get(i);
char firstChar = l.toUpperCase().charAt( 0 ); //대문자 변환후 첫번째 문자열을 가져옵니다.
if (firstChar == section){ //첫번째 문자열과 섹션에 표시될 문자열을 비교합니다.
return i; //같다면 해당 인덱스를 반환합니다.
}
}
return - 1 ;
}
public int getSectionForPosition( int arg0) {
return 0 ;
}
public Object[] getSections() {
return null ;
} }</p><p></p> |
adapter를 만들면서 가장 고민했던 부분은 섹션을 어떻게 표시할 것인가 하는 문제였다. 정말 말도안되는 다양한 상상들을 해봤는데 리스트안에 리스트를 중첩시키는 방법이라던가 섹션을 하나의 row로 만든다던가 여러가지 고민을 했었는데 결국 row에 레이아웃을 추가해놓고 높이를 wrap_content로 주는 방법을 택했다. 섹션이 들어가야 하는 자리를 찾는 방법은 여러가지가 있겠지만 바로전 문자의 첫글자와 현재 첫글자를 비교해 그전과 다를때 현재의 첫글자를 섹션에 표시하는 방법으로 구현하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
<!-- main.xml --> <? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout
android:layout_width = "fill_parent"
android:layout_height = "fill_parent" >
< ListView android:id = "@+id/list"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
/>
< com.test.indextest.IndexBar
android:id = "@+id/indexbar"
android:layout_height = "fill_parent"
android:layout_width = "26px"
android:layout_alignParentRight = "true"
/> </ RelativeLayout > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 |
//IndexTest.java import java.util.ArrayList; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; public class IndexTest extends Activity{
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView list = (ListView)findViewById(R.id.list);
ArrayList<String> stringList = setListData();
CustomAdapter adapter = new CustomAdapter( this ,stringList);
list.setAdapter(adapter);
IndexBar indexBar = (IndexBar) findViewById(R.id.indexbar);
indexBar.setListView(list);
} private ArrayList<String> setListData() {
ArrayList<String> stringList = new ArrayList<String>();
stringList.add( "above" );
stringList.add( "account" );
stringList.add( "address" );
stringList.add( "billion" );
stringList.add( "board" );
stringList.add( "build" );
stringList.add( "feature" );
stringList.add( "featurette" );
stringList.add( "further" );
stringList.add( "fuss" );
stringList.add( "fussy" );
stringList.add( "have" );
stringList.add( "haven" );
stringList.add( "hurt" );
stringList.add( "hybrid" );
stringList.add( "identify" );
stringList.add( "ignore" );
stringList.add( "illustrate" );
stringList.add( "implement" );
stringList.add( "indicate" );
stringList.add( "irrevocable" );
stringList.add( "jacket" );
stringList.add( "jackpot" );
stringList.add( "jealous" );
stringList.add( "judge" );
stringList.add( "xerox" );
stringList.add( "zone" );
return stringList;
} } |
정말 너무 허접한 코드라 다른 사람들에게 보여주기 좀 부끄럽지만 나도 편하게 구현하려고 아무리 구글링을 해도 못찾고 직접만들게 된지라 나처럼 헤메고 있는 사람들이 어떻게 구현해야 할지 고민 하는데 도움이 되길바라며 이렇게 올려본다.
완성된 화면
반응형
'개발 > 안드로이드' 카테고리의 다른 글
Android USB Accessory(USB 악세서리) (0) | 2012.07.05 |
---|---|
SQLite 현재 날짜 얻기 (0) | 2012.07.04 |
url scheme 추가하기(custom scheme 생성) (1) | 2012.06.27 |
안드로이드에서 강제로 터치 이벤트 발생시키기 (1) | 2012.06.14 |
스피너(Spinner) 사용 - 크기 및 모양 조작하기 (0) | 2012.05.25 |
댓글