EditText yang Dapat Difokuskan di dalam ListView

121

Saya telah menghabiskan sekitar 6 jam sejauh ini, dan tidak menemukan apa-apa selain rintangan. Premis umumnya adalah bahwa ada beberapa baris dalam a ListView(apakah itu dibuat oleh adaptor, atau ditambahkan sebagai tampilan header) yang berisi EditTextwidget dan file Button. Yang ingin saya lakukan adalah dapat menggunakan jogball / panah, untuk menavigasi selektor ke item individual seperti biasa, tetapi ketika saya sampai ke baris tertentu - bahkan jika saya harus secara eksplisit mengidentifikasi baris - yang memiliki fokus yang dapat difokuskan Nak, saya ingin anak itu mengambil fokus daripada menunjukkan posisi dengan selektor.

Saya sudah mencoba banyak kemungkinan, dan sejauh ini tidak berhasil.

tata letak:

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

Tampilan header:

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

Dengan asumsi ada item lain di adaptor, menggunakan tombol panah akan memindahkan pilihan ke atas / bawah dalam daftar, seperti yang diharapkan; tetapi ketika sampai ke baris header, itu juga ditampilkan dengan selektor, dan tidak ada cara untuk fokus ke dalam EditTextmenggunakan jogball. Catatan: mengetuk pada EditText akan memfokuskannya pada saat itu, namun itu bergantung pada layar sentuh, yang seharusnya tidak menjadi persyaratan.

ListViewrupanya memiliki dua mode dalam hal ini:
1 setItemsCanFocus(true).: selektor tidak pernah ditampilkan, tetapi EditTextbisa mendapatkan fokus saat menggunakan panah. Algoritme penelusuran fokus sulit untuk diprediksi, dan tidak ada umpan balik visual (pada baris mana pun: memiliki turunan yang dapat difokuskan atau tidak) pada item mana yang dipilih, yang keduanya dapat memberikan pengalaman yang tidak terduga kepada pengguna.
2 setItemsCanFocus(false).: selektor selalu digambar dalam mode non-sentuh, dan EditTexttidak pernah bisa mendapatkan fokus - bahkan jika Anda mengetuknya.

Untuk memperburuk keadaan, memanggil editTextView.requestFocus()mengembalikan true, tetapi pada kenyataannya tidak memberikan fokus EditText.

Apa yang saya bayangkan pada dasarnya adalah gabungan dari 1 & 2, di mana daripada pengaturan daftar jika semua item dapat difokuskan atau tidak, saya ingin mengatur fokusabilitas untuk satu item dalam daftar, sehingga selektor transisi mulus dari memilih seluruh baris untuk item yang tidak dapat difokuskan, dan melintasi pohon fokus untuk item yang berisi turunan yang dapat difokuskan.

Ada yang mau?

Joe
sumber

Jawaban:

101

Maaf, jawab pertanyaan saya sendiri. Ini mungkin bukan solusi yang paling tepat atau paling elegan, tetapi berhasil untuk saya, dan memberikan pengalaman pengguna yang cukup solid. Saya melihat kode untuk ListView untuk melihat mengapa kedua perilaku tersebut sangat berbeda, dan saya menemukan ini dari ListView.java:

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

Jadi, saat menelepon setItemsCanFocus(false), itu juga mengatur fokusabilitas turunan sehingga tidak ada anak yang bisa fokus. Ini menjelaskan mengapa saya tidak bisa begitu saja beralih mItemsCanFocusdi OnItemSelectedListener ListView - karena ListView kemudian memblokir fokus untuk semua anak.

Apa yang saya miliki sekarang:

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

Saya menggunakan beforeDescendantskarena selektor hanya akan digambar ketika ListView itu sendiri (bukan anak-anak) memiliki fokus, jadi perilaku defaultnya adalah ListView mengambil fokus terlebih dahulu dan menarik penyeleksi.

