ListView 내부의 Focusable EditText


121

나는 지금까지 약 6 시간을 보냈고, 장애물에 부딪혔다. 일반적인 전제는 ListView(어댑터에 의해 생성되었는지 또는 헤더보기로 추가되었는지 여부에 관계없이) EditText위젯 및 Button. 내가 원하는 것은 조그 볼 / 화살표를 사용하여 선택기를 평소와 같이 개별 항목으로 이동할 수있는 것뿐입니다.하지만 특정 행에 도달하면 (명시 적으로 행을 식별해야하는 경우에도) 포커스가있는 선택기를 사용하여 위치를 표시하는 대신 그 아이가 초점을 맞추기를 원합니다.

나는 많은 가능성을 시도했지만 지금까지 운이 없었습니다.

나열한 것:

<ListView
    android:id="@android:id/list" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    />

헤더보기 :

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);

어댑터에 다른 항목이 있다고 가정하면 화살표 키를 사용하여 예상대로 목록에서 선택 항목을 위 / 아래로 이동합니다. 그러나 머리글 행 EditText에 도달 하면 선택기와 함께 표시되며 조그 볼을 사용하여 집중할 방법이 없습니다 . 참고 :을 탭하면 해당 지점에 초점 EditText 맞춰 지지만 터치 스크린에 의존하므로 필수 사항은 아닙니다.

ListView이 점에서 분명히 두 가지 모드가 있습니다.
1. setItemsCanFocus(true): 선택기가 표시되지 않지만 EditText화살표를 사용하면 초점을 맞출 수 있습니다. 포커스 검색 알고리즘은 예측하기 어렵고 어떤 항목이 선택되었는지에 대한 시각적 피드백 (모든 행 : 포커스 가능한 자식 포함 여부)이 없으므로 둘 다 사용자에게 예기치 않은 경험을 제공 할 수 있습니다.
2. setItemsCanFocus(false): 선택기는 항상 비 터치 모드로 그려지며, EditText탭해도 초점을 맞출 수 없습니다.

설상가상으로, 호출 editTextView.requestFocus()은 true를 반환하지만 실제로는 EditText 포커스를 제공하지 않습니다.

내가 구상하고있는 것은 기본적으로 1과 2의 하이브리드입니다. 모든 항목에 초점을 맞출 수 있는지 여부를 목록 설정이 아닌 목록 의 단일 항목에 대해 초점을 설정 하여 선택기가 선택에서 원활하게 전환되도록합니다. 초점을 맞출 수없는 항목에 대한 전체 행과 초점을 맞출 수있는 하위 항목이 포함 된 항목에 대한 초점 트리를 순회합니다.

수취인이 있습니까?

답변:


101

죄송합니다. 제 질문에 답했습니다. 가장 정확하거나 가장 우아한 솔루션이 아닐 수도 있지만 저에게 효과적이며 매우 견고한 사용자 경험을 제공합니다. 두 가지 동작이 왜 그렇게 다른지 확인하기 위해 ListView의 코드를 살펴본 결과 ListView.java에서이 문제를 발견했습니다.

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }

따라서를 호출 할 때 setItemsCanFocus(false)자식이 포커스를받을 수 없도록 하위 포커스 가능성도 설정합니다. 이것은 mItemsCanFocusListView의 OnItemSelectedListener에서 토글 할 수없는 이유를 설명합니다 . ListView가 모든 자식에 대한 포커스를 차단했기 때문입니다.

내가 지금 가지고있는 것 :

<ListView
    android:id="@android:id/list" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent"
    android:descendantFocusability="beforeDescendants"
    />

beforeDescendants선택기는 ListView 자체 (자식 아님)에 포커스가있을 때만 그려지기 때문에 사용 하므로 기본 동작은 ListView가 먼저 포커스를 받고 선택기를 그리는 것이어야합니다.

그런 다음 OnItemSelectedListener에서 선택기를 재정의하려는 헤더 뷰를 알고 있으므로 (주어진 위치에 포커스 가능한 뷰가 포함되어 있는지 동적으로 확인하려면 더 많은 작업이 필요함) 하위 포커스 가능성을 변경하고 EditText에 포커스를 설정할 수 있습니다. 그 헤더에서 벗어나면 다시 변경합니다.

