Reputation: 309
I am having a listView, so i want to add a sticky header so that it sticks to the top of the listView and when a different category starts in the listView a Different header take its place, Like the contacts, where is "a" as a Sticky header is at the top till "b" comes in. Is there any library to do it?? I am using custom List Adapter to show my List...
this is my custom adapter class
public class NewsRowAdapter extends ArrayAdapter<Item> {
private Activity activity;
private List<Item> items;
private Item objBean;
private int row;
private DisplayImageOptions options;
ImageLoader imageLoader;
public NewsRowAdapter(Activity act, int resource, List<Item> arrayList) {
super(act, resource, arrayList);
this.activity = act;
this.row = resource;
this.items = arrayList;
imageLoader = ImageLoader.getInstance();
File cacheDir1 = StorageUtils.getCacheDirectory(activity);
ImageLoaderConfiguration config = new
ImageLoaderConfiguration.Builder(activity)
.maxImageWidthForMemoryCache(600)
.maxImageHeightForMemoryCache(400)
.httpConnectTimeout(5000)
.httpReadTimeout(20000)
.threadPoolSize(3)
.threadPriority(Thread.MIN_PRIORITY + 3)
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new UsingFreqLimitedMemoryCache(20000)) // You can pass your own memory cache implementation
.discCache(new TotalSizeLimitedDiscCache(cacheDir1, 30000)) // You can pass your own disc cache implementation
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.build();
ImageLoader.getInstance().init(config);
// imageLoader = ImageLoader;
//
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.icon2x)
.showImageForEmptyUrl(R.drawable.icon2x).cacheInMemory()
.cacheOnDisc().build();
//imageLoader = ImageLoader.getInstance();
}
@Override
public Item getItem(int position)
{
return items.get(position);
}
@Override
public int getCount()
{
return items.size();
}
@Override
public int getViewTypeCount()
{
return 3;
}
@Override
public int getItemViewType(int position)
{
Item item = items.get(position);
if (item.isHeader())
{
return TYPE_SECTION_HEADER;
}
else
{
return TYPE_LIST_ITEM;
}
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(row, null);
holder = new ViewHolder();
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
if ((items == null) || ((position + 1) > items.size()))
return view;
objBean = items.get(position);
holder.tvName = (TextView) view.findViewById(R.id.title);
holder.tvId = (TextView) view.findViewById(R.id.id);
holder.tvFlag = (TextView) view.findViewById(R.id.flag);
holder.tvimageurl=(TextView) view.findViewById(R.id.imageurl);
holder.tvGender = (ImageView) view.findViewById(R.id.image);
//holder.tvAge = (TextView) view.findViewById(R.id.tvage);
holder.pbar = (ProgressBar) view.findViewById(R.id.pbar);
if (holder.tvName != null && null != objBean.getName()
&& objBean.getName().trim().length() > 0) {
holder.tvName.setText(Html.fromHtml(objBean.getName()));
}
if (holder.tvId != null && null != objBean.getId()
&& objBean.getId().trim().length() > 0) {
holder.tvId.setText(Html.fromHtml(objBean.getId()));
}
if (holder.tvFlag != null && null != objBean.getFlag()
&& objBean.getFlag().trim().length() > 0) {
holder.tvFlag.setText(Html.fromHtml(objBean.getFlag()));
}
if (holder.tvimageurl != null && null != objBean.getGender()
&& objBean.getFlag().trim().length() > 0) {
holder.tvimageurl.setText(Html.fromHtml(objBean.getGender()));
}
//if (holder.tvBDate != null && null != objBean.getBirthdate()
// && objBean.getBirthdate().trim().length() > 0) {
// holder.tvBDate.setText(Html.fromHtml(objBean.getBirthdate()));
//}
if (holder.tvGender != null) {
if (null != objBean.getGender()
&& objBean.getGender().trim().length() > 0) {
final ProgressBar pbar = holder.pbar;
imageLoader.displayImage(objBean.getGender(), holder.tvGender,
options, new ImageLoadingListener() {
@Override
public void onLoadingComplete() {
pbar.setVisibility(View.INVISIBLE);
}
@Override
public void onLoadingFailed() {
pbar.setVisibility(View.INVISIBLE);
}
@Override
public void onLoadingStarted() {
pbar.setVisibility(View.INVISIBLE);
}
});
} else {
holder.tvGender.setImageResource(R.drawable.icon2x);
}
}
return view;
}
public class ViewHolder {
public TextView tvimageurl;
public TextView tvFlag;
public TextView tvId;
public ProgressBar pbar;
public TextView tvName, tvCity, tvBDate, tvAge;
ImageView tvGender;
}
}
help needed.....
Upvotes: 4
Views: 20130
Reputation: 8281
I struggled quite a long time writing my own StickyHeader
as the StickyListHeaders
git was not perfectly matching my needs. However, the StickyListHeaders
help me a lot in understanding how to finally have it working and therefore the writer of the StickyListHeaders
deserve mentioning it.
Many things have to be put together to have a sticky header working.
First, declare the following
private TextView mStickyHeader;
private TextView mStickyHeader2;
private int mCurrentStickyHeaderSection;
in onActivityCreated
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
mCurrentStickyHeaderSection = -1;
mStickyHeader = (TextView) getView().findViewById(R.id.textview_sticky_header_section);
mStickyHeader2 = (TextView) getView().findViewById(R.id.textview_sticky_header_section_2);
mListView = (ListView) getView().findViewById(R.id.listView_with_sticky_headers);
mListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
updateStickyHeader(firstVisibleItem);
}
@Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {
// TODO Auto-generated method stub
}
});
I assume you have written a CustomAdapter
with section indexes and will only focus on the sticky headers.
The following method contains all that you need to have the sticky header. The tricky part is determining if the firstVisibleItem
is a header (pos = -1) or not and the corresponding section. This part can take you a few times to have it working properly.
private void updateStickyHeader(int firstVisibleItem) {
// here is the tricky part. You have to determine pos and section
// pos is the position within a section pos = -1 means it's the header of the section
// you have to determine if firstVisibleItem is a header or not
// you also have to determine the section to which belongs the first item
int pos = whatIsThePositionOfTheItem[firstVisibleItem];
int section = whatIsTheSectionOfTheItem[firstVisibleItem];
if (section != mCurrentStickyHeaderSection) {
mStickyHeader.setText("Your_Previous_Section_Text");
mStickyHeader2.setText("Your_Next_Section_Text");
mCurrentStickyHeaderSection = section;
}
int stickyHeaderHeight = mStickyHeader.getHeight();
if (stickyHeaderHeight == 0) {
stickyHeaderHeight = mStickyHeader.getMeasuredHeight();
}
View SectionLastView = mListView.getChildAt(0);
if (SectionLastView != null && pos == -1 && SectionLastView.getBottom() <= stickyHeaderHeight) {
int lastViewBottom = SectionLastView.getBottom();
mStickyHeader.setTranslationY(lastViewBottom - stickyHeaderHeight);
mStickyHeader2.setTranslationY(lastViewBottom - stickyHeaderHeight + mStickyHeader.getHeight());
} else if (stickyHeaderHeight != 0) {
mStickyHeader.setTranslationY(0);
mStickyHeader2.setTranslationY(mStickyHeader.getHeight());
}
}
Finally, the layout must contain a FrameLayout
and should look like that
<?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" >
//this textview is just a global title of your listview if you need one but can be remove
<TextView
android:id="@+id/textview_title"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textIsSelectable="false" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textview_sticky_header_section"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textIsSelectable="false" />
<TextView
android:id="@+id/textview_sticky_header_section_2"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textIsSelectable="false" />
</FrameLayout>
<ListView
android:id="@+id/listView_with_sticky_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fastScrollEnabled="true"
android:longClickable="true" >
</ListView>
</LinearLayout>
Update
As asked in the comments here is an explanation of the whatIsThePositionOfTheItem
and whatIsTheSectionOfTheItem
. These methods have to give back the position
and section
of a given item.
In my case it was quite simple, my data was already containing the section
and position
. Actually, I am displaying text, and that text starts with the section
and position
. So I simply parse that text to determine the section
and position
.
For others, it is hard to give an example because it really depends on what you are displaying. But to make a long story short whatIsThePositionOfTheItem
and whatIsTheSectionOfTheItem
have to return the position and section of a given item. You will probably have to fill a table with the position
and section
of each item in your list and get the position
and section
from that table.
Upvotes: 7
Reputation: 17613
There is a library exactly with that name that implements what you want:
You can check it out here: https://github.com/emilsjolander/StickyListHeaders
Upvotes: 5