descendantFocusability - Child View의 Focus를 제어하자
descendantFocusability
Child View의 Focus를 제어하자
ㆍ 시작하기 전에 descendantfocusablility라는 이 속성은
몇몇 특별한 상황에서 필요합니다.
ㆍ 물론 사용하는 방법에 따라 유용하게 쓰일수도 있습니다.
ㆍ 보통 ListView, WebView, VideoView에서 사용됩니다.
오늘은 3번째인
VideoView를 사용할때
문제가 생길 수 있는 상황에서
descendantFocusability 속성을
사용해서 해결해보도록 하겠습니다.
(바쁘신 분들은 글 하단에 해결방법을
보시면 될 것 같습니다.
저는 모든지 실험을 해보는 성격이라..
앞에 내용이 지루할수도 있습니다..)
어떤 상황인지?
VideoView를 사용할 때
ScrollView까지 동시에 사용하는
상황입니다.
그리고 그 VideoView가 상단에
존재하지 않을때가 되겠네요.
그림으로 한번 보면
그림이 참 허접하지만 ㅠㅠ.
이런식으로 VideoView가
최상단에 있지 않은 상황입니다.
xml 코드로 한번 보면
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.tistory.itpangpang.resourceex.MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:text="TextView"/>
<CheckBox
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:hint="CheckBox"/>
<VideoView
android:layout_width="match_parent"
android:layout_height="200dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:text="TextView2"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
이렇게 되겠죠?
이 상태에서 만약
앱을 실행한다면 어떻게 될까요?
위에 gif를 잘 보면
앱을 실행했을때
화면이 최초에 최상단이 아닌
VideoView가 있는곳으로
이동되어있는 것을 확인할 수 있습니다.
여기서 왜 이러한 문제가
생기나 생각해보면
당연히 focus 관련문제겠죠?
확인해 보겠습니다.
앱 실행시 현재 포커스를
체크해보겠습니다.
public class MainActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
}
}
역시나 예상대로 VideoView에
Focus가 잡힌것을 확인할 수 있습니다.
그럼 간단하게 Focus를 제거하면 될까요?
public class MainActivity extends AppCompatActivity
{
VideoView vv;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vv = (VideoView)findViewById(R.id.vv);
vv.setFocusableInTouchMode(false);
vv.setFocusable(false);
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
}
}
이런식으로 먼저 해보겠습니다.
결과는 변한게 없습니다.(실패..)
그럼 이번에는 가장 최상단에
위치하고 있는 TextView에
Focus를 강제로 넣어보겠습니다.
public class MainActivity extends AppCompatActivity
{
VideoView vv;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vv = (VideoView)findViewById(R.id.vv);
tv = (TextView)findViewById(R.id.tv);
/*vv.setFocusable(false);
vv.setFocusableInTouchMode(false);*/
tv.setFocusableInTouchMode(true);
tv.requestFocus();
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
}
}
오 Focus가 TextView로 갔습니다.
그럼 해결되었을까요??
오호! 성공입니다.
그런데 어떤 상황에서는
위 방법이 안먹힌다는
얘기도 들었습니다..
(도대체 어떤 상황이길래...)
그래서!!
모든 상황에서
문제해결을 하기 위한
방법을 찾아봤습니다.
문제해결방법!
제목에도 있드시
descendantFocusability
속성을 이용해주는 방법입니다.
이 놈이 어떤속성인가!
한번 보기 위해
Reference를 참고해보겠습니다.
역시나 저도 영어를 못하지만..
느낌정도는 알 수 있기에..
ViewGroup과 그것 자식들(descendants)의
관계(relationship)를 정의한다..
when.. 뷰가 포커스를 갖는것을 발견했을때?
이 정도 느낌이 되겠네요..
(틀릴수도 있는..ㅠㅠ)
여기를 슥~ 한번 보면
value 2가 눈에 확 띕니다.
(blocksDescendants)
ViewGroup will block!
its descendants from receiving focus!!
자식들이 포커스 받는것을
막을 수 있다! 뭐 이런 내용이겠네요
바로 써보겠습니다.
자.. 과연 어디다가 써야할까요?
ScrollView?? - No!
여기서 헷갈리시면 안됩니다.
ScrollView의 바로 밑 자식은
하나 밖에 없습니다
(ScrollView는 자식을
하나밖에 둘 수 없는 것도 중요하죠!)
바로 위치는 ScrollView의 자식인
LinearLayout에 적어줘야 합니다
VideoView는 LinearLayout과
바로 연결되는 자식입니다..
(ScrollView한테는 손자,손녀 정도 되겠네요)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
이렇게 써주시면 됩니다.
전체 xml로 보자면
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.tistory.itpangpang.resourceex.MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:text="TextView"/>
<CheckBox
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:hint="CheckBox"/>
<VideoView
android:id="@+id/vv"
android:layout_width="match_parent"
android:layout_height="200dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:textSize="30dp"
android:text="TextView2"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
이렇게 됩니다!
결과를 확인해보면?
아!! 잘나옵니다.
문제해결은 여기서 완료했습니다.
하지만 혹시! 위 상황때문에
또 다른 문제점이 생길수 있습니다.
(개발은 어떤상황이 닥칠지 모르니..)
자식들의 포커스를 막아버렸으니? 또 다른 문제점?
혹시 모르니 이것도 적도록 하겠습니다
.
위에서 VideoView와 그 친구들이 있는
부모의 View인 LinearLayout에
android:descendantFocusability="blocksDescendants"
처리를 해놨습니다.
아무 문제가 없을것 같지만.
위 속성을 자세히 생각해보면
앱이 실행될때, 또는 해당 화면에 들어왔을때
!!그때만 막는게 아니라 계속 막는 다는 이야기입니다..
이게 무슨 얘기인가 하면.
상황에 따라서는 requestFocus()를 통해서
특정View에 Focus를 넣을 수도 있는데
위 속성을 넣는다면 이게 불가능 하다는 얘기입니다.
코드로 보자면
public class MainActivity extends AppCompatActivity
{
VideoView vv;
TextView tv;
LinearLayout ll;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vv = (VideoView)findViewById(R.id.vv);
tv = (TextView)findViewById(R.id.tv);
ll = (LinearLayout)findViewById(R.id.ll);
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
tv.setFocusableInTouchMode(true);
tv.requestFocus();
Log.d("ITPANGPANG","request 후 포커스 : " +getCurrentFocus());
}
});
}
}
자 처음 들어올때는
Focus를 막았다고 쳐봅시다.
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
이 부분을 보면
이렇게 Focus가 없겠죠?
그런데 상황에 따라서는
TextView를 선택했을때
Focus를 가져오고 싶을 경우가 있습니다.
그래서 위 코드에서 TextView를 click하면
강제로 requestFocus를 받도록 해놨습니다.
tv.setFocusableInTouchMode(true);
tv.requestFocus();
Log.d("ITPANGPANG","request 후 포커스 : " +getCurrentFocus());
결과를 보면
예상과 다르게
TextView에 Focus가
잡히지 않는 것을 확인 할 수 있습니다.
뭐 엄청 심각한것 처럼 말했지만..
해결방법은 간단합니다..
코드로 descendantfocusability 속성을
원래대로 변경해주면 됩니다.!!
public class MainActivity extends AppCompatActivity
{
VideoView vv;
TextView tv;
LinearLayout ll;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vv = (VideoView)findViewById(R.id.vv);
tv = (TextView)findViewById(R.id.tv);
ll = (LinearLayout)findViewById(R.id.ll);
Log.d("ITPANGPANG","현재 포커스 : " +getCurrentFocus());
ll.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
tv.setFocusableInTouchMode(true);
tv.requestFocus();
Log.d("ITPANGPANG","request 후 포커스 : " +getCurrentFocus());
}
});
}
}
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
이렇게 Focus Block을 풀고 싶을때 써주시면 됩니다.
위와 같이 써주고
다시 TextView를 선택해보면
제대로 TextView에 다시
Focus가 잡힌것을 확인 할 수 있습니다.
아! 왜 하필
FOCUS_BEFORE_DESCENDATNTS
적었는가 하면?
위 값이 defalut 값입니다.
xml 속성에서
android:descendantFocusability="??"
이것을 안적어줬을때랑 똑같습니다.
끝!!
*글 내용은 작성자가 직접 테스트해서 나온 결과이므로 틀리는 부분이
있을 수도 있으니 이 점 참고해주세요!!
'안드로이드(android) > 위젯(Widget)' 카테고리의 다른 글
EditText textCursorDrawable - Cursor color 변경 (1) | 2017.02.12 |
---|---|
TextView 줄간격, 자간, 장평 변경하기 (0) | 2017.01.26 |
EditText Keyboard show/hide 이벤트 잡기 (3) | 2016.11.25 |
EditText Keyboard 내리기 (0) | 2016.11.24 |
TextView, Button 등등 Focus 주기 (0) | 2016.11.22 |