ListView içinde Odaklanabilir EditText


121

Şimdiye kadar yaklaşık 6 saat geçirdim ve barikatlardan başka hiçbir şeye çarpmadım. Genel önerme, ListViewbir EditTextpencere öğesi ve bir Button. Tek yapmak istediğim, seçiciyi normal gibi tek tek öğelere yönlendirmek için jogball / okları kullanabilmek, ancak belirli bir satıra geldiğimde - net bir şekilde satırı tanımlamam gerekse bile - odaklanabilir çocuk, o çocuğun konumu seçici ile belirtmek yerine odaklanmasını istiyorum.

Pek çok olasılık denedim ve şimdiye kadar hiç şansım olmadı.

Yerleşim:

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

Başlık görünümü:

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

Bağdaştırıcıda başka öğeler olduğunu varsayarsak, ok tuşlarının kullanılması, beklendiği gibi, listedeki seçimi yukarı / aşağı hareket ettirecektir; ancak başlık satırına gelindiğinde, seçici ile birlikte görüntülenir EditTextve jogball'u kullanmaya odaklanmanın yolu yoktur . Not: dokunarak EditText olacak , o noktada ancak bir gereklilik olmamalı Dokunmatik, güvenir o kadar odaklanır.

ListViewGörünüşe göre bu konuda iki moda sahiptir:
1 setItemsCanFocus(true).: seçici asla görüntülenmez, ancak EditTextokları kullanırken odaklanabilir. Odak arama algoritmasını tahmin etmek zordur ve hangi öğenin seçildiğine dair görsel geri bildirim (herhangi bir satırda: odaklanabilir çocuklara sahip olmak veya değil) yoktur, her ikisi de kullanıcıya beklenmedik bir deneyim verebilir.
2 setItemsCanFocus(false).: seçici her zaman dokunmatik olmayan modda çizilir ve EditTextüzerine dokunsanız bile asla odaklanamaz.

editTextView.requestFocus()Sorunları daha da kötüleştirmek için, çağrı doğru döndürür, ancak gerçekte EditText odağını vermez.

Ne öngören olduğum temelde eğer yerine liste ayarından daha 1 & 2, bir melez olduğunu tüm öğeleri odaklanabilir ya da olmasın, bir için ayarlanan focusability istediğiniz tek listede öğe böylece seçmekten seçici sorunsuz geçişler olduğunu odaklanılamayan öğeler için tüm satır ve odaklanılabilen alt öğeler içeren öğeler için odak ağacında gezinme.

Alıcı var mı?

Yanıtlar:


101

Üzgünüm, kendi sorumu cevapladım. En doğru veya en şık çözüm olmayabilir ama benim için işe yarıyor ve oldukça sağlam bir kullanıcı deneyimi sağlıyor. İki davranışın neden bu kadar farklı olduğunu görmek için ListView koduna baktım ve bunu ListView.java'dan buldum:

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

Yani, arama yaparken setItemsCanFocus(false), hiçbir çocuğun odaklanamayacağı şekilde alt odaklanabilirliği de ayarlıyor. Bu, neden mItemsCanFocusListView'ın OnItemSelectedListener öğesinde geçiş yapamadığımı açıklıyor - çünkü ListView daha sonra tüm çocuklara odaklanmayı engelliyordu.

Şimdi sahip olduğum şey:

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

Kullandığım beforeDescendantsvarsayılan davranış ListView ilk odağı alır ve seçicileri çizer olduğunu olmalıdır böylece ListView kendisi (bir çocuk), odağa sahip olduğunda seçici sadece çekilecektir çünkü.

Ardından, OnItemSelectedListener'da, seçiciyi geçersiz kılmak istediğim üstbilgi görünümünü bildiğimden (herhangi bir konumun odaklanılabilir bir görünüm içerip içermediğini dinamik olarak belirlemek için daha fazla çalışma gerekir), alt odaklanabilirliği değiştirebilir ve EditText üzerinde odak ayarlayabilirim. Ve o başlıktan çıktığımda, onu tekrar değiştir.

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

Yorumlanan setItemsCanFocusaramaları not edin . Bu aramalarla doğru davranışı elde ettim, ancak setItemsCanFocus(false)odağın EditText'ten ListView dışındaki başka bir widget'a atlamasına, ListView'a geri dönmesine ve seçiciyi bir sonraki seçili öğede görüntülemesine neden oldum ve bu atlama odağı dikkat dağıtıcıydı. ItemsCanFocus değişikliğini kaldırmak ve sadece alt odaklanabilirliği değiştirmek bana istenen davranışı kazandırdı. Tüm öğeler seçiciyi normal bir şekilde çizer, ancak EditText ile satıra gelindiğinde bunun yerine metin alanına odaklanır. Sonra o EditText'ten devam ederken, seçiciyi tekrar çizmeye başladı.


çok havalı, henüz test edilmedi. 1.5, 1.6 ve 3.0 üzerinde test ettiniz mi?
Rafael Sanches

Rafael Sanches: 2.1'den beri projeye dokunmadım, ancak o zamanlar 1.5, 1.6 ve 2.1'de çalıştığı doğrulandı. Hala 2.2 veya sonrasında çalıştığına dair hiçbir garanti vermiyorum.
Joe

