2012-11-27 85 views
118

我有ListView上有一些事件。活動按日期排序,我希望每天都有標題和日期,然後事件在下面進行監聽。Android的ListView標題

這是我如何填充該列表:

ArrayList<TwoText> crs = new ArrayList<TwoText>(); 

crs.add(new TwoText("This will be header", event.getDate())); 

for (Event event : events) { 
    crs.add(new TwoText(event.getStartString() + "-" + event.getEndString(), event.getSubject())); 
} 

arrayAdapter = new TwoTextArrayAdapter(this, R.layout.my_list_item, crs); 
lv1.setAdapter(arrayAdapter); 

,這是我的課TwoText的樣子:

public class TwoText { 
    public String classID; 
    public String state; 

    public TwoText(String classID, String state) { 
     this.classID = classID; 
     this.state = state; 
    } 
} 

,這是我TwoTextArrayAdapter類的樣子:

import java.util.ArrayList; 
import android.app.Activity; 
import android.content.Context; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.TextView; 

public class TwoTextArrayAdapter extends ArrayAdapter<TwoText> { 

    private ArrayList<TwoText> classes; 
    private Activity con; 
    TextView seperator; 

    public TwoTextArrayAdapter(Activity context, int textViewResourceId, ArrayList<TwoText> classes) { 
     super(context, textViewResourceId, classes); 
     this.con = context; 
     this.classes = classes; 

    } 

    @Override 

    public View getView(int position, View convertView, ViewGroup parent) { 

     View v = convertView; 

     if (v == null) { 

      LayoutInflater vi = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

      v = vi.inflate(R.layout.my_list_item, null); 

     } 

     TwoText user = classes.get(position); 

     if (user != null) { 

      TextView content1 = (TextView) v.findViewById(R.id.list_content1); 

      TextView content2 = (TextView) v.findViewById(R.id.list_content2); 

      if (content1 != null) { 

       content1.setText(user.classID); 
      } 
      if(content2 != null) { 

       content2.setText(user.state); 
      } 
     } 
     return v; 
    } 
} 

,這是my_list_item.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" > 

    <TextView 
     style="?android:attr/listSeparatorTextViewStyle" 
     android:id="@+id/separator" 
     android:text="Header" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:background="#757678" 
     android:textColor="#f5c227" /> 

    <LinearLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:orientation="horizontal" > 

     <TextView 
      android:id="@+id/list_content1" 
      android:layout_width="wrap_content" 
      android:layout_height="match_parent" 
      android:layout_margin="5dip" 
      android:clickable="false" 
      android:gravity="center" 
      android:longClickable="false" 
      android:paddingBottom="1dip" 
      android:paddingTop="1dip" 
      android:text="sample" 
      android:textColor="#ff7f1d" 
      android:textSize="17dip" 
      android:textStyle="bold" /> 

     <TextView 
      android:id="@+id/list_content2" 
      android:layout_width="wrap_content" 
      android:layout_height="match_parent" 
      android:layout_margin="5dip" 
      android:clickable="false" 
      android:gravity="center" 
      android:linksClickable="false" 
      android:longClickable="false" 
      android:paddingBottom="1dip" 
      android:paddingTop="1dip" 
      android:text="sample" 
      android:textColor="#6d6d6d" 
      android:textSize="17dip" /> 
    </LinearLayout> 

</LinearLayout> 

我現在在做的事情是,我像常規列表對象一樣添加標題,但是我想將它作爲標題並在我的情況下有一個日期。

我在我的XML的代碼頭:

<TextView 
     style="?android:attr/listSeparatorTextViewStyle" 
     android:id="@+id/separator" 
     android:text="Header" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:background="#757678" 
     android:textColor="#f5c227" /> 

,我試圖隱藏它時,它是不必要的,並將其顯示neccessary的時候,但我只是搞砸我的代碼休息。我嘗試了更多的教程,但他們也有同樣的效果。

任何人都可以指導我如何做到這一簡單的方法嗎?

回答

329