public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
    if (position == 1)
    {
        // listView.setItemsCanFocus(true);

        // Use afterDescendants, because I don't want the ListView to steal focus
        listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        myEditText.requestFocus();
    }
    else
    {
        if (!listView.isFocused())
        {
            // listView.setItemsCanFocus(false);

            // Use beforeDescendants so that the EditText doesn't re-take focus
            listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            listView.requestFocus();
        }
    }
}

public void onNothingSelected(AdapterView<?> listView)
{
    // This happens when you start scrolling, so we need to prevent it from staying
    // in the afterDescendants mode if the EditText was focused 
    listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}

주석 처리 된 setItemsCanFocus호출에 유의하십시오 . 이러한 호출로 올바른 동작을 얻었지만 setItemsCanFocus(false)포커스가 EditText에서 ListView 외부의 다른 위젯으로 이동하고 ListView로 돌아가서 다음 선택된 항목에 선택기를 표시했으며 그 점프 포커스가 산만 해졌습니다. ItemsCanFocus 변경 사항을 제거하고 하위 포커스 기능을 전환하여 원하는 동작을 얻었습니다. 모든 항목은 정상적으로 선택기를 그리지 만 EditText가있는 행에 도달하면 대신 텍스트 필드에 초점을 맞췄습니다. 그런 다음 해당 EditText를 계속할 때 선택기를 다시 그리기 시작했습니다.


매우 시원하고 아직 테스트하지 않았습니다. 1.5, 1.6 및 3.0에서 테스트 했습니까?
Rafael Sanches

Rafael Sanches : 2.1 이후 프로젝트에 손을 대지 않았지만 당시에는 1.5, 1.6 및 2.1에서 작업이 확인되었습니다. 2.2 이상에서 여전히 작동한다는 보장은 없습니다.
Joe

13
android : descendantFocusability = "afterDescendants"만 필요-어쨌든 +1
kellogs

5
@kellogs : 예, descendantFocusability="afterDescendants"EditText가 ListView 내에서 포커스를 받도록 허용하지만 dpad로 탐색하는 동안에는 목록 항목 선택기 표시 되지 않습니다 . 내 작업은 EditText가있는 행을 제외한 모든 행에 목록 항목 선택기를 갖는 것이 었습니다. 그래도 도움이되었습니다. FWIW, 우리는이 구현을 재평가하고 ListView 내부의 focusable이 관용적 인 Android UI 디자인이 아니라고 결정했기 때문에 더 Android 친화적 인 접근 방식을 위해 아이디어를 폐기했습니다.
Joe

5
이것은 아이스크림 샌드위치에서 테스트 되었습니까? 나는 그것을 작동시킬 수 없습니다. 감사.
라자 Anantharam

99

이것은 나를 도왔다.
매니페스트에서 :

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>

3
관련성이 있는지 잘 모르겠습니다. windowSoftInputMode를 설정하면 IME가 열려있을 때 나머지 창 내용을 조정하는 방식 만 변경됩니다. ListView의 포커스 유형을 선택적으로 변경할 수 없습니다. 이것이 초기 사용 사례와 어떤 관련이 있는지 조금 더 설명해 주시겠습니까?

1
@Joe IME가 열리면 커서와 포커스도 내 화면에서 이동하여 텍스트를 입력 할 수 없게됩니다. 당신 OnItemSelectedListener은 그것을 바꾸지 않습니다. 그러나 Iogan의 간단한 솔루션은 매력처럼 작동합니다. 감사합니다!
Gubbel

2
@Gubbel : 사실, 원래 질문이 완전히 다른 것에 관한 것이기 때문에 전혀 변경되지 않을 것입니다. :) 기쁜 로건의 수정은 당신이 찾고 있던 것에 대해 작동하지만 단순히 질문과 원격으로 관련이 없습니다.
Joe

8
이 플러스 조의 사용 android:descendantFocusability재산 것은 내 가지고 EditText안에들 ListView키보드를 적절히 해결, 둘 다 upvoted. android:descendantFocusability그 자체로는 트릭을 수행하지 않았고 내가 처리해야하는 @Overriding onItemSelected모든 14 EditText초 에 대해 원격으로 열정적이지 않았습니다 . :) 감사!
Thomson Comer 2011

이것은 나를 위해 일했지만 TabHost 또는 TabActivity를 사용하는 경우 매니페스트의 TabActivity 정의에 대해 android : windowSoftInputMode =”adjustPan”을 설정해야합니다.
kiduxa 2013-06-08

18