Kemudian di OnItemSelectedListener, karena saya tahu tampilan header mana yang ingin saya ganti selektornya (akan membutuhkan lebih banyak pekerjaan untuk secara dinamis menentukan apakah ada posisi tertentu yang berisi tampilan yang dapat difokuskan), saya dapat mengubah fokusabilitas turunan, dan menetapkan fokus pada EditText. Dan saat saya keluar dari header itu, ubah kembali.

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

Perhatikan setItemsCanFocuspanggilan yang diberi komentar . Dengan panggilan itu, saya mendapatkan perilaku yang benar, tetapi setItemsCanFocus(false)menyebabkan fokus melompat dari EditText, ke widget lain di luar ListView, kembali ke ListView dan menampilkan selektor pada item yang dipilih berikutnya, dan fokus lompatan itu mengganggu. Menghapus perubahan ItemsCanFocus, dan hanya mengaktifkan fokus turunan memberi saya perilaku yang diinginkan. Semua item menggambar selektor seperti biasa, tetapi ketika sampai ke baris dengan EditText, itu difokuskan pada bidang teks sebagai gantinya. Kemudian ketika melanjutkan dari EditText itu, itu mulai menggambar pemilih lagi.

Joe
sumber
sangat keren, belum diuji. Sudahkah Anda menguji pada 1.5, 1.6 dan 3.0?
Rafael Sanches
Rafael Sanches: Saya belum menyentuh proyek ini sejak 2.1, tetapi pada saat itu, dipastikan bekerja di 1.5, 1.6, dan 2.1. Saya tidak menjamin bahwa ini masih berfungsi di 2.2 atau yang lebih baru.
Joe
13
hanya membutuhkan android: descendantFocusability = "afterDescendants" - bagaimanapun juga +1
kellogs
5
@kellogs: ya, descendantFocusability="afterDescendants"akan memungkinkan EditText Anda untuk mengambil fokus di dalam ListView, tetapi kemudian Anda tidak mendapatkan pemilih item daftar saat menavigasi dengan dpad. Tugas saya adalah memiliki pemilih item daftar di semua baris kecuali satu dengan EditText. Senang itu membantu. FWIW, kami akhirnya mengevaluasi ulang implementasi ini dan memutuskan bahwa fokus di dalam ListView bukanlah desain UI Android idiomatis, jadi kami membatalkan ide tersebut untuk mendukung pendekatan yang lebih ramah Android.
Joe
5
Apakah ini sudah diuji pada Ice Cream Sandwich? Saya tidak bisa membuatnya bekerja. Terima kasih.
Rajat Anantharam
99

Ini membantu saya.
Dalam manifes Anda:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
logan
sumber
3
Saya tidak yakin saya melihat relevansinya. Pengaturan windowSoftInputMode hanya mengubah cara IME menyesuaikan sisa konten jendela saat terbuka. Ini tidak memungkinkan Anda untuk secara selektif mengubah tipe fokus ListView. Bisakah Anda menjelaskan lebih lanjut, bagaimana hal ini terkait dengan kasus penggunaan awal?
Joe
1
@Joe Saat IME dibuka, kursor - dan mungkin juga fokusnya - hanya melompat-lompat di layar saya, sehingga tidak mungkin untuk memasukkan teks. Anda OnItemSelectedListenertidak mengubah itu. Namun solusi sederhana Iogan bekerja seperti pesona, terima kasih!
Gubbel
2
@ Gubbel: Memang, itu tidak akan mengubah itu sama sekali, karena pertanyaan aslinya adalah tentang sesuatu yang sama sekali berbeda :) Perbaikan Logan senang bekerja untuk apa yang Anda cari, tetapi itu sama sekali tidak terkait dengan pertanyaan.
Joe
8
Menggunakan properti plus Joe ini android:descendantFocusabilitymembuat saya EditTextdi dalam ListViewmenyelesaikan keyboard dengan benar, menaikkan suara keduanya. android:descendantFocusabilitydengan sendirinya tidak melakukan trik dan saya tidak terlalu antusias @Overriding onItemSelecteduntuk semua 14 yang EditTextharus saya tangani. :) Terima kasih!
Thomson Comer
Ini berhasil untuk saya, tetapi perhatikan jika Anda menggunakan TabHost atau TabActivity, Anda harus menyetel android: windowSoftInputMode = "adjustPan" untuk definisi TabActivity di manifes.
kiduxa
18