以下是我的工作方式,關鍵是getItemViewTypegetViewTypeCount,Adapter類。 getViewTypeCount返回列表中有多少類型的項目,在這種情況下,我們有一個標題項目和一個事件項目,所以有兩個。 getItemViewType應該返回我們在輸入position處有什麼類型的View

Android會自動處理convertView中的正確類型View

這裏什麼下面的代碼的結果如下:Example

首先,我們有我們的兩個列表項目類型將實行

public interface Item { 
    public int getViewType(); 
    public View getView(LayoutInflater inflater, View convertView); 
} 

然後一個接口,我們有一個適配器,採用列表Item

public class TwoTextArrayAdapter extends ArrayAdapter<Item> { 
    private LayoutInflater mInflater; 

    public enum RowType { 
     LIST_ITEM, HEADER_ITEM 
    } 

    public TwoTextArrayAdapter(Context context, List<Item> items) { 
     super(context, 0, items); 
     mInflater = LayoutInflater.from(context); 
    } 

    @Override 
    public int getViewTypeCount() { 
     return RowType.values().length; 

    } 

    @Override 
    public int getItemViewType(int position) { 
     return getItem(position).getViewType(); 
    } 
@Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     return getItem(position).getView(mInflater, convertView); 
    } 

編輯 更好的性能..可以滾動

private static final int TYPE_ITEM = 0; 
private static final int TYPE_SEPARATOR = 1; 

public View getView(int position, View convertView, ViewGroup parent) { 
    ViewHolder holder = null; 
    int rowType = getItemViewType(position); 
    View View; 
    if (convertView == null) { 
     holder = new ViewHolder(); 
     switch (rowType) { 
      case TYPE_ITEM: 
       convertView = mInflater.inflate(R.layout.task_details_row, null); 
       holder.View=getItem(position).getView(mInflater, convertView); 
       break; 
      case TYPE_SEPARATOR: 
       convertView = mInflater.inflate(R.layout.task_detail_header, null); 
       holder.View=getItem(position).getView(mInflater, convertView); 
       break; 
     } 
     convertView.setTag(holder); 
    } 
    else 
    { 
     holder = (ViewHolder) convertView.getTag(); 
    } 
    return convertView; 
} 

public static class ViewHolder { 
    public View View; } 
} 

時注意到接着我們班實行Item和膨脹正確的佈局。在你的情況,你會有類似Header類和ListItem類。

public class Header implements Item { 
    private final String   name; 

    public Header(String name) { 
     this.name = name; 
    } 

    @Override 
    public int getViewType() { 
     return RowType.HEADER_ITEM.ordinal(); 
    } 

    @Override 
    public View getView(LayoutInflater inflater, View convertView) { 
     View view; 
     if (convertView == null) { 
      view = (View) inflater.inflate(R.layout.header, null); 
      // Do some initialization 
     } else { 
      view = convertView; 
     } 

     TextView text = (TextView) view.findViewById(R.id.separator); 
     text.setText(name); 

     return view; 
    } 

} 

然後是ListItem

public class ListItem implements Item { 
    private final String   str1; 
    private final String   str2; 

    public ListItem(String text1, String text2) { 
     this.str1 = text1; 
     this.str2 = text2; 
    } 

    @Override 
    public int getViewType() { 
     return RowType.LIST_ITEM.ordinal(); 
    } 

    @Override 
    public View getView(LayoutInflater inflater, View convertView) { 
     View view; 
     if (convertView == null) { 
      view = (View) inflater.inflate(R.layout.my_list_item, null); 
      // Do some initialization 
     } else { 
      view = convertView; 
     } 

     TextView text1 = (TextView) view.findViewById(R.id.list_content1); 
     TextView text2 = (TextView) view.findViewById(R.id.list_content2); 
     text1.setText(str1); 
     text2.setText(str2); 

     return view; 
    } 

} 

和一個簡單的Activity以顯示它

public class MainActivity extends ListActivity { 

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