13
Sadece android gerekli: descendantFocusability = "afterDescendants" - yine 1
Kellogs

5
@kellogs: evet, descendantFocusability="afterDescendants"EditText'inizin ListView içinde odaklanmasına izin verir, ancak daha sonra bir dpad ile gezinirken hiçbir liste öğesi seçicisi almazsınız. Benim görevim, liste öğesi seçicinin EditText ile olan hariç tüm satırlarda olmasını sağlamaktı. Yine de yardımcı olmasına sevindim. FWIW, bu uygulamayı yeniden değerlendirmeye başladık ve bir ListView içindeki odaklanılabilirliğin deyimsel Android UI tasarımı olmadığına karar verdik, bu yüzden fikri daha Android dostu bir yaklaşım lehine hurdaya çıkardık.
Joe

5
Bu, Ice Cream Sandwich üzerinde test edildi mi? Çalıştıramıyorum. Teşekkürler.
Rajat Anantharam

99

Bu bana yardımcı oldu.
Manifestinde:

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

3
Alaka düzeyini gördüğümden emin değilim. WindowSoftInputMode'un ayarlanması, yalnızca IME'nin açıkken kalan pencere içeriğini ayarlama şeklini değiştirir. ListView'ın odak türünü seçmeli olarak değiştirmenize izin vermez. Bunun ilk kullanım durumuyla nasıl bir ilgisi olduğunu biraz daha açıklayabilir misiniz?
Joe

1
@Joe IME açıldığında, imleç - ve muhtemelen odak da - ekranımda geziniyor ve metin girmeyi imkansız hale getiriyor. Senin OnItemSelectedListenerbunu değiştirmezsin. Ancak Iogan'ın basit çözümü bir cazibe gibi çalışıyor, teşekkürler!
Gubbel

2
@Gubbel: Aslında, bunu hiç değiştirmezdi, çünkü asıl soru tamamen farklı bir şeyle ilgiliydi :) Glad logan'ın düzeltmesi aradığınız şey için işe yarıyor, ancak bu soruyla uzaktan yakından ilişkili bile değil.
Joe

8
Bu artı Joe'nun android:descendantFocusabilitymülkünü kullanmak , klavyemi doğru EditTextbir şekilde ListViewçözme işlemine girdi, her ikisine de olumlu oy verdi. android:descendantFocusabilityTek başına hile yapmadım ve uğraşmak zorunda olduğum @Overriding onItemSelected14 EditTextsaniyenin tümü için uzaktan hevesli değildim. :) Teşekkürler!
Thomson Comer

Bu benim için çalıştı, ancak TabHost veya TabActivity kullanıyorsanız, manifestteki TabActivity tanımı için android: windowSoftInputMode = ”AdjustPan” ayarlamanız gerektiğine dikkat edin.
kiduxa

18

