Focusable EditText bên trong ListView


121

Cho đến nay, tôi đã dành khoảng 6 giờ cho việc này và không gặp phải vấn đề gì ngoài rào cản. Tiền đề chung là có một số hàng trong a ListView(cho dù nó được tạo bởi bộ điều hợp hay được thêm vào dưới dạng chế độ xem tiêu đề) có chứa EditTexttiện ích con và a Button. Tất cả những gì tôi muốn làm là có thể sử dụng phím bấm / mũi tên để điều hướng bộ chọn đến các mục riêng lẻ như bình thường, nhưng khi tôi đến một hàng cụ thể - ngay cả khi tôi phải xác định rõ ràng hàng - có thể lấy tiêu điểm con, tôi muốn con đó lấy nét thay vì chỉ ra vị trí bằng bộ chọn.

Tôi đã thử nhiều khả năng, và cho đến nay vẫn chưa gặp may.

bố trí:

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

Chế độ xem tiêu đề:

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

Giả sử có các mục khác trong bộ điều hợp, sử dụng các phím mũi tên sẽ di chuyển lựa chọn lên / xuống trong danh sách, như mong đợi; nhưng khi đến hàng tiêu đề, nó cũng được hiển thị bằng bộ chọn và không có cách nào để tập trung vào hàng EditTextbằng cách sử dụng bóng chạy. Lưu ý: việc chạm vào EditText sẽ lấy nét tại điểm đó, tuy nhiên thao tác này phụ thuộc vào màn hình cảm ứng, điều này không phải là bắt buộc.

ListViewrõ ràng có hai chế độ về vấn đề này:
1 setItemsCanFocus(true).: bộ chọn không bao giờ được hiển thị, nhưng EditTextcó thể lấy nét khi sử dụng các mũi tên. Thuật toán tìm kiếm tiêu điểm khó dự đoán và không có phản hồi trực quan (trên bất kỳ hàng nào: có con có thể lấy tiêu điểm hay không) về mục nào được chọn, cả hai đều có thể mang lại cho người dùng trải nghiệm không mong đợi.
2 setItemsCanFocus(false).: bộ chọn luôn được vẽ ở chế độ không cảm ứng và EditTextkhông bao giờ có thể lấy nét - ngay cả khi bạn chạm vào nó.

Để làm cho vấn đề tồi tệ hơn, việc gọi editTextView.requestFocus()trả về true, nhưng trên thực tế không mang lại tiêu điểm EditText.

Những gì tôi đang hình dung về cơ bản là sự kết hợp giữa 1 và 2, trong đó thay vì cài đặt danh sách nếu tất cả các mục đều có thể lấy tiêu điểm hay không, tôi muốn đặt khả năng lấy tiêu điểm cho một mục duy nhất trong danh sách, để bộ chọn chuyển đổi liền mạch từ việc chọn toàn bộ hàng cho các mục không có tiêu điểm và đi ngang qua cây tiêu điểm cho các mục chứa các mục có thể lấy tiêu điểm.

Bất kỳ người dự thi?

Câu trả lời:


101

Xin lỗi, đã trả lời câu hỏi của riêng tôi. Nó có thể không phải là giải pháp chính xác nhất hoặc thanh lịch nhất, nhưng nó phù hợp với tôi và mang lại trải nghiệm người dùng khá vững chắc. Tôi đã xem xét mã cho ListView để xem tại sao hai hành vi lại khác nhau như vậy và bắt gặp điều này từ ListView.java:

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

Vì vậy, khi gọi setItemsCanFocus(false), nó cũng thiết lập khả năng lấy nét của con cháu để không đứa trẻ nào có thể lấy nét. Điều này giải thích tại sao tôi không thể chỉ chuyển đổi mItemsCanFocustrong OnItemSelectedListener của ListView - bởi vì ListView khi đó đang chặn tiêu điểm đối với tất cả trẻ em.

Những gì tôi có bây giờ:

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

Tôi sử dụng beforeDescendantsvì bộ chọn sẽ chỉ được vẽ khi bản thân ListView (không phải con) có tiêu điểm, vì vậy hành vi mặc định cần là ListView lấy tiêu điểm trước và vẽ các bộ chọn.