Tugas saya adalah menerapkan ListViewyang mengembang saat diklik. Ruang tambahan menunjukkan EditTexttempat Anda dapat memasukkan beberapa teks. Aplikasi harus berfungsi pada 2.2+ (hingga 4.2.2 pada saat penulisan ini)

Saya mencoba berbagai solusi dari posting ini dan lainnya yang dapat saya temukan; mengujinya pada perangkat 2.2 hingga 4.2.2. Tidak ada solusi yang memuaskan di semua perangkat 2.2+, setiap solusi disajikan dengan masalah yang berbeda.

Saya ingin membagikan solusi terakhir saya:

  1. setel tampilan daftar ke android:descendantFocusability="afterDescendants"
  2. setel tampilan daftar ke setItemsCanFocus(true);
  3. atur aktivitas Anda ke android:windowSoftInputMode="adjustResize" Banyak orang menyarankan adjustPantetapi adjustResizememberikan ux imho yang jauh lebih baik, cukup uji ini dalam kasus Anda. Dengan adjustPanAnda akan mendapatkan daftar item terbawah dikaburkan misalnya. Dokumen menyarankan itu ("Ini umumnya kurang diinginkan daripada mengubah ukuran"). Juga pada 4.0.4 setelah pengguna mulai mengetik pada keyboard lunak, layar bergeser ke atas.
  4. pada 4.2.2 dengan adjustResize ada beberapa masalah dengan fokus EditText. Solusinya adalah menerapkan solusi rjrjr dari utas ini. Ini terlihat langka tapi sebenarnya tidak. Dan itu berhasil. Cobalah.

Tambahan 5. Karena adaptor di-refresh (karena EditTextperubahan ukuran tampilan) ketika mendapatkan fokus pada versi HoneyComb sebelumnya, saya menemukan masalah dengan tampilan terbalik: mendapatkan View untuk item ListView / urutan terbalik di 2.2; bekerja pada 4.0.3

Jika Anda melakukan beberapa animasi, Anda mungkin ingin mengubah perilakunya adjustPan untuk versi pra-honeycomb sehingga pengubahan ukuran tidak diaktifkan dan adaptor tidak menyegarkan tampilan. Anda hanya perlu menambahkan sesuatu seperti ini

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

Semua ini memberikan ux yang dapat diterima pada perangkat 2.2 - 4.2.2. Semoga ini akan menghemat waktu karena saya membutuhkan setidaknya beberapa jam untuk sampai pada kesimpulan ini.

AndroidGecko
sumber
1
hanya langkah pertama dan kedua yang cukup dalam kasus saya
Kalpesh Lakhani
Bekerja sempurna dengan xElement.setOnClickListener (..) saya di ArrayAdapter dan ListView.setOnItemClickListener (...) - akhirnya !! Terima kasih.
Javatar
10

Ini menyelamatkan hidupku --->

  1. atur garis ini

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Kemudian di manifes Anda dalam tag aktivitas, ketik this ->

    <activity android:windowSoftInputMode="adjustPan">

Niatmu yang biasa

ravi ranjan.dll
sumber
7

Kami mencoba ini pada daftar pendek yang tidak melakukan daur ulang tampilan. Sejauh ini baik.

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>

Jawa:

/**
 * 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();
        }
      }
    });
  }
}
rjrjr
sumber
Sedikit dimodifikasi, solusi ini berfungsi untuk saya setelah 2 hari @ # ( : if (sticky! = Null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); sticky = null;}
Chris van de Steeg
4

posting ini sama persis dengan kata kunci saya. Saya memiliki header ListView dengan EditText pencarian dan Tombol pencarian.

Untuk memberikan fokus pada EditText setelah kehilangan fokus awal, satu-satunya HACK yang saya temukan adalah:

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

Kehilangan banyak waktu dan itu bukan perbaikan yang nyata. Semoga bisa membantu seseorang yang tangguh.

Rafael Sanches
sumber
2

Jika daftar tersebut dinamis dan berisi widget yang dapat difokuskan, maka opsi yang tepat adalah menggunakan RecyclerView, bukan IMO ListView.

Solusi yang mengatur adjustPan, FOCUS_AFTER_DESCENDANTSatau mengingat posisi fokus secara manual, memang hanya solusi. Mereka memiliki kasus sudut (gulir + masalah keyboard lunak, posisi perubahan tanda sisipan di EditText). Mereka tidak mengubah fakta bahwa ListView membuat / menghancurkan tampilan secara massal selama notifyDataSetChanged.

Dengan RecyclerView, Anda memberi tahu tentang penyisipan, pembaruan, dan penghapusan individual. Tampilan terfokus tidak dibuat ulang sehingga tidak ada masalah dengan kontrol formulir yang kehilangan fokus. Sebagai bonus tambahan, RecyclerView menganimasikan penyisipan dan penghapusan item daftar.

Berikut adalah contoh dari dokumen resmi tentang cara memulai RecyclerView: Panduan pengembang - Membuat Daftar dengan RecyclerView

Pēteris Caune
sumber
1

beberapa kali saat Anda menggunakan android:windowSoftInputMode="stateAlwaysHidden"aktivitas manifes atau xml, saat itu fokus keyboard akan hilang. Jadi pertama-tama periksa properti itu di xml dan manifes Anda, jika ada, hapus saja. Setelah menambahkan opsi ini untuk memanifestasikan file di aktivitas samping android:windowSoftInputMode="adjustPan"dan menambahkan properti ini ke listview di xmlandroid:descendantFocusability="beforeDescendants"


sumber
0

Solusi sederhana lainnya adalah menentukan onClickListener Anda, dalam metode getView (..), ListAdapter Anda.

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;

Dengan begitu baris Anda dapat diklik, dan tampilan dalam Anda juga :)

Weber Antoine
sumber
1
Pertanyaannya berkaitan dengan fokus, bukan kemampuan diklik.
Joe
0

Bagian terpenting adalah membuat fokus berfungsi untuk sel daftar. Khusus untuk daftar di Google TV ini penting:

Metode setItemsCanFocus dari tampilan daftar melakukan trik:

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

Daftar sel xml saya dimulai seperti berikut:

<?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 juga penting untuk navigasi D-Pad.

Untuk lebih jelasnya lihat jawaban hebat lainnya.

Michael Biermann
sumber
0

Saya baru saja menemukan solusi lain. Saya percaya ini lebih merupakan peretasan daripada solusi tetapi berfungsi pada android 2.3.7 dan android 4.3 (Saya bahkan telah menguji D-pad lama yang bagus itu)

init tampilan web Anda seperti biasa dan tambahkan ini: (terima kasih Michael Bierman)

listView.setItemsCanFocus(true);

Selama panggilan getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
alaeri
sumber
apa yang dilihat di sini dalam view.requestFocus?
Narendra Singh
ini adalah jawaban lama tetapi merujuk pada tampilan dalam cakupan yang diberikan sebagai argumen ke OnFocusChangeListener yang saya yakini
alaeri
1
Ini menyebabkan loop dalam mengubah fokus.
Morteza Rastgoo
0

Coba saja ini

android:windowSoftInputMode="adjustNothing"

dalam

aktivitas

bagian dari manifes Anda. Ya, itu tidak menyesuaikan apa pun, yang berarti editText akan tetap di tempatnya saat IME dibuka. Tapi itu hanya sedikit ketidaknyamanan yang masih sepenuhnya menyelesaikan masalah kehilangan fokus.

Tor_Gash
sumber