2013-07-22 6 views
17

Chcę utworzyć element, który jest wyświetlany w środku ScrollView (lub ListView) na pierwszym, a następnie utknie w nagłówku ekranu, gdy jest przewijane. Jest to prototypowa implementacja w CSS + JS:Tworzenie środkowego elementu, który utknie w nagłówku (ScrollView/ListView)

Na pierwszy rzut oka chciałbym zrobić ScrollView zawierać ListView, ale oficjalne docs mówi:

Nigdy nie należy używać Scrollview z ListView, ponieważ ListView dba o własną przewijania pionowego. Co najważniejsze, czyniąc to, pokonuje wszystkie ważne optymalizacje w ListView do radzenia sobie z dużymi listami, ponieważ skutecznie zmusza ListView do wyświetlenia całej listy elementów, aby wypełnić nieskończony kontener dostarczony przez ScrollView.

Podejście, czy mogę spróbować osiągnąć ten interfejs?

Aktualizacja: Próbowałem StickyListHeaders, ale: „w tej chwili nie jest możliwe, aby elementy interaktywne w nagłówku, przyciski, przełączniki, itp zadziała tylko wtedy, gdy nagłówek nie jest zablokowany.” Poza tym uważam, że to niezbyt odpowiedni dla tej sytuacji. Nie potrzebuję wielu nagłówków, ale tylko jeden środkowy element, który utknie w nagłówku.

+0

Właśnie znalazłem tej biblioteki: https://github.com/emilsjolander/StickyListHeaders, więc postaram teraz, a następnie dodaj odpowiedź sam, jeśli działa dobrze. – minhee

+0

Znalazłem StickyListHeaders nie nadaje się do mojej pracy, więc zaktualizowałem pytanie. – minhee

Odpowiedz

6

Użyłem (a raczej próbowałem użyć) biblioteki StickyListHeaders w przeszłości. Po kilku problemach wymyśliłem następujące. To niewiele różni się od tego, co sugerowały inne plakaty.

Główny plik układu activity_layout.xml składa się z ListView i LinearLayout, który jest domyślnie niewidoczny. Przy użyciu metody OnScrollListener onScroll() widoczność LinearLayout's jest przełączana. Nie ma potrzeby nadmuchiwania innego układu ani dynamicznego dodawania widoków do elementu nadrzędnego układu. To właśnie metoda onScroll wygląda następująco:

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
    if (firstVisibleItem > 3) {  // 5th row will stick 
     llHeader.setVisibility(View.VISIBLE); 
    } else { 
     llHeader.setVisibility(View.GONE); 
    } 
} 

Po prostu przełączyć widoczność, aby uzyskać pożądany efekt. Możesz rzucić okiem na poniższy kod. Jest dobrym przykładem tego, czego możesz się spodziewać. Ta aktywność zawiera plik ListView o ściśle rozszerzonym kodzie z numerem BaseAdapter. ListView jest zapełniany numerowanymi przyciskami (po jednym w każdym wierszu, zaczynając od 0 i przechodząc do 19).

public class StickyHeader extends Activity {  

    LinearLayout llHeader; 
    ListView lv; 
    SHAdapter shAdapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_layout); 

     lv = (ListView) findViewById(R.id.listView1);   
     llHeader = (LinearLayout) findViewById(R.id.llHeader);   
     shAdapter = new SHAdapter();   
     lv.setAdapter(shAdapter); 

     lv.setOnScrollListener(new OnScrollListener() { 

      @Override 
      public void onScrollStateChanged(AbsListView view, int scrollState) {} 

      @Override 
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
       if (firstVisibleItem > 3) { 
        llHeader.setVisibility(View.VISIBLE); 
       } else { 
        llHeader.setVisibility(View.GONE); 
       } 
      } 
     }); 
    } 

    public class SHAdapter extends BaseAdapter { 

     Button btCurrent; 
     int[] arr = new int[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; 

     @Override 
     public int getCount() { 
      return 20; 
     } 

     @Override 
     public Object getItem(int arg0) { 
      return arr[arg0]; 
     } 

     @Override 
     public long getItemId(int position) { 
      return 0; 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 
      convertView = getLayoutInflater().inflate(R.layout.list_item_layout, null);   
      btCurrent = (Button) convertView.findViewById(R.id.button1);    

      if ((Integer)getItem(position) == 4) { 
       btCurrent.setText("Number " + getItem(position) + " is sticky"); 
      } else { 
       btCurrent.setText("" + getItem(position)); 
      } 

      return convertView; 
     } 
    } 
} 

activity_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" > 

    <ListView 
     android:id="@+id/listView1" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" > 
    </ListView> 

    <!-- This LinearLayout's visibility is toggled --> 

    <!-- Improvement suggested by user 'ar34z' 
     (see comment section below) --> 
    <include layout="@layout/list_item_layout" /> 

</RelativeLayout> 

list_item_layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/llHeader" 
    android:layout_width="match_parent" 
    android:layout_height="50dp" 
    android:background="@color/white" 
    android:orientation="vertical" > 

    <Button 
     android:id="@+id/button1" 
     android:layout_gravity="center" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 
+0

To naprawdę pomoże, dzięki. –

+2

Dobra odpowiedź. Aby uczynić go bardziej łatwym do utrzymania, możesz dołączyć plik list_item_layout.xml do pliku activity_layout.xml. Po zmianie układu wystarczy zaktualizować plik list_item_layout.xml. – ar34z