     List<Item> items = new ArrayList<Item>(); 
     items.add(new Header("Header 1")); 
     items.add(new ListItem("Text 1", "Rabble rabble")); 
     items.add(new ListItem("Text 2", "Rabble rabble")); 
     items.add(new ListItem("Text 3", "Rabble rabble")); 
     items.add(new ListItem("Text 4", "Rabble rabble")); 
     items.add(new Header("Header 2")); 
     items.add(new ListItem("Text 5", "Rabble rabble")); 
     items.add(new ListItem("Text 6", "Rabble rabble")); 
     items.add(new ListItem("Text 7", "Rabble rabble")); 
     items.add(new ListItem("Text 8", "Rabble rabble")); 

     TwoTextArrayAdapter adapter = new TwoTextArrayAdapter(this, items); 
     setListAdapter(adapter); 
    } 

} 

佈局R.layout.header

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="horizontal" > 

    <TextView 
     style="?android:attr/listSeparatorTextViewStyle" 
     android:id="@+id/separator" 
     android:text="Header" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:background="#757678" 
     android:textColor="#f5c227" /> 

</LinearLayout> 

佈局R.layout.my_list_item

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="horizontal" > 

    <TextView 
     android:id="@+id/list_content1" 
     android:layout_width="wrap_content" 
     android:layout_height="match_parent" 
     android:layout_margin="5dip" 
     android:clickable="false" 
     android:gravity="center" 
     android:longClickable="false" 
     android:paddingBottom="1dip" 
     android:paddingTop="1dip" 
     android:text="sample" 
     android:textColor="#ff7f1d" 
     android:textSize="17dip" 
     android:textStyle="bold" /> 

    <TextView 
     android:id="@+id/list_content2" 
     android:layout_width="wrap_content" 
     android:layout_height="match_parent" 
     android:layout_margin="5dip" 
     android:clickable="false" 
     android:gravity="center" 
     android:linksClickable="false" 
     android:longClickable="false" 
     android:paddingBottom="1dip" 
     android:paddingTop="1dip" 
     android:text="sample" 
     android:textColor="#6d6d6d" 
     android:textSize="17dip" /> 

</LinearLayout> 

佈局R.layout.activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    tools:context=".MainActivity" > 

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

</RelativeLayout> 

您也可以更大膽和使用ViewHolders,負載的東西異步,或任何你喜歡。

+0

這是什麼R.layout.header?是不是my_list_item剛剛改名? –

+0

也R.id.seperator –

+0

我已經添加到帖子的佈局,他們大部分使用您的文章中的XML。 – antew

9

您可能正在尋找一個ExpandableListView其中有標題(組)分開項目(孩子)。

關於這個問題的好教程:here

+0

我不希望它們可擴展 –

+0

如果這是唯一的問題,你可以覆蓋onItemClick方法prev擴大/摺疊視圖。 –

+0

但我仍然需要將它們點擊以用於其他目的 –

2

作爲一種替代方案,有一個很好的3rd party library專爲此用例而設計。因此您需要根據存儲在適配器中的數據生成標題。他們被稱爲Rolodex適配器,並與ExpandableListViews一起使用。他們可以很容易地進行自定義,使其具有標題列表的正常列表。

使用OP的Event對象,並知道頭部是基於與之關聯的Date ......代碼將是這個樣子:

的活動

//There's no need to pre-compute what the headers are. Just pass in your List of objects. 
    EventDateAdapter adapter = new EventDateAdapter(this, mEvents); 
    mExpandableListView.setAdapter(adapter); 

適配器

private class EventDateAdapter extends NFRolodexArrayAdapter<Date, Event> { 

    public EventDateAdapter(Context activity, Collection<Event> items) { 
     super(activity, items); 
    } 

    @Override 
    public Date createGroupFor(Event childItem) { 
     //This is how the adapter determines what the headers are and what child items belong to it 
     return (Date) childItem.getDate().clone(); 
    } 

