2013-07-22 11 views
17

Voglio rendere un elemento che viene mostrato nel mezzo di ScrollView (o ListView) al primo e poi rimane bloccato nell'intestazione dello schermo quando è scorrere.Creazione di un elemento intermedio per rimanere bloccati nell'intestazione (ScrollView/ListView)

È un'implementazione prototipo in CSS + JS: http://jsfiddle.net/minhee/aPcv4/embedded/result/.

A prima vista che vorrei fare ScrollView includere ListView, ma la documentazione ufficiale dice:

si dovrebbe mai usare uno ScrollView con un controllo ListView, perché ListView si prende cura della propria scorrimento verticale. Soprattutto, facendo ciò, si annullano tutte le ottimizzazioni importanti in ListView per gestire elenchi di grandi dimensioni, poiché costringono efficacemente ListView a visualizzare l'intero elenco di elementi per riempire il contenitore infinito fornito da ScrollView.

Quindi, quali approcci posso provare a raggiungere questa interfaccia utente?

Aggiornamento: Ho provato StickyListHeaders, ma: “non è attualmente possibile avere elementi interattivi nella testata, pulsanti, interruttori, ecc funziona solo quando l'intestazione non è bloccato.” In più, trovo che sia non molto adatto a questa situazione. Non ho bisogno di più intestazioni, ma solo un elemento centrale per rimanere bloccato nell'intestazione.

+0

Ho appena trovato questa libreria: https://github.com/emilsjolander/StickyListHeaders, quindi cercherò questo ora, e quindi aggiungi risposta io stesso se funziona bene. – minhee

+0

Ho trovato che StickyListHeaders non è adatto per il mio lavoro, quindi ho aggiornato la domanda. – minhee

risposta

6

Ho usato (o piuttosto provato a usare) la libreria StickyListHeaders in passato. Dopo aver avuto alcuni problemi con esso, ho trovato il seguente. Non è molto diverso da quello che altri poster hanno suggerito.

Il file di layout principale activity_layout.xml è costituito da un ListView e un LinearLayout che è invisibile per impostazione predefinita. Utilizzando il metodo onScroll() di OnScrollListener, la visibilità dello LinearLayout's è attivata. Non è necessario gonfiare un altro layout o aggiungere viste dinamicamente al genitore del proprio layout. Questo è ciò che il metodo onScroll assomiglia:

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

Semplicemente, commutare la visibilità per ottenere l'effetto desiderato. Puoi dare un'occhiata al seguente codice. È un esempio funzionante di ciò che puoi aspettarti. L'attività contiene uno ListView con un'estensione rigorosamente barebone di BaseAdapter. Il numero ListView è popolato con pulsanti numerati (uno su ogni riga, a partire da 0 e fino a 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

Questo davvero aiuto, grazie. –

+2

Bella risposta. Per renderlo più gestibile, puoi includere list_item_layout.xml in activity_layout.xml. Quando il layout viene modificato, tutto ciò che devi fare è aggiornare list_item_layout.xml. – ar34z

+1

@ ar34z Assolutamente! Grazie, ho apportato le modifiche appropriate. – Vikram

0

Per ottenere ciò, inserirò la listaView in un relativoLayout e aggiungerò un listener sullo scroll. Quindi, quando il primo oggetto Visible cambia, duplicherò l'itemView necessario e lo visualizzerà sul tuo listView.

Ecco un breve esempio di ciò che penso. L'unica cosa è che la vista duplicata deve essere opaca.

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

} 

E 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

Questo è quello che ho fatto su uno dei miei progetti tranne che ho usato un 'FrameLayout'. Funziona bene. – Wenger

1

Questo è il codice più semplice per illustrare l'idea principale:

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

Ho un FrameLayout che contiene nulla, ma il mio 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 è la posizione dell'articolo (ad esempio, PINNED_ITEM = 2). Questo layout funge da sovrapposizione per la lista. Lo ScrollListener tiene traccia degli elementi visibili correnti e se rileva che un elemento deve essere bloccato, lo aggiunge al layout e lo rimuove in caso contrario.

La riga pinnedView.setBackgroundColor(0xFF000000); è necessaria per impostare uno sfondo opaco per l'articolo. L'oggetto sarà trasparente se non lo fai. È possibile modificare lo sfondo in base alle proprie esigenze (ad esempio, è possibile utilizzare uno sfondo dell'attributo del tema corrente).

Problemi correlati