박상권의 삽질블로그

[안드로이드/Android]EditText를 커스텀하여 'X'버튼 추가하기 - ClearEditText 본문

IT/Android-TIP (한글)

[안드로이드/Android]EditText를 커스텀하여 'X'버튼 추가하기 - ClearEditText

박상권 2016. 4. 12. 14:05

안드로이드 개발자들이 모여있는 오픈채팅방에 참여해보세요 .
Q&A 및 팁을 공유하는 방입니다..
오픈채팅방 참여



블로그를 Medium으로 옮겨서 운영하고 있습니다.
앞으로 새로운 글은 모두 미디엄 블로그를 통해서 올릴 예정입니다.
미디엄에서 다양하고 유익한 포스팅을 살펴보세요
미디엄 블로그 보기


우리는 EditText를 구현하면서 사용자의 편의를 위해서 EditText와 겹쳐서 'X'버튼을 추가하는 경우가 많습니다.

'X'버튼을 누르면 EditText의 텍스트 내용을 초기화하는 작업을 해줍니다.


아래 화면은 제가 운영중인 셀폰이라는 앱에서 이름을 변경하는 화면입니다.

사용자가 이름을 입력하게 되면 오른쪽에 'X'버튼 아이콘이 보여지고 사용자가 'X'버튼을 누르는경우 EditText의 모든 내용을 삭제할수 있도록 해주고 있습니다.








사실 이러한 패턴은 많은 안드로이드 앱에서 볼수 있습니다.

안드로이드 SDK tool인 uiautomatorviewer를 이용하여 카카오톡의 '대화방 검색' EditText를 살펴보아도 역시 레이아웃에 EditText와 ImageButton을 겹쳐서 구현해 놓은것을 알 수 있습니다.





위의 셀폰과 카카오톡뿐만 아니라 이와 비슷한 방식으로 구현하는 앱들은 대부분 아래와 같은 방식으로 구현되어 있을것입니다.

1. 레이아웃 xml에 EditText와 ImageButton(혹은 ImageView)를 겹쳐서 배치(X버튼은 맨 오른쪽으로 위치)

2. EditText에 TextWatcher를 추가

3. TextWatcher의 onTextChanged()에서 Text의 길이에 따라 X버튼 보여주기/숨기기



이게 엄청 복잡한 작업은 아니긴 합니다.

하지만 앱의 기능이 많아질수록 EditText의 개수가 늘어나게 되면 위와같은 반복작업을 해주어야 합니다.









귀찮습니다





네, 맞습니다. 이러한 작업이 반복되면 귀찮아 집니다.

우리는 귀찮은걸 참을수 없습니다.













ClearEditText만들기!

'X'버튼을 함께 포함하고있는 EditText를 커스텀해서 만들어 보겠습니다.

(설명 읽기 귀찮으신분들은 Github에서 바로 다운받아서 실행해보세요)








ClearEditText 에서 구현해야 하는 기능은 아래와 같습니다.

1. 'X'버튼을 EditText에 추가

2. 텍스트 길이에 따라 'X'버튼 보이기/숨기기

3. 'X'버튼이 눌리는 경우 텍스트 초기화

4. EditText에 포커스가 있을때에만 'X'버튼을 보이기








1. EditText에 'X'버튼 추가


AppCompatEditText를 상속받은 ClearEditText 클래스를 만들어 줍니다.

기본으로 생기는 생성자도 3개 모두 추가해줍니다.


public ClearEditText(final Context context) {
super(context);
init();
}

public ClearEditText(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}