    @Override 
    public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition, 
          boolean isLastChild, View convertView, ViewGroup parent) { 
     //Inflate your view 

     //Gets the Event data for this view 
     Event event = getChild(groupPosition, childPosition); 

     //Fill view with event data 
    } 

    @Override 
    public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded, 
          View convertView, ViewGroup parent) { 
     //Inflate your header view 

     //Gets the Date for this view 
     Date date = getGroup(groupPosition); 

     //Fill view with date data 
    } 

    @Override 
    public boolean hasAutoExpandingGroups() { 
     //This forces our group views (headers) to always render expanded. 
     //Even attempting to programmatically collapse a group will not work. 
     return true; 
    } 

    @Override 
    public boolean isGroupSelectable(int groupPosition) { 
     //This prevents a user from seeing any touch feedback when a group (header) is clicked. 
     return false; 
    } 
} 
1

我做了什麼來製作日期(例如2016年12月1日)作爲標題。 我用StickyHeaderListView庫

https://github.com/emilsjolander/StickyListHeaders

轉換日期長以毫秒[不包括時間],並使其作爲標題編號。

@Override 
public long getHeaderId(int position) { 
    return <date in millis>; 
} 
0

Here is a sample project,基於antew的詳細實用的解答,實現與集成視圖持有者提高滾動性能多個頭一個ListView

在該項目中,在ListView表示的對象是任一類或HeaderItemRowItem的實例,這兩者都是抽象類Item的子類。Item的每個子類對應於自定義適配器ItemAdapter中的不同視圖類型。 ItemAdapter上的方法getView()將每個列表項目的視圖的代表創建爲HeaderItemRowItem上的個別化getView()方法,具體取決於在適配器上傳遞給getView()方法的位置處使用的Item子類。每個Item子類都提供它自己的查看持有者。

視圖持有人的實施如下。 Item子類的getView()方法檢查傳遞給ItemAdapter上的getView()方法的View對象是否爲空。如果是,則適當的佈局被充氣,並且通過View.setTag()實例化視圖保持器對象並且與充氣視圖關聯。如果View對象不爲空,則視圖持有者對象已與該視圖關聯,並通過View.getTag()檢索視圖持有者。其中使用的視圖持有者的方式可以在下面的代碼段中可以看出從HeaderItem

@Override 
View getView(LayoutInflater i, View v) { 
    ViewHolder h; 
    if (v == null) { 
     v = i.inflate(R.layout.header, null); 
     h = new ViewHolder(v); 
     v.setTag(h); 
    } else { 
     h = (ViewHolder) v.getTag(); 
    } 
    h.category.setText(text()); 
    return v; 
} 

private class ViewHolder { 
    final TextView category; 

    ViewHolder(View v) { 
     category = v.findViewById(R.id.category); 
    } 
} 

ListView中的完整的實現如下。這裏是Java代碼:

import android.app.ListActivity; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.TextView; 

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashSet; 
import java.util.List; 
import java.util.Set; 