내 임무는 ListView클릭하면 확장되는 것을 구현 하는 것이 었습니다 . 추가 공간은 EditText텍스트를 입력 할 수있는 위치를 보여줍니다 . 앱은 2.2 이상에서 작동해야합니다 (이 문서 작성시 최대 4.2.2).

이 게시물과 내가 찾을 수있는 다른 솔루션에서 수많은 솔루션을 시도했습니다. 2.2 ~ 4.2.2 장치에서 테스트했습니다. 2.2+ 모든 장치에서 만족스러운 솔루션은 없었으며 각 솔루션은 서로 다른 문제를 나타 냈습니다.

최종 솔루션을 공유하고 싶었습니다.

  1. listview를 다음으로 설정 android:descendantFocusability="afterDescendants"
  2. listview를 다음으로 설정 setItemsCanFocus(true);
  3. 당신의 활동을 android:windowSoftInputMode="adjustResize" 많은 사람들이 제안 adjustPan하지만 adjustResize훨씬 더 나은 ux imho를 제공합니다. 당신의 경우에 이것을 테스트하십시오. 와adjustPan 당신이 얻을 것이다 아래 많은 ListItems는 예를 들어 가려. 문서에 따르면 ( "일반적으로 크기 조정보다 바람직하지 않음"). 또한 4.0.4에서 사용자가 소프트 키보드로 입력을 시작하면 화면이 맨 위로 이동합니다.
  4. 4.2.2에서는 adjustResizeEditText 포커스에 몇 가지 문제가 있습니다. 해결책은이 스레드에서 rjrjr 솔루션을 적용하는 것입니다. 무섭게 보이지만 그렇지 않습니다. 그리고 작동합니다. 먹어봐.

추가 5.EditText HoneyComb 이전 버전에 초점을 맞출 때 어댑터가 새로 고쳐 졌기 때문에 (보기 크기 조정으로 인해) 반전 된보기에서 문제가 발견 되었습니다. 2.2에서 ListView 항목보기 / 역순으로보기; 4.0.3에서 작동

일부 애니메이션을 수행하는 경우 adjustPan크기 조정이 실행되지 않고 어댑터가 뷰를 새로 고치지 않도록 벌집 이전 버전의 동작을로 변경할 수 있습니다. 다음과 같이 추가하면됩니다.

if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

이 모든 것이 2.2-4.2.2 장치에서 허용 가능한 UX를 제공합니다. 이 결론에 도달하는 데 최소한 몇 시간이 걸렸기 때문에 사람들이 시간을 절약 할 수 있기를 바랍니다.


1
제 경우에 충분한 첫 번째와 두 번째 단계
Kalpesh Lakhani

ArrayAdapter의 xElement.setOnClickListener (..) 및 ListView.setOnItemClickListener (...)와 완벽하게 작동합니다. 큰 thx.
Javatar

10

이것은 내 생명을 구했습니다 --->

  1. 이 줄을 설정

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. 그런 다음 활동 태그의 매니페스트에 this->를 입력하십시오.

    <activity android:windowSoftInputMode="adjustPan">

평소의 의도


7

우리는보기 재활용을하지 않는 짧은 목록에서 이것을 시도하고 있습니다. 여태까지는 그런대로 잘됐다.

XML :

<RitalinLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
  <ListView
      android:id="@+id/cart_list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbarStyle="outsideOverlay"
      />
</RitalinLayout>

자바:

/**
 * It helps you keep focused.
 *
 * For use as a parent of {@link android.widget.ListView}s that need to use EditText
 * children for inline editing.
 */
public class RitalinLayout extends FrameLayout {
  View sticky;

  public RitalinLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    ViewTreeObserver vto = getViewTreeObserver();

    vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
      @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if (newFocus == null) return;

        View baby = getChildAt(0);

        if (newFocus != baby) {
          ViewParent parent = newFocus.getParent();
          while (parent != null && parent != parent.getParent()) {
            if (parent == baby) {
              sticky = newFocus;
              break;
            }
            parent = parent.getParent();
          }
        }
      }
    });

    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override public void onGlobalLayout() {
        if (sticky != null) {
          sticky.requestFocus();
        }
      }
    });
  }
}

약간 수정 된이 솔루션은 @ # ( : if (sticky! = null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); sticky = null;}
Chris van de Steeg

4

이 게시물은 내 키워드와 정확히 일치했습니다. 검색 EditText 및 검색 버튼이있는 ListView 헤더가 있습니다.

