안드로이드 리스트뷰에서 검색된 아이템 보여주기. [textFilterEnabled] (Android ListView Item Filtering)

2016. 10. 27. 16:01


1. ListView 아이템 필터링(filtering)

안드로이드에서 ListView를 사용하는 가장 주된 목적은, "사용자가 정의한 데이터 목록을 아이템 단위로 구성하여, 화면에 반복적으로 출력"하는 것입니다. 즉, 문자열 또는 이미지로 표현되는 아이템들이 세로 방향으로 나열되어야 하는 곳에 주로 ListView가 사용되죠.


그런데 ListView를 사용하여 UI 구성할 때, 한 가지 주의할 점이 있습니다. 바로 ListView에 추가된 데이터의 갯수가 많아질 수록, 아이템 내용 식별이 쉽지 않아, 원하는 아이템을 찾기가 어려워진다는 것입니다. 이를 위해, [안드로이드 리스트뷰 아이템 정렬하기 2]에서 살펴 본 ListView에 아이템 정렬 기능을 구현하여 찾고자 하는 아이템으로의 위치 이동을 용이하게 만들 수도 있지만, 하나의 아이템에 표시되는 항목이 많으면 그마저도 쉽지 않은 경우가 많습니다. 그럼, 어떻게 하는 게 좋을까요?


이런 경우, 개발자가 선택할 수 있는 해결 방법은 ListView에 필터링(filtering) 기능을 추가하는 것입니다. 아이템의 갯수가 많아져서 원하는 아이템을 식별하기가 어려워졌으니, 사용자로부터 문자열을 입력 받은 다음, 조건에 맞는 아이템 목록만 화면에 표시하는 것이지요.

1.1 텍스트 필터링. (Text Filtering)

안드로이드 ListView에는 기본적으로 텍스트를 필터링(filtering) 할 수 있는 기능을 제공합니다. 이는 ListView의 속성 중 "textFilterEnabled"를 "true"로 지정함으로써 사용할 수 있습니다.

  * android:textFilterEnabled - 텍스트 필터링 사용 여부 지정
        > true 또는 false 값 지정 (기본  true).
        > ListView의 Adapter가 Filterable 인터페이스를 지원해야 함.

ListView의 textFilterEnabled 속성을 "true"로 지정하여 ListView의 필터링(filtering) 기능을 활성화했다면, ListView의 setFilterText() 함수를 호출하여 필터링(filtering)될 문자열을 전달해야 합니다. (이 때 전달되는 문자열은 대소문자를 구분하지 않습니다.)

    void setFilterText(String filterText)

textFilterEnabled 속성 값 설정과, setFilterText() 함수 호출 과정을 실행하고 나면, 필터링(filtering)된 문자열을 포함하는 아이템만 표시됩니다.

또한 setFilterText()에 의해 필터링(filtering)되어 표시된 결과를 초기 상태(모든 아이템 표시)로 되돌리기 위해서는 clearTextFilter() 함수를 호출하면 됩니다.

    void clearTextFilter(String filterText)

2. 기본(TextView) ListView에서 아이템 필터링(filtering) 하기

지금부터 ListView의 아이템을 필터링(filtering)하는 예제를 작성해보도록 하겠습니다. 필터링(filter) 동작의 이해를 위해, [안드로이드 리스트뷰 기본 사용법]에서 설명한 기본적인 형태(TextView로만 구성)의 ListView를 바탕으로 설명하도록 하겠습니다.


예제는 다음과 같은 형태로 구성됩니다.

리스트뷰 필터링 예제 구성도


2.1 MainActivity의 Layout 구성.

가장 먼저, 예제의 화면 구성도에 맞게 MainActivity의 Layout 리소스 XML을 작성합니다. 프로젝트의 "activity_main.xml" 또는 "content_main.xml" 파일에 작성하면 됩니다.

[STEP-1] "activity_main.xml" - MainActivity의 Layout 구성.
<?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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.listviewfilterbasic.MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="26sp"
        android:layout_marginRight="8dp"
        android:id="@+id/textView1"
        android:text="Filter Text" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/textView1"
        android:id="@+id/editTextFilter"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/editTextFilter"
        android:textFilterEnabled="true"
        android:id="@+id/listview1"/>

</RelativeLayout>

2.2 Adapter 생성 후 ListView에 지정.