Sau đó, trong OnItemSelectedListener, vì tôi biết chế độ xem tiêu đề nào tôi muốn ghi đè bộ chọn (sẽ mất nhiều công sức hơn để xác định động xem có vị trí nhất định nào chứa chế độ xem có thể lấy tiêu điểm hay không), tôi có thể thay đổi khả năng lấy nét của con cháu và đặt tiêu điểm trên EditText. Và khi tôi điều hướng ra khỏi tiêu đề đó, hãy thay đổi lại tiêu đề đó.

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);
}

Lưu ý các setItemsCanFocuscuộc gọi đã nhận xét . Với các lệnh gọi đó, tôi đã có hành vi chính xác, nhưng setItemsCanFocus(false)khiến tiêu điểm nhảy từ EditText, sang một widget khác bên ngoài ListView, quay trở lại ListView và hiển thị bộ chọn trên mục được chọn tiếp theo và tiêu điểm nhảy đó gây mất tập trung. Loại bỏ thay đổi ItemsCanFocus và chỉ cần chuyển đổi khả năng lấy nét của con cháu đã giúp tôi có được hành vi mong muốn. Tất cả các mục sẽ vẽ bộ chọn như bình thường, nhưng khi đến hàng với EditText, nó tập trung vào trường văn bản. Sau đó, khi tiếp tục ra khỏi EditText đó, nó bắt đầu vẽ lại bộ chọn.


rất tuyệt, chưa thử nghiệm. Bạn đã thử nghiệm trên 1.5, 1.6 và 3.0 chưa?
Rafael Sanches

Rafael Sanches: Tôi chưa chạm vào dự án kể từ 2.1, nhưng vào thời điểm đó, nó đã được xác nhận là hoạt động trong 1.5, 1.6 và 2.1. Tôi không đảm bảo rằng nó vẫn hoạt động trong phiên bản 2.2 trở lên.
Joe

13
chỉ cần android :cendantFocusability = "afterDescendants" - anyway +1
kellogs

5
@kellogs: vâng, descendantFocusability="afterDescendants"sẽ cho phép EditText của bạn tập trung vào bên trong ListView, nhưng sau đó bạn không nhận được bộ chọn mục danh sách khi điều hướng bằng dpad. Nhiệm vụ của tôi là có bộ chọn mục danh sách trên tất cả các hàng ngoại trừ hàng có EditText. Rất vui vì nó đã giúp. FWIW, chúng tôi đã kết thúc việc đánh giá lại việc triển khai này và quyết định rằng một tiêu điểm có thể tập trung bên trong ListView không phải là thiết kế giao diện người dùng Android-ngu ngốc, vì vậy chúng tôi đã loại bỏ ý tưởng để chuyển sang cách tiếp cận thân thiện với Android hơn.
Joe

5
Điều này đã được thử nghiệm trên Ice Cream Sandwich chưa? Tôi không thể làm cho nó hoạt động. Cảm ơn.
Rajat Anantharam

99

Điều này đã giúp tôi.
Trong tệp kê khai của bạn:

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

3
Tôi không chắc mình thấy sự liên quan. Cài đặt windowSoftInputMode chỉ thay đổi cách IME điều chỉnh phần còn lại của nội dung cửa sổ khi nó đang mở. Nó không cho phép bạn thay đổi có chọn lọc loại tiêu điểm của ListView. Bạn có thể giải thích thêm một chút, điều này liên quan như thế nào đến trường hợp sử dụng ban đầu?
Joe

1
@Joe Khi IME được mở, con trỏ - và có thể cả tiêu điểm - chỉ nhảy xung quanh màn hình của tôi, khiến tôi không thể nhập văn bản. Của bạn OnItemSelectedListenerkhông thay đổi điều đó. Tuy nhiên giải pháp đơn giản của Iogan hoạt động như một sự quyến rũ, cảm ơn!
Gubbel

2
@Gubbel: Thật vậy, nó sẽ không thay đổi điều đó chút nào, bởi vì câu hỏi ban đầu là về một thứ hoàn toàn khác :) Rất vui là bản sửa lỗi của logan hoạt động cho những gì bạn đang tìm kiếm, nhưng nó đơn giản là thậm chí không liên quan từ xa đến câu hỏi.
Joe

8
Sử dụng này cộng với Joe android:descendantFocusabilitytài sản có của tôi EditTexts bên trong một ListViewgiải quyết bàn phím đúng cách, bỏ phiếu tán cả. android:descendantFocusabilitytự nó đã không thực hiện thủ thuật và tôi không nhiệt tình từ xa @Overriding onItemSelectedtrong suốt 14 EditTextgiây mà tôi phải đối phó. :) Cảm ơn!
Thomson Comer,