Benim görevim ListView, tıklandığında genişleyen uygulamaktı . Ek alan, EditTextmetin girebileceğiniz yeri gösterir . Uygulama 2.2+ sürümünde işlevsel olmalıdır (bunu yazarken 4.2.2'ye kadar)

Bu yazıdan ve bulabildiğim diğerlerinden çok sayıda çözüm denedim; bunları 2.2'den 4.2.2'ye kadar cihazlarda test etti. 2.2+ tüm cihazlarda çözümlerin hiçbiri tatmin edici değildi, her çözüm farklı problemlerle sunuldu.

Nihai çözümümü paylaşmak istedim:

  1. liste görünümünü şuna ayarla android:descendantFocusability="afterDescendants"
  2. liste görünümünü şuna ayarla setItemsCanFocus(true);
  3. Aktivitenizi android:windowSoftInputMode="adjustResize" birçok insanın önerdiği olarak ayarlayın, adjustPanancak adjustResizeçok daha iyi ux imho verir, bunu kendi durumunuzda test edin. Bununla birlikte adjustPan, örneğin gizlenmiş alt liste öğeleri alacaksınız. Dokümanlar şunu önermektedir ("Bu, genellikle yeniden boyutlandırmaya göre daha az tercih edilir"). Ayrıca 4.0.4'te, kullanıcı sanal klavyede yazmaya başladıktan sonra ekran en üste kayar.
  4. 4.2.2'de adjustResizeEditText odağında bazı sorunlar var. Çözüm, bu iş parçacığından rjrjr çözümü uygulamaktır. Korkunç görünüyor ama değil. Ve çalışıyor. Sadece dene.

Ek 5.EditText Kazanımlar HoneyComb öncesi sürümlere odaklandığında bağdaştırıcının yenilenmesi nedeniyle (görünüm yeniden boyutlandırması nedeniyle) tersine çevrilmiş görünümlerde bir sorun buldum: 2.2'de ListView öğesi için Görünüm / ters sıra alma; 4.0.3 üzerinde çalışır

Bazı animasyonlar yapıyorsanız adjustPan, yeniden boyutlandırmanın çalışmaması ve adaptörün görünümleri yenilememesi için bal peteği öncesi sürümler için davranışı değiştirmek isteyebilirsiniz . Sadece böyle bir şey eklemelisin

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

Bütün bunlar 2.2 - 4.2.2 cihazlarda kabul edilebilir ux sağlar. Bu sonuca varmam en az birkaç saat sürdüğü için, insanlara biraz zaman kazandıracağını umuyorum.


1
benim durumumda sadece birinci ve ikinci adım yeterli
Kalpesh Lakhani

ArrayAdapter içindeki xElement.setOnClickListener (..) ve ListView.setOnItemClickListener (...) ile mükemmel çalışır - nihayet !! Büyük teşekkürler.
Javatar

10

Bu benim hayatımı kurtardı --->

  1. bu satırı ayarla

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Ardından, etkinlik etiketindeki bildiriminize şunu yazın ->

    <activity android:windowSoftInputMode="adjustPan">

Her zamanki niyetin


7

Bunu herhangi bir geri dönüşüme yol açmayan kısa bir listede deniyoruz. Çok uzak çok iyi.

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

Biraz değiştirilmiş, bu çözüm 2 gün @ # ( : if (sticky! = Null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); sticky = null;}
Chris van de Steeg

4

bu gönderi tam olarak anahtar kelimelerimle eşleşiyordu. Bir arama EditText ve bir arama Düğmesine sahip bir ListView başlığım var.

İlk odağı kaybettikten sonra EditText'e odak vermek için bulduğum tek HACK şudur:

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

Çok fazla saat kaybettim ve bu gerçek bir düzeltme değil. Umarım sert birine yardımcı olur.


2

Liste dinamikse ve odaklanılabilir widget'lar içeriyorsa, doğru seçenek, ListView IMO yerine RecyclerView kullanmaktır.

Kümesini oluşturan geçici çözümler adjustPan, FOCUS_AFTER_DESCENDANTSveya el odaklanmış konumunu hatırlamak vardır aslında sadece geçici çözümler. Köşe durumları vardır (kaydırma + yumuşak klavye sorunları, Metni Düzenle'de düzeltme işareti değiştirme). ListView görünümlerini toplu halde oluşturduğu / yok ettiği gerçeğini değiştirmezler. sırasında notifyDataSetChanged.

RecyclerView ile bireysel eklemeler, güncellemeler ve silmeler hakkında bildirimde bulunursunuz. Odaklanılan görünüm yeniden oluşturulmadığından, form kontrollerinin odağı kaybetmesine neden olmaz. Ek bir bonus olarak RecyclerView, liste öğesi ekleme ve çıkarma işlemlerini canlandırır.

İşte nasıl başlayacağınızla ilgili resmi belgelerden bir örnek RecyclerView: Geliştirici kılavuzu - RecyclerView ile Liste Oluşturun


1

bazı zamanlarda android:windowSoftInputMode="stateAlwaysHidden"manifest aktivitesinde veya xml'de kullandığınızda, bu sefer klavye odağını kaybedecektir. Öyleyse, önce xml'nizde bu özelliği kontrol edin ve varsa, sadece kaldırın. Dosyayı yan aktivitede bildirmek için bu seçeneği ekledikten sonra android:windowSoftInputMode="adjustPan"ve bu özelliği xml'de listview'e ekledikten sonraandroid:descendantFocusability="beforeDescendants"


0

Diğer bir basit çözüm, ListAdapter'ınızın getView (..) yönteminde onClickListener'ınızı tanımlamaktır.

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;

Bu şekilde satırınız tıklanabilir ve iç görünümünüz de :)


1
Soru tıklanabilirlikle değil, odaklanmayla ilgilidir.
Joe

0

En önemli kısım, odağın liste hücresi için çalışmasını sağlamaktır. Özellikle Google TV'deki liste için bu çok önemlidir:

Liste görünümünün setItemsCanFocus yöntemi hile yapar:

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

Liste hücrem xml aşağıdaki gibi başlar:

<?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 navigasyonu için de önemlidir.

Daha fazla ayrıntı için diğer harika yanıtlara göz atın.


0

Sadece başka bir çözüm buldum. Bunun bir çözümden çok bir hack olduğuna inanıyorum ama android 2.3.7 ve android 4.3'te çalışıyor (o eski D-pad'i bile test ettim)

her zamanki gibi web görünümünüzü başlatın ve şunu ekleyin: (teşekkürler Michael Bierman)

listView.setItemsCanFocus(true);

GetView çağrısı sırasında:

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

view.requestFocus'da buradaki görünüm nedir?
Narendra Singh

bu eski bir cevaptır, ancak OnFocusChangeListener'a bir argüman olarak verilen kapsamdaki görüşe atıfta bulunulduğuna inanıyorum
alaeri

1
Odağın değişmesinde bir döngüye neden olur.
Morteza Rastgoo

0

Sadece bunu dene

android:windowSoftInputMode="adjustNothing"

içinde

aktivite

bildiriminizin bölümü. Evet, hiçbir şeyi ayarlamaz, bu da editText'in IME açılırken olduğu yerde kalacağı anlamına gelir. Ancak bu, odak kaybetme sorununu hala tamamen çözen küçük bir rahatsızlıktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.