2013-07-22 18 views
17

Tôi muốn tạo phần tử được hiển thị ở giữa ScrollView (hoặc ListView) ở đầu và sau đó bị kẹt trong tiêu đề màn hình khi nó đã cuộn.Làm cho phần tử ở giữa bị kẹt trong tiêu đề (ScrollView/ListView)

Đó là triển khai mẫu thử nghiệm trong CSS + JS: http://jsfiddle.net/minhee/aPcv4/embedded/result/.

Thoạt nhìn tôi sẽ làm ScrollView bao gồm ListView, nhưng các tài liệu chính thức cho biết:

Bạn không bao giờ nên sử dụng một scrollview với một ListView, vì ListView sẽ chăm sóc của di chuyển dọc riêng của mình. Quan trọng nhất, làm điều này đánh bại tất cả các tối ưu hóa quan trọng trong ListView để xử lý các danh sách lớn, vì nó có hiệu lực buộc ListView hiển thị toàn bộ danh sách các mục để lấp đầy vùng chứa vô hạn do ScrollView cung cấp.

Vì vậy, tôi có thể thử cách tiếp cận nào để đạt được giao diện người dùng này?

Cập nhật: Tôi cố gắng StickyListHeaders, nhưng: “nó hiện không thể có yếu tố tương tác trong tiêu đề, Buttons, công tắc, vv sẽ chỉ làm việc khi tiêu đề là không bị mắc kẹt.” Thêm vào đó, tôi thấy nó không thích hợp cho tình huống này. Tôi không cần nhiều tiêu đề, nhưng chỉ một phần tử trung gian bị kẹt trong tiêu đề.

+0

Tôi chỉ tìm thấy thư viện này: https://github.com/emilsjolander/StickyListHeaders, vì vậy tôi sẽ cố gắng điều này bây giờ, và sau đó thêm câu trả lời bản thân mình nếu nó hoạt động tốt. – minhee

+0

Tôi thấy StickyListHeaders không phù hợp với công việc của tôi, vì vậy tôi đã cập nhật câu hỏi. – minhee

Trả lời

6

Tôi đã sử dụng (hoặc đúng hơn, đã cố gắng sử dụng) thư viện StickyListHeaders trước đây. Sau khi có một số vấn đề với nó, tôi đã đưa ra những điều sau đây. Nó không khác nhiều so với những gì áp phích khác đã gợi ý.

Tệp bố cục chính activity_layout.xml bao gồm ListViewLinearLayout được ẩn theo mặc định. Sử dụng phương thức onScrollListener của onScroll(), khả năng hiển thị LinearLayout's được bật. Bạn không cần thổi phồng một bố cục khác hoặc thêm chế độ xem động vào bố mẹ của bố cục. Đây là phương thức của onScroll trông giống như:

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

Đơn giản chỉ cần bật/tắt chế độ hiển thị để đạt được hiệu quả mong muốn. Bạn có thể xem mã sau đây. Đó là một ví dụ làm việc về những gì bạn có thể mong đợi. Hoạt động có chứa một ListView với phần mở rộng nghiêm ngặt là BaseAdapter. ListView được điền bằng các nút được đánh số (một trên mỗi hàng, bắt đầu từ 0 và lên đến 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

Điều này thực sự giúp đỡ, cảm ơn. –

+2

Câu trả lời hay. Để làm cho nó dễ bảo trì hơn, bạn có thể bao gồm list_item_layout.xml trong tệp activity_layout.xml. Khi bố cục được thay đổi, tất cả những gì bạn phải làm là cập nhật list_item_layout.xml. – ar34z

+1

@ ar34z Tuyệt đối! Cảm ơn, tôi đã thực hiện các thay đổi thích hợp. – Vikram

0

Để thực hiện việc này, tôi sẽ đặt listView trong relativeLayout và thêm người nghe vào cuộn. Sau đó, trong đó, khi firstVisibleItem thay đổi, tôi sẽ nhân đôi itemView bạn cần và hiển thị nó trên listView của bạn.

Đây là một ví dụ ngắn về những gì tôi nghĩ. Điều duy nhất là chế độ xem trùng lặp phải mờ đục.

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

} 

Và 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

Đây là những gì tôi đã làm trên một trong các dự án của tôi ngoại trừ tôi đã sử dụng 'FrameLayout'. Nó hoạt động tốt. – Wenger

1

Đây là mã đơn giản nhất để minh họa cho ý tưởng chính:

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

Tôi có một FrameLayout đó chứa gì, nhưng ListView tôi:

<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 là vị trí của mặt hàng của bạn (ví dụ: PINNED_ITEM = 2). Bố cục này hoạt động như một lớp phủ cho danh sách. ScrollListener theo dõi các mục hiển thị hiện tại và nếu phát hiện thấy một mục sẽ được ghim, nó sẽ thêm mục đó vào bố cục và xóa nó theo cách khác.

Dòng pinnedView.setBackgroundColor(0xFF000000); là cần thiết để đặt nền mờ cho mục. Mục sẽ được minh bạch nếu bạn không làm điều này. Bạn có thể tinh chỉnh nền theo nhu cầu của mình (ví dụ: bạn có thể sử dụng nền từ thuộc tính chủ đề hiện tại của mình).

Các vấn đề liên quan