Điều này phù hợp với tôi, nhưng hãy lưu ý nếu bạn đang sử dụng TabHost hoặc TabActivity, bạn phải đặt android: windowSoftInputMode = ”AdjustPan” cho định nghĩa TabActivity trong tệp kê khai.
kiduxa

18

Nhiệm vụ của tôi là triển khai ListViewphần mở rộng khi được nhấp vào. Khoảng trống bổ sung hiển thị EditTextnơi bạn có thể nhập một số văn bản. Ứng dụng phải hoạt động trên 2.2+ (lên đến 4.2.2 tại thời điểm viết bài này)

Tôi đã thử nhiều giải pháp từ bài đăng này và những giải pháp khác mà tôi có thể tìm thấy; đã thử nghiệm chúng trên thiết bị 2.2 đến 4.2.2. Không có giải pháp nào phù hợp trên tất cả các thiết bị 2.2+, mỗi giải pháp đưa ra các vấn đề khác nhau.

Tôi muốn chia sẻ giải pháp cuối cùng của mình:

  1. đặt listview thành android:descendantFocusability="afterDescendants"
  2. đặt listview thành setItemsCanFocus(true);
  3. đặt hoạt động của bạn thành android:windowSoftInputMode="adjustResize" Nhiều người đề xuất adjustPannhưng adjustResizemang lại hiệu quả tốt hơn nhiều, chỉ cần kiểm tra điều này trong trường hợp của bạn. Với adjustPanbạn sẽ nhận được listitems dưới che khuất ví dụ. Các tài liệu gợi ý rằng ("Điều này thường ít mong muốn hơn là thay đổi kích thước"). Cũng trên 4.0.4 sau khi người dùng bắt đầu gõ trên bàn phím mềm, màn hình sẽ xoay lên trên cùng.
  4. trên 4.2.2 với adjustResizemột số vấn đề với tiêu điểm EditText. Giải pháp là áp dụng giải pháp rjrjr từ luồng này. Nó trông có sẹo nhưng không phải vậy. Và nó hoạt động. Hãy thử nó.

Bổ sung 5. Do bộ điều hợp được làm mới (vì thay đổi kích thước chế độ xem) khi EditTextlợi ích tập trung vào các phiên bản HoneyComb trước, tôi đã tìm thấy sự cố với các chế độ xem bị đảo ngược: nhận Chế độ xem cho mục ListView / thứ tự ngược lại trên 2.2; hoạt động trên 4.0.3

Nếu bạn đang thực hiện một số hoạt ảnh, bạn có thể muốn thay đổi hành vi đối adjustPanvới các phiên bản trước tổ ong để thay đổi kích thước không kích hoạt và bộ điều hợp không làm mới chế độ xem. Bạn chỉ cần thêm một cái gì đó như thế này

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

Tất cả điều này mang lại ux có thể chấp nhận được trên các thiết bị 2.2 - 4.2.2. Hy vọng nó sẽ giúp mọi người tiết kiệm thời gian vì tôi đã mất ít nhất vài giờ để đưa ra kết luận này.


1
chỉ cần bước đầu tiên và bước thứ hai là đủ trong trường hợp của tôi
Kalpesh Lakhani

Hoạt động hoàn hảo với xElement.setOnClickListener (..) của tôi trong ArrayAdapter và ListView.setOnItemClickListener (...) - cuối cùng !! Thx lớn.
Javatar

10

Điều này đã cứu mạng tôi --->

  1. đặt dòng này

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Sau đó, trong tệp kê khai của bạn trong thẻ hoạt động, hãy nhập mã này ->

    <activity android:windowSoftInputMode="adjustPan">

Ý định thông thường của bạn


7

Chúng tôi đang thử điều này trên một danh sách ngắn không tái chế chế độ xem nào. Càng xa càng tốt.

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>

Java:

/**
 * 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();
        }
      }
    });
  }
}

Được sửa đổi một chút, giải pháp này có hiệu quả với tôi sau 2 ngày kể từ @ # ( : if (stick! = Null) {stick.RequestFocus (); stick.RequestFocusFromTouch (); stick = null;}
Chris van deectorsg

4

bài đăng này khớp chính xác với các từ khóa của tôi. Tôi có một tiêu đề ListView với một EditText tìm kiếm và một nút tìm kiếm.

Để cung cấp tiêu điểm cho EditText sau khi mất tiêu điểm ban đầu, HACK duy nhất mà tôi tìm thấy là:

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

Mất nhiều giờ và nó không phải là một sửa chữa thực sự. Hy vọng nó sẽ giúp một ai đó khó khăn.


2

Nếu danh sách là động và chứa các tiện ích con có thể tập trung, thì tùy chọn phù hợp là sử dụng RecyclerView thay vì ListView IMO.

Các giải pháp thay thế cho việc đặt adjustPan, FOCUS_AFTER_DESCENDANTShoặc ghi nhớ vị trí lấy nét theo cách thủ công, thực sự chỉ là các giải pháp thay thế. Chúng có các trường hợp góc (vấn đề cuộn + bàn phím mềm, dấu mũ thay đổi vị trí trong EditText). Chúng không thay đổi thực tế là ListView tạo / hủy các chế độ xem hàng loạt trong suốt thời gian đó notifyDataSetChanged.

Với RecyclerView, bạn thông báo về từng lần chèn, cập nhật và xóa. Chế độ xem tập trung không được tạo lại nên không có vấn đề gì với các điều khiển biểu mẫu mất tiêu điểm. Như một phần thưởng bổ sung, RecyclerView tạo hoạt ảnh cho việc chèn và xóa mục danh sách.

Đây là ví dụ từ các tài liệu chính thức về cách bắt đầu RecyclerView: Hướng dẫn dành cho nhà phát triển - Tạo danh sách với RecyclerView


1

một số lần khi bạn sử dụng android:windowSoftInputMode="stateAlwaysHidden"trong hoạt động tệp kê khai hoặc xml, lúc đó nó sẽ mất tiêu điểm bàn phím. Vì vậy, trước tiên hãy kiểm tra thuộc tính đó trong xml và tệp kê khai của bạn, nếu có, chỉ cần xóa nó. Sau khi thêm tùy chọn này vào tệp kê khai trong hoạt động bên android:windowSoftInputMode="adjustPan"và thêm thuộc tính này vào listview trong xmlandroid:descendantFocusability="beforeDescendants"


0

Một giải pháp đơn giản khác là xác định onClickListener của bạn, trong phương thức getView (..), của ListAdapter.

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;

Bằng cách đó hàng của bạn có thể nhấp được và cả chế độ xem bên trong của bạn :)


1
Câu hỏi liên quan đến tiêu điểm, không phải khả năng nhấp chuột.
Joe

0

Phần quan trọng nhất là lấy tiêu điểm làm việc cho ô danh sách. Đặc biệt đối với danh sách trên Google TV, điều này là cần thiết:

Phương thức setItemsCanFocus của dạng xem danh sách thực hiện thủ thuật:

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

Xml ô danh sách của tôi bắt đầu như sau:

<?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 cũng rất quan trọng đối với điều hướng D-Pad.

Để biết thêm chi tiết, hãy xem các câu trả lời tuyệt vời khác.


0

Tôi chỉ tìm thấy một giải pháp khác. Tôi tin rằng đó là một bản hack hơn là một giải pháp nhưng nó hoạt động trên Android 2.3.7 và Android 4.3 (Tôi thậm chí đã thử nghiệm chiếc D-pad cũ tốt đó)

init webview của bạn như bình thường và thêm cái này: (cảm ơn Michael Bierman)

listView.setItemsCanFocus(true);

Trong cuộc gọi getView:

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

xem ở đây là gì trong view.requestFocus?
Narendra Singh

đây là một câu trả lời cũ nhưng nó đề cập đến chế độ xem trong phạm vi được đưa ra như một đối số cho OnFocusChangeListener mà tôi tin rằng
alaeri

1
Nó gây ra một vòng lặp trong việc thay đổi tiêu điểm.
Morteza Rastgoo

0

Chỉ cần thử cái này

android:windowSoftInputMode="adjustNothing"

bên trong

Hoạt động

trong tệp kê khai của bạn. Có, nó không điều chỉnh gì cả, có nghĩa là editText sẽ ở nguyên vị trí khi IME đang mở. Nhưng đó chỉ là một chút bất tiện nhỏ mà vẫn giải quyết triệt để vấn đề mất tập trung.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.