+1

@ ar34z Absolutnie! Dzięki, wprowadziłem odpowiednie zmiany. – Vikram

0

W tym celu wstawię listę do względnego widoku i dodam detektor na przewinięciu. Następnie w nim, gdy zmieni się firstVisibleItem, powielę itemView, którego potrzebujesz i wyświetl go na liście viewView.

Oto krótka próbka tego, co myślę. Jedyną rzeczą jest to, że powielony widok musi być nieprzejrzysty.

import android.app.Activity; 
import android.os.Bundle; 
import android.view.View; 
import android.view.ViewGroup.LayoutParams; 
import android.widget.AbsListView; 
import android.widget.AbsListView.OnScrollListener; 
import android.widget.ArrayAdapter; 
import android.widget.LinearLayout; 
import android.widget.ListView; 
import android.widget.RelativeLayout; 

public class MainActivity extends Activity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     /*Create listView inside a relativelayout*/ 
     final RelativeLayout rl = new RelativeLayout(this); 
     final ListView lv = new ListView(this); 
     rl.addView(lv); 
     /* Set it as a content view*/ 
     setContentView(rl); 
     /*populate it*/ 
     String[] items = { "Cupcake", 
       "Donut", 
       "Eclair", 
       "Froyo", 
       "Gingerbread", 
       "Honeycomb", 
       "Ice Cream Sandwich", 
       "Jelly Bean"}; 
     int size = 10; 
     String[] arrayItems = new String[items.length*size]; 
     for(int i = 0; i < arrayItems.length; i++) 
      arrayItems[i] = items[i%items.length] + " " + i;  
     /* Need to use a non transparent view*/ 
     final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
        R.layout.simple_list_item_1_opaque, arrayItems);   
     lv.setAdapter(adapter); 

     /* Choose the item to stick*/ 
     final int itemToStick = 3; 

     /* Create things needed for duplication */ 
     final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 
     params.addRule(RelativeLayout.ALIGN_PARENT_TOP, 1); 
     final RelativeLayout.LayoutParams selectorParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, lv.getDividerHeight()); 
     lv.setOnScrollListener(new OnScrollListener() { 

      @Override 
      public void onScrollStateChanged(AbsListView view, int scrollState) { 
       // TODO Auto-generated method stub 

      } 

      @Override 
      public void onScroll(AbsListView view, int firstVisibleItem, 
        int visibleItemCount, int totalItemCount) { 
       if(itemToStick <= firstVisibleItem && rl.getChildCount() == 1) 
       { 
        /* Put view in a linearlayout in order to be able to add the sepline */ 
        LinearLayout ll = new LinearLayout(view.getContext()); 
        ll.setOrientation(LinearLayout.VERTICAL); 
        ll.addView(adapter.getView(itemToStick, null, null),params); 
        /* Create Divider */ 
        View selector = new LinearLayout(view.getContext()); 
        selector.setBackground(lv.getDivider()); 
        /* add views*/ 
        ll.addView(selector,selectorParams); 
        rl.addView(ll,params); 

       } 
       /* Remove view when scrolling up to it */ 
       else if(itemToStick > firstVisibleItem) 
       { 
        if(rl.getChildCount() > 1) 
         rl.removeViewAt(1); 
       } 
      } 
     }); 
    } 

} 

I simple_list_item_1_opaque:

<?xml version="1.0" encoding="utf-8"?> 
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/text1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:textAppearance="?android:attr/textAppearanceListItemSmall" 
    android:gravity="center_vertical" 
    android:paddingStart="?android:attr/listPreferredItemPaddingStart" 
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" 
    android:minHeight="?android:attr/listPreferredItemHeightSmall" 
    android:background="#FFFFFF" 
/> 
+0

To jest to, co zrobiłem w jednym z moich projektów, poza tym, że użyłem 'FrameLayout'. To dobrze działa. – Wenger

1

Jest to najprostszy kod do zilustrowania główny pomysł:

listView.setOnScrollListener(new OnScrollListener() { 
    ViewGroup mainView = (ViewGroup) findViewById(R.id.main); 
    View pinnedView = null; 

    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
     if (firstVisibleItem < PINNED_ITEM) { 
      mainView.removeView(pinnedView); 
      pinnedView = null; 
     } else if (pinnedView == null) { 
      pinnedView = adapter.getView(PINNED_ITEM, null, view); 
      pinnedView.setBackgroundColor(0xFF000000); 
      mainView.addView(pinnedView); 
     } 
    } 

    @Override 
    public void onScrollStateChanged(AbsListView view, int scrollState) {} 
}); 

Mam FrameLayout który zawiera nic, ale mój ListView:

<FrameLayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:id="@+id/main" 
> 
    <ListView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
    /> 
</FrameLayout> 

PINNED_ITEM to pozycja Twojego przedmiotu (na przykład PINNED_ITEM = 2). Ten układ działa jako nakładka dla listy. ScrollListener śledzi bieżące widoczne elementy i jeśli wykryje, że element powinien zostać przypięty, dodaje go do układu i usuwa go w inny sposób.

Linia pinnedView.setBackgroundColor(0xFF000000); jest potrzebna do ustawienia nieprzejrzystego tła dla przedmiotu. Element będzie przezroczysty, jeśli tego nie zrobisz. Możesz dostosować tło do swoich potrzeb (na przykład możesz użyć tła z bieżącego atrybutu tematu).

Powiązane problemy