2013-07-22 5 views
17

Ich möchte ein Element machen, das in der Mitte von ScrollView (oder ListView) zuerst angezeigt wird und dann in der Kopfzeile des Bildschirms stecken bleibt, wenn es ist gescrollt.Ein mittleres Element machen, um in der Kopfzeile stecken zu bleiben (ScrollView/ListView)

Es ist eine Prototyp-Implementierung in CSS + JS: http://jsfiddle.net/minhee/aPcv4/embedded/result/.

Auf den ersten Blick würde ich ScrollView machen ListView aufzunehmen, aber die offizielle Dokumentation sagt:

Sie nie eine Scrollview mit einem Listview verwenden sollten, weil Listview Pflege seiner eigenen vertikalen Bildlauf stattfindet. Am wichtigsten ist, dass dadurch alle wichtigen Optimierungen in ListView für den Umgang mit großen Listen vereitelt werden, da es ListView effektiv zwingt, seine gesamte Liste von Elementen anzuzeigen, die den von ScrollView bereitgestellten unbegrenzten Container füllen.

Also, welche Ansätze kann ich versuchen, diese Benutzeroberfläche zu erreichen?

aktualisieren: Ich habe versucht StickyListHeaders, aber: „Es ist derzeit nicht möglich ist, interaktive Elemente im Header zu haben, Knöpfe, Schalter, etc. wird nur funktionieren, wenn der Header nicht hängen geblieben ist.“ Außerdem finde ich es für diese Situation nicht sehr geeignet. Ich brauche nicht mehrere Header, sondern nur ein Mittelelement, um im Header stecken zu bleiben.

+0

Ich fand gerade diese Bibliothek: https://github.com/emilsjolander/StickyListHeaders, also werde ich versuchen, dies jetzt, und fügen Sie dann selbst Antwort hinzu, wenn es gut funktioniert. – minhee

+0

Ich fand StickyListHeaders ist nicht geeignet für meinen Job, also habe ich die Frage aktualisiert. – minhee

Antwort

6

Ich habe die StickyListHeaders Bibliothek in der Vergangenheit verwendet (oder vielmehr versucht, zu verwenden). Nachdem ich einige Probleme damit hatte, kam ich auf Folgendes. Es ist nicht viel anders als was andere Poster vorgeschlagen haben.Die Hauptlayoutdatei activity_layout.xml besteht aus einer ListView und einer LinearLayout, die standardmäßig nicht sichtbar ist. Mit der OnScroll() - Methode des OnScrollListener wird die Sichtbarkeit LinearLayout's umgeschaltet. Sie müssen kein anderes Layout aufblasen oder Ansichten dynamisch zum übergeordneten Element Ihres Layouts hinzufügen. Dies ist, was die onScroll Methode wie folgt aussieht:

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

einfach, die Sichtbarkeit schalten Sie den gewünschten Effekt zu erzielen. Sie können sich den folgenden Code ansehen. Es ist ein funktionierendes Beispiel für das, was Sie erwarten können. Die Aktivität enthält eine ListView mit einer streng Barebone-Erweiterung von BaseAdapter. Die ListView ist mit nummerierten Schaltflächen (einer in jeder Zeile, beginnend bei 0 und bis zu 19) bestückt.

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

Das hilft wirklich, danke. –

+2

Schöne Antwort. Um es wartbarer zu machen, könnten Sie die Datei list_item_layout.xml in die Datei activity_layout.xml aufnehmen. Wenn das Layout geändert wird, müssen Sie lediglich die Datei list_item_layout.xml aktualisieren. – ar34z

+1

@ ar34z Absolut! Danke, ich habe entsprechende Änderungen vorgenommen. – Vikram

0

Um dies zu erreichen, werde ich die listView in eine relativeLayout setzen und einen Listener auf der Rolle hinzufügen. Dann, wenn sich das firstVisibleItem ändert, verdopple ich das von Ihnen benötigte itemView und zeige es über Ihr listView an.

Hier ist eine kurze Probe von dem, was ich denke. Die einzige Sache ist, dass die doppelte Ansicht undurchsichtig sein muss.

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

} 

Und 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

Das habe ich in einem meiner Projekte gemacht, außer dass ich ein 'FrameLayout' verwendet habe. Es läuft gut. – Wenger

1

Dies ist der einfachste Code, um die Hauptidee zu erläutern:

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

Ich habe ein FrameLayout, das nichts enthält, aber meine 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 ist die Position Ihres Artikels (z. B. PINNED_ITEM = 2). Dieses Layout dient als Überlagerung für die Liste. Das ScrollListener verfolgt die aktuellen sichtbaren Elemente und wenn es erkennt, dass ein Element fixiert werden soll, fügt es es dem Layout hinzu und entfernt es andernfalls.

Die Zeile pinnedView.setBackgroundColor(0xFF000000); wird benötigt, um einen undurchsichtigen Hintergrund für das Objekt festzulegen. Das Element wird transparent sein, wenn Sie dies nicht tun. Sie können den Hintergrund nach Ihren Bedürfnissen anpassen (zum Beispiel können Sie einen Hintergrund von Ihrem aktuellen Designattribut verwenden).