public ClearEditText(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

각 생성자에서 호출할 init()함수를 만들어줍니다.

init()에서는 'X'버튼을 추가해주고 Touch,Focus,TextWatcher 리스너를 추가해줄것입니다.




이제 우리는 EditText에 'X' 버튼을 추가해주어야 합니다.

여기서는 X버튼으로 appcompat에서 제공하는 abc_ic_clear_mtrl_alpha 를 사용하도록 하겠습니다.

혹은 자신이 원하는 'X'버튼을 이미지로 사용해도 좋습니다.



Drawable tempDrawable = ContextCompat.getDrawable(getContext(), R.drawable.abc_ic_clear_mtrl_alpha);
clearDrawable = DrawableCompat.wrap(tempDrawable);
DrawableCompat.setTintList(clearDrawable,getHintTextColors());
clearDrawable.setBounds(0, 0, clearDrawable.getIntrinsicWidth(), clearDrawable.getIntrinsicHeight());

setClearIconVisible(false);


롤리팝 이하버전을 위해 Compat을 이용해서 wrapDrawable을 만들어주고

DrawableCompat을 이용해서 X이미지를 hint의 색깔에 맞춰서 같은 색으로 맞출수 있도록 Tint를 적용해줍니다.

getIntrinsicWidth()와 getIntrinsicHeight()를 이용해서 크기를 지정해 줍니다.


private void setClearIconVisible(boolean visible) {
clearDrawable.setVisible(visible, false);
setCompoundDrawables(null,null, visible ? clearDrawable : null,null);
}

setCompoundDrawables()는 setDrawablePadding과 같은 효과를 줍니다. X버튼이 보여져야 하는경우 EditText의 오른쪽에 위치시킵니다.












2. 텍스트 길이에 따라 X버튼 보이기/없애기


init()에서 addTextChangedListener(this)를 추가해줍니다.


@Override
public final void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
if (isFocused()) {
setClearIconVisible(s.length() > 0);
}
}

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

@Override
public void afterTextChanged(Editable s) {
}

Focus상태일때만 판단을 하며 텍스트의 길이가 0보다 크면 보여주고 그렇지 않으면 보여주지 않습니다.












3. X버튼이 눌리는경우 텍스트 초기화


init()에서 super.setOnTouchListener(this)를 추가해줍니다.



@Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
final int x = (int) motionEvent.getX();
if (clearDrawable.isVisible() && x > getWidth() - getPaddingRight() - clearDrawable.getIntrinsicWidth()) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
setError(null);
setText(null);
}
return true;
}

if (onTouchListener != null) {
return onTouchListener.onTouch(view, motionEvent);
} else {
return false;
}

}


X버튼이 보여지고 있고 터치 위치가 X버튼의 위치일경우 text를 초기화 시킵니다.

텍스트 에러를 표시해주고 있었다면 에러도 없애주기 위해 setError(null) 처리 해줍니다.

이 ClearEditText 에도 터치리스너가 추가 될수 있으므로 터치리스너를 설정할수있는 함수를 제공해주고 터치리스너에게 터치값을 넘겨주어야 합니다.


@Override
public void setOnTouchListener( OnTouchListener onTouchListener) {
this.onTouchListener = onTouchListener;
}












4. EditText에 포커스가 있을때에만 X버튼을 보이기


init()에서 super.setOnFocusChangeListener(this)를 추가해줍니다.


@Override
public void onFocusChange(final View view, final boolean hasFocus) {
if (hasFocus) {
setClearIconVisible(getText().length() > 0);
} else {
setClearIconVisible(false);
}

if (onFocusChangeListener != null) {
onFocusChangeListener.onFocusChange(view, hasFocus);
}
}


이 EditText에 포커스가 있는경우는 텍스트의 길이에 따라서 X버튼의 표시여부를 결정합니다.

포커스가 없는경우는 X버튼을 보여주지 않습니다.

위의 터치리스너와 마찬가지로 포커스리스너 또한 이 EditText에 추가될수 있으므로 설정할수 있는 함수를 제공하고 포커스변화값을 넘겨주어야 합니다.


@Override
public void setOnFocusChangeListener( OnFocusChangeListener onFocusChangeListener) {
this.onFocusChangeListener = onFocusChangeListener;
}














'아몰랑 그냥 전체 소스코드만 알려줘' 

라는 분들을 위해 ClearEditText 전체 소스코드를 준비했습니다.





이것도 귀찮으시다면? Github에서 프로젝트를 다운받아서 바로 테스트해보세요.


(내용이 유용하셨다면 Github 오른쪽위의 [Star]버튼을 눌러주시면 감사하겠습니다

저에게는 별풍선 주시는 효과가 있어요!)





여기에 추가할 유용한 기능들을 생각해볼 수 있습니다.

- xml에서 X버튼 이미지를 지정해서 추가해줄수 있도록 하기

- 현재 입력된 글자수를 표시해주는 EditText만들기

- EditText의 내용이 유효하지 않은경우 빨간밑줄 혹은 EditText Shake해주기


'CustomView만들기'에 대해서 좀더 궁금하시다면 아래 글을 참고하세요

[IT/Android-TIP (한글)] - [안드로이드]CustomView를 만들어서 재사용하기






이글은 Giving your Edit Texts the All Clear 에서 영감을 받아 작성되었습니다.

(사실 거의 대부분 갖다 썼...)



감사합니다.



Comments