초기 초점을 잃은 후 EditText에 초점을 맞추기 위해 내가 찾은 유일한 해킹은 다음과 같습니다.

    searchText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            // LOTS OF HACKS TO MAKE THIS WORK.. UFF...
            searchButton.requestFocusFromTouch();
            searchText.requestFocus();
        }
    });

많은 시간을 잃었고 실제 수정이 아닙니다. 힘든 사람에게 도움이되기를 바랍니다.


2

목록이 동적이고 포커스 가능한 위젯을 포함하는 경우 올바른 옵션은 ListView IMO 대신 RecyclerView 를 사용하는 것입니다.

adjustPan, 를 설정 FOCUS_AFTER_DESCENDANTS하거나 초점이 맞춰진 위치를 수동으로 기억하는 해결 방법은 실제로 해결 방법 일뿐입니다. 코너 케이스 (스크롤 + 소프트 키보드 문제, EditText에서 캐럿 위치 변경)가 있습니다. 그들은 ListView가 .NET 에서 한꺼번에 뷰를 생성 / 파괴한다는 사실을 변경하지 않습니다 notifyDataSetChanged.

RecyclerView를 사용하면 개별 삽입, 업데이트 및 삭제에 대해 알립니다. 포커스가있는 뷰가 다시 생성되지 않으므로 폼 컨트롤이 포커스를 잃는 문제가 없습니다. 추가 보너스로 RecyclerView는 목록 항목 삽입 및 제거에 애니메이션을 적용합니다.

다음은 시작하는 방법에 대한 공식 문서의 예입니다 RecyclerView. 개발자 가이드-RecyclerView로 목록 만들기


1

android:windowSoftInputMode="stateAlwaysHidden"매니페스트 활동 또는 xml에서 사용하면 키보드 포커스를 잃을 수 있습니다. 따라서 먼저 xml 및 매니페스트에서 해당 속성을 확인하십시오.있는 경우 제거하십시오. 이 옵션을 사이드 액티비티의 매니페스트 파일에 android:windowSoftInputMode="adjustPan"추가하고이 속성을 xml의 listview에 추가 한 후android:descendantFocusability="beforeDescendants"


0

또 다른 간단한 해결책은 ListAdapter의 getView (..) 메서드에서 onClickListener를 정의하는 것입니다.

public View getView(final int position, View convertView, ViewGroup parent){
    //initialise your view
    ...
    View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
    ...

    //define your listener on inner items

    //define your global listener
    row.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            doSomethingWithViewAndPosition(v,position);
        }
    });

    return row;

그렇게하면 행을 클릭 할 수 있고 내부보기도 가능합니다. :)


1
질문은 클릭 가능성이 아니라 초점과 관련이 있습니다.

0

가장 중요한 부분은 목록 셀에 초점을 맞추는 것 입니다. 특히 Google TV 목록의 경우 필수입니다.

목록보기의 setItemsCanFocus 메서드가 트릭을 수행합니다.

...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...

내 목록 셀 xml은 다음과 같이 시작됩니다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/puzzleDetailFrame"
             android:focusable="true"
             android:nextFocusLeft="@+id/gameprogress_lessDetails"
             android:nextFocusRight="@+id/gameprogress_reset"
...

nextFocusLeft / Right는 D-Pad 탐색에도 중요합니다.

자세한 내용은 다른 훌륭한 답변을 확인하십시오.


0

방금 다른 해결책을 찾았습니다. 나는 그것이 해결책 이라기보다 해킹이라고 생각하지만 그것은 안드로이드 2.3.7과 안드로이드 4.3에서 작동합니다 (저는 그 좋은 오래된 D 패드를 테스트했습니다)

평소대로 웹보기를 초기화하고 다음을 추가하십시오 : (Michael Bierman에게 감사드립니다)

listView.setItemsCanFocus(true);

getView 호출 중 :

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });

view.requestFocus에서 여기보기는 무엇입니까?
Narendra Singh

이 오래된 대답하지만 OnFocusChangeListener I에 대한 인수로 주어진 범위에서보기로 생각 함
alaeri

1
초점 변경에 루프가 발생합니다.
Morteza Rastgoo

0

그냥 시도 해봐

android:windowSoftInputMode="adjustNothing"

활동

매니페스트 섹션. 예, 아무것도 조정하지 않습니다. 즉, IME가 열릴 때 editText가 그대로 유지됩니다. 그러나 그것은 여전히 ​​초점을 잃는 문제를 완전히 해결하는 작은 불편 일뿐입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.