public class MainActivity extends ListActivity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setListAdapter(new ItemAdapter(getItems())); 
    } 

    class ItemAdapter extends ArrayAdapter<Item> { 
     final private List<Class<?>> viewTypes; 

     ItemAdapter(List<Item> items) { 
      super(MainActivity.this, 0, items); 
      if (items.contains(null)) 
       throw new IllegalArgumentException("null item"); 
      viewTypes = getViewTypes(items); 
     } 

     private List<Class<?>> getViewTypes(List<Item> items) { 
      Set<Class<?>> set = new HashSet<>(); 
      for (Item i : items) 
       set.add(i.getClass()); 
      List<Class<?>> list = new ArrayList<>(set); 
      return Collections.unmodifiableList(list); 
     } 

     @Override 
     public int getViewTypeCount() { 
      return viewTypes.size(); 
     } 

     @Override 
     public int getItemViewType(int position) { 
      Item t = getItem(position); 
      return viewTypes.indexOf(t.getClass()); 
     } 

     @Override 
     public View getView(int position, View v, ViewGroup unused) { 
      return getItem(position).getView(getLayoutInflater(), v); 
     } 
    } 

    abstract private class Item { 
     final private String text; 

     Item(String text) { 
      this.text = text; 
     } 

     String text() { return text; } 

     abstract View getView(LayoutInflater i, View v); 
    } 

    private class HeaderItem extends Item { 
     HeaderItem(String text) { 
      super(text); 
     } 

     @Override 
     View getView(LayoutInflater i, View v) { 
      ViewHolder h; 
      if (v == null) { 
       v = i.inflate(R.layout.header, null); 
       h = new ViewHolder(v); 
       v.setTag(h); 
      } else { 
       h = (ViewHolder) v.getTag(); 
      } 
      h.category.setText(text()); 
      return v; 
     } 

     private class ViewHolder { 
      final TextView category; 

      ViewHolder(View v) { 
       category = v.findViewById(R.id.category); 
      } 
     } 
    } 

    private class RowItem extends Item { 
     RowItem(String text) { 
      super(text); 
     } 

     @Override 
     View getView(LayoutInflater i, View v) { 
      ViewHolder h; 
      if (v == null) { 
       v = i.inflate(R.layout.row, null); 
       h = new ViewHolder(v); 
       v.setTag(h); 
      } else { 
       h = (ViewHolder) v.getTag(); 
      } 
      h.option.setText(text()); 
      return v; 
     } 

     private class ViewHolder { 
      final TextView option; 

      ViewHolder(View v) { 
       option = v.findViewById(R.id.option); 
      } 
     } 
    } 

    private List<Item> getItems() { 
     List<Item> t = new ArrayList<>(); 
     t.add(new HeaderItem("Header 1")); 
     t.add(new RowItem("Row 2")); 
     t.add(new HeaderItem("Header 3")); 
     t.add(new RowItem("Row 4")); 

     t.add(new HeaderItem("Header 5")); 
     t.add(new RowItem("Row 6")); 
     t.add(new HeaderItem("Header 7")); 
     t.add(new RowItem("Row 8")); 

     t.add(new HeaderItem("Header 9")); 
     t.add(new RowItem("Row 10")); 
     t.add(new HeaderItem("Header 11")); 
     t.add(new RowItem("Row 12")); 

     t.add(new HeaderItem("Header 13")); 
     t.add(new RowItem("Row 14")); 
     t.add(new HeaderItem("Header 15")); 
     t.add(new RowItem("Row 16")); 

     t.add(new HeaderItem("Header 17")); 
     t.add(new RowItem("Row 18")); 
     t.add(new HeaderItem("Header 19")); 
     t.add(new RowItem("Row 20")); 

     t.add(new HeaderItem("Header 21")); 
     t.add(new RowItem("Row 22")); 
     t.add(new HeaderItem("Header 23")); 
     t.add(new RowItem("Row 24")); 

     t.add(new HeaderItem("Header 25")); 
     t.add(new RowItem("Row 26")); 
     t.add(new HeaderItem("Header 27")); 
     t.add(new RowItem("Row 28")); 
     t.add(new RowItem("Row 29")); 
     t.add(new RowItem("Row 30")); 

     t.add(new HeaderItem("Header 31")); 
     t.add(new RowItem("Row 32")); 
     t.add(new HeaderItem("Header 33")); 
     t.add(new RowItem("Row 34")); 
     t.add(new RowItem("Row 35")); 
     t.add(new RowItem("Row 36")); 

     return t; 
    } 

} 

也有兩個列表項目佈局,每個項目的子類別。這裏是佈局header,通過HeaderItem使用:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:background="#FFAAAAAA" 
    > 
    <TextView 
     android:id="@+id/category" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_margin="4dp" 
     android:textColor="#FF000000" 
     android:textSize="20sp" 
     android:textStyle="bold" 
     /> 
</LinearLayout> 

這裏是佈局row,通過RowItem使用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:minHeight="?android:attr/listPreferredItemHeight" 
    > 
    <TextView 
     android:id="@+id/option" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:textSize="15sp" 
     /> 
</LinearLayout> 

下面是所得的ListView的一部分的圖像:

ListView with multiple headers