다음 단계로, Adapter를 생성하여 ListView에 지정합니다. [1.1 텍스트 필터링. (Text Filtering)]에서 textFilterEnabled 속성의 설명에 나와 있듯이, textFilterEnabled 속성이 정상적으로 동작하려면 Adapter가 Filterable 인터페이스를 implements하고 있어야 합니다.


[안드로이드 리스트뷰 기본 사용법]에서 살펴본대로, 기본 형태(단순 문자열 출력)의 ListView에서는 Custom Adapter를 만들 필요 없이 안드로이드에서 제공되는 ArrayAdatper만으로 충분한 기능을 구현할 수 있습니다. 또한 ArrayAdatper는 Filterable을 implements하고 있기 때문에 텍스트 필터링(filtering) 기능을 지원합니다.

Filterable을 implements하는 ArrayAdapter


ArrayAdapter를 생성한 다음, ListView에 지정합니다.

[STEP-2] "MainActivity.java" - onCreate() 함수에서 Adatper 생성 및 ListView에 지정.
public class MainActivity extends AppCompatActivity {

    ListView listview = null ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... 코드 계속
        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1) ;
        listview = (ListView) findViewById(R.id.listview1) ;
        listview.setAdapter(adapter) ;

        // 코드 계속 ...
    }
}

2.3 ListView에 데이터 추가.

예제에서 결과를 확인하기 위한 데이터를 추가합니다.

[STEP-3] "MainActivity.java" - onCreate() 함수에서 ListView에 데이터 추가.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... 코드 계속

        adapter.add("Adam Smith") ;
        adapter.add("Bryan Adams") ;
        adapter.add("Chris Martin") ;
        adapter.add("Daniel Craig") ;
        adapter.add("Eric Clapton") ;
        adapter.add("Frank Sinatra") ;
        adapter.add("Gary Moore") ;
        adapter.add("Helloween") ;
        adapter.add("Ian Hunter") ;
        adapter.add("Jennifer Lopez") ;

        // ... 코드 계속
    }

2.4 EditText 텍스트 변경 이벤트 처리

ListView의 아이템 필터링(filtering)에 사용될 텍스트는 EditText(@id/editTextFilter)를 사용하여 입력받습니다. 하지만 별도의 "확인" 버튼을 두지 않고, EditText의 내용이 변경될 때마다 ListView의 필터링(filtering) 텍스트를 변경하도록 만들겠습니다.


EditText의 내용이 변경될 때의 이벤트 리스너는 addTextChangedListener()를 통해 전달하며, TextWatcher 인터페이스를 implements하여 전달하면 됩니다. TextWatcher 인터페이스는 3개의 함수를 가지고 있는데, EditText의 텍스트가 변경되었을 때의 이벤트는 afterTextChanged() 함수에서 처리하면 됩니다.

[STEP-4] "MainActivity.java" - EditText 텍스트 변경 이벤트 처리
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... 코드 계속

        EditText editTextFilter = (EditText)findViewById(R.id.editTextFilter) ;
        editTextFilter.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable edit) {
                // TODO : item filtering
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        }) ;
    }

2.5 ListView 아이템 필터링(filtering)

마지막으로 ListView의 setFilterText() 함수를 호출하여, 필터링(filtering) 된 아이템만 화면에 출력되도록 만듭니다.

[STEP-5] "MainActivity.java" - ListView의 아이템 필터링(filtering)
        // ... 코드 계속
        editTextFilter.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable edit) {
                String filterText = edit.toString() ;
                if (filterText.length() > 0) {
                    listview.setFilterText(filterText) ;
                } else {
                    listview.clearTextFilter() ;
                }
            }
        // 코드 계속 ...
        }) ;

3. 예제 실행 결과.

예제 코드 작성을 완료한 다음, 앱을 실행하면, 다음과 같은 화면이 표시됩니다.

리스트뷰 필터링 예제 실행화면1


EditText에 "a"를 입력하면, ListView 아이템 텍스트 중에서 "a"를 포함하는 아이템이 ListView에 표시되는 것을 확인할 수 있습니다. 그리고 입력된 "a"를 지우면 모든 아이템이 표시됩니다.

리스트뷰 필터링 예제 실행화면2


다른 텍스트를 입력하면, 해당 텍스트에 필터링(filtering)된 출력 결과가 표시되는 것을 확인할 수 있습니다.

리스트뷰 필터링 예제 실행화면1


4. 참고.

.END.


ANDROID 프로그래밍/LISTVIEW