[android] Android ListView 헤더
일종의 이벤트가있는 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" />
그리고 필요하지 않을 때는 숨기고 필요할 때 보여 주려고했지만 나머지 코드를 엉망으로 만들었습니다. 튜토리얼을 몇 번 더 시도했지만 효과도 동일했습니다.
누구든지 그렇게 쉬운 방법을 안내해 줄 수 있습니까?
답변
방법은 다음과 같습니다. 키는 클래스의 getItemViewType 및 getViewTypeCount 입니다 Adapter
. getViewTypeCount
목록에있는 항목의 유형을 반환합니다.이 경우에는 헤더 항목과 이벤트 항목이 있으므로 두 개입니다. getItemViewType
어떤 종류의 반환해야 View
우리가 입력에 있습니다 position
.
안드로이드는 당신에게 올바른 유형을 통과 처리됩니다 View
에 convertView
자동으로.
아래 코드의 결과는 다음과 같습니다.
먼저 두 개의 목록 항목 유형이 구현할 인터페이스가 있습니다.
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); }
EDIT
Better For Performance .. 스크롤 할 때 알아 차릴 수 있습니다.
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
, 비동기식으로로드하거나 원하는대로 사용할 수 있습니다 .
답변
항목 (자식)을 구분하는 헤더 (그룹)가 있는 ExpandableListView 를 찾고있을 것입니다 .
주제에 대한 멋진 튜토리얼 : 여기 .
답변
대안 으로이 사용 사례를 위해 설계된 멋진 타사 라이브러리가 있습니다. 어댑터에 저장되는 데이터를 기반으로 헤더를 생성해야합니다. 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;
}
}
답변
날짜 (예 : 2016 년 12 월 1 일)를 헤더로 만들기 위해 수행 한 작업입니다. StickyHeaderListView 라이브러리를 사용했습니다.
https://github.com/emilsjolander/StickyListHeaders
날짜를 밀리 초 단위의 long으로 변환하고 [시간을 포함하지 않음] 헤더 ID로 만듭니다.
@Override
public long getHeaderId(int position) {
return <date in millis>;
}
답변
다음은 antew의 상세하고 유용한 답변을 기반으로 한 샘플 프로젝트로 , ListView
스크롤 성능을 향상시키기 위해 뷰 홀더를 통합하는 다중 헤더를 구현합니다 .
이 프로젝트에서에 표시된 객체는 ListView
클래스 HeaderItem
또는 클래스의 인스턴스이며 RowItem
둘 다 추상 클래스의 하위 클래스 Item
입니다. 의 각 하위 클래스 Item
는 사용자 정의 어댑터의 다른보기 유형에 해당합니다 ItemAdapter
. 의 메서드 는 어댑터 의 메서드에 전달 된 위치에서 사용되는 하위 클래스 에 따라 또는 의 개별화 된 메서드에 각 목록 항목에 대한 뷰 생성 getView()
을 ItemAdapter
위임합니다 . 각 서브 클래스는 자체 뷰 홀더를 제공합니다.getView()
HeaderItem
RowItem
Item
getView()
Item
뷰 홀더는 다음과 같이 구현됩니다. getView()
온 방법 Item
서브 클래스가 있는지 여부를 확인 View
받는 전달 된 객체 getView()
에 대한 방법은 ItemAdapter
널이다. 그렇다면 적절한 레이아웃이 확장되고 뷰 홀더 객체가 인스턴스화되고를 통해 확장 된 뷰와 연결됩니다 View.setTag()
. 경우 View
객체가 null는 아니고, 다음 뷰 홀더 객체는 이미 뷰와 연관되고, 뷰 홀더를 통해 검색됩니다 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;
}
}
또한 각 Item 하위 클래스에 대해 하나씩 두 개의 목록 항목 레이아웃이 있습니다. 다음은 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의 일부 이미지입니다.