Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draw divider for list header/footer views #268

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions library/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<attr name="android:scrollbarStyle" />
<attr name="android:divider" />
<attr name="android:dividerHeight" />
<attr name="android:headerDividersEnabled" />
<attr name="android:footerDividersEnabled" />

<!-- StickyListHeaders attributes -->
<attr name="hasStickyHeaders" format="boolean" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public StickyListHeadersListView(Context context, AttributeSet attrs, int defSty
mDividerHeight = mList.getDividerHeight();
mList.setDivider(null);
mList.setDividerHeight(0);
mList.setListDivider(mDivider, mDividerHeight);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very confusing as it looks like the two above line of code are useless even though they aren't

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because mDivider and mDividerHeight must be passed to mList, those three set*Divider*()s will be there anyway. I'd love to make it less confusing, yet nothing came up in my mind for now.


if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.StickyListHeadersListView, 0, 0);
Expand Down Expand Up @@ -199,6 +200,11 @@ public StickyListHeadersListView(Context context, AttributeSet attrs, int defSty
mDividerHeight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_dividerHeight,
mDividerHeight);

mList.setListDivider(mDivider, mDividerHeight);

mList.setHeaderDividersEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_headerDividersEnabled, true));
mList.setFooterDividersEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_footerDividersEnabled, true));

// -- StickyListHeaders attributes --
mAreHeadersSticky = a.getBoolean(R.styleable.StickyListHeadersListView_hasStickyHeaders, true);
mIsDrawingListUnderStickyHeader = a.getBoolean(
Expand Down Expand Up @@ -652,13 +658,15 @@ public StickyListHeadersAdapter getAdapter() {

public void setDivider(Drawable divider) {
mDivider = divider;
mList.setListDivider(mDivider, mDividerHeight);
if (mAdapter != null) {
mAdapter.setDivider(mDivider, mDividerHeight);
}
}

public void setDividerHeight(int dividerHeight) {
mDividerHeight = dividerHeight;
mList.setListDivider(mDivider, mDividerHeight);
if (mAdapter != null) {
mAdapter.setDivider(mDivider, mDividerHeight);
}
Expand All @@ -672,6 +680,14 @@ public int getDividerHeight() {
return mDividerHeight;
}

public void setHeaderDividersEnabled(boolean headerDividersEnabled) {
mList.setHeaderDividersEnabled(headerDividersEnabled);
}

public void setFooterDividersEnabled(boolean footerDividersEnabled) {
mList.setFooterDividersEnabled(footerDividersEnabled);
}

public void setOnScrollListener(OnScrollListener onScrollListener) {
mOnScrollListenerDelegate = onScrollListener;
}
Expand Down Expand Up @@ -718,6 +734,10 @@ public void addFooterView(View v) {
mList.addFooterView(v);
}

public void addFooterView(View v, Object data, boolean isSelectable) {
mList.addFooterView(v, data, isSelectable);
}

public void removeFooterView(View v) {
mList.removeFooterView(v);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public class WrapperView extends ViewGroup {
super(c);
}

WrapperView(Context c, View item) {
super(c);

update(item, null, null, 0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can easily get confusing when a constructor does anything else than constructing an object, i think calling update() should be done outside of the constructor as before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you've made a valid point. I'll revert this change.

}

public boolean hasHeader() {
return mHeader != null;
}
Expand Down
124 changes: 114 additions & 10 deletions library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import android.widget.AbsListView;
Expand All @@ -19,11 +20,18 @@ interface LifeCycleListener {
}

private LifeCycleListener mLifeCycleListener;
private List<View> mFooterViews;
private List<WrapperView> mHeaderWrapperViews;
private List<WrapperView> mFooterWrapperViews;
private int mTopClippingLength;
private Rect mSelectorRect = new Rect();// for if reflection fails
private Field mSelectorPositionField;
private boolean mClippingToPadding = true;
private Drawable mDivider;
private int mDividerHeight;
private Rect mDividerTempRect = new Rect();
private boolean mHeaderDividersEnabled = true;
private boolean mFooterDividersEnabled = true;


public WrapperViewList(Context context) {
super(context);
Expand Down Expand Up @@ -103,36 +111,132 @@ protected void dispatchDraw(Canvas canvas) {
} else {
super.dispatchDraw(canvas);
}

// bottom divider
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not that fun code to put in this class. I don't have any other suggestion right now but i definitely think this should somehow be moved into the WrapperView.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I don't like this code either in that this makes similar logic to be scattered, also have no idea how this could be done nice at this moment.

if (mFooterDividersEnabled && mDivider != null && getChildCount() > 0) {
int t = getChildAt(getChildCount() - 1).getBottom();

mDividerTempRect.set(mDivider.getBounds());

mDivider.setBounds(getPaddingLeft(), t, getRight() - getLeft() - getPaddingRight(), t + mDividerHeight);
mDivider.draw(canvas);
mDivider.setBounds(mDividerTempRect); // restore bounds
}
mLifeCycleListener.onDispatchDrawOccurred(canvas);
}

void setLifeCycleListener(LifeCycleListener lifeCycleListener) {
mLifeCycleListener = lifeCycleListener;
}

void setListDivider(Drawable divider, int dividerHeight) {
this.mDivider = divider;
this.mDividerHeight = dividerHeight;
}

@Override
public void setHeaderDividersEnabled(boolean headerDividersEnabled) {
super.setHeaderDividersEnabled(headerDividersEnabled);
this.mHeaderDividersEnabled = headerDividersEnabled;

updateHeaderViews();
}

@Override
public void setFooterDividersEnabled(boolean footerDividersEnabled) {
super.setFooterDividersEnabled(footerDividersEnabled);
this.mFooterDividersEnabled = footerDividersEnabled;

updateFooterViews();
}

@Override
public void addHeaderView(View v, Object data, boolean isSelectable) {
if (mHeaderWrapperViews == null) {
mHeaderWrapperViews = new ArrayList<WrapperView>();
}

WrapperView wv = new WrapperView(getContext(), v);
super.addHeaderView(wv, data, isSelectable);
mHeaderWrapperViews.add(wv);

updateHeaderViews();
}

@Override
public void addFooterView(View v) {
super.addFooterView(v);
if (mFooterViews == null) {
mFooterViews = new ArrayList<View>();
public boolean removeHeaderView(View v) {
WrapperView wv = getWrapperViewByItem(mHeaderWrapperViews, v);
if (wv != null) {
super.removeHeaderView(wv);
mHeaderWrapperViews.remove(wv);
updateHeaderViews();
return true;
}
return false;
}

private void updateHeaderViews() {
for (int i = 0; i < getHeaderViewsCount(); i++) {
WrapperView wv = mHeaderWrapperViews.get(i);
if (i == 0 || !mHeaderDividersEnabled) {
wv.update(wv.getItem(), null, null, 0);
} else {
wv.update(wv.getItem(), null, mDivider, mDividerHeight);
}
}
mFooterViews.add(v);
}

@Override
public void addFooterView(View v, Object data, boolean isSelectable) {
if (mFooterWrapperViews == null) {
mFooterWrapperViews = new ArrayList<WrapperView>();
}

WrapperView wv = new WrapperView(getContext(), v);
super.addFooterView(wv, data, isSelectable);
mFooterWrapperViews.add(wv);

updateFooterViews();
}

@Override
public boolean removeFooterView(View v) {
if (super.removeFooterView(v)) {
mFooterViews.remove(v);
WrapperView wv = getWrapperViewByItem(mFooterWrapperViews, v);
if (wv != null) {
super.removeFooterView(wv);
mFooterWrapperViews.remove(wv);
// no need to update dividers
return true;
}
return false;
}

private void updateFooterViews() {
for (int i = 0; i < getFooterViewsCount(); i++) {
WrapperView wv = mFooterWrapperViews.get(i);
if (!mFooterDividersEnabled) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small detail but i would rather have if(true) than if(!false) as it reads a bit better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean mFooterDividersEnabled == false?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think he means

if (mFooterDividersEnabled) {
    wv.update(wv.getItem(), null, mDivider, mDividerHeight);
} else {
    wv.update(wv.getItem(), null, null, 0);
}

Invert the if-condition and switch the bodies.

wv.update(wv.getItem(), null, null, 0);
} else {
wv.update(wv.getItem(), null, mDivider, mDividerHeight);
}
}
}

static WrapperView getWrapperViewByItem(List<WrapperView> wrapperViewList, View item) {
for (WrapperView wrapperView : wrapperViewList) {
if (wrapperView.getItem() == item) {
return wrapperView;
}
}
return null;
}

boolean containsFooterView(View v) {
if (mFooterViews == null) {
if (mFooterWrapperViews == null) {
return false;
}
return mFooterViews.contains(v);

return getWrapperViewByItem(mFooterWrapperViews, v) != null;
}

void setTopClippingLength(int topClipping) {
Expand Down
17 changes: 9 additions & 8 deletions sample/res/layout/list_footer.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TextView android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="700dip"
android:gravity="center"
android:layout_gravity="center"/>

android:layout_height="match_parent">

<TextView
android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="350dip"
android:gravity="center"
android:layout_gravity="center" />


</FrameLayout>
17 changes: 9 additions & 8 deletions sample/res/layout/list_header.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TextView android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="400dip"
android:gravity="center"
android:layout_gravity="center"/>

android:layout_height="match_parent">

<TextView
android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="200dip"
android:gravity="center"
android:layout_gravity="center" />


</FrameLayout>
20 changes: 18 additions & 2 deletions sample/res/layout/main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
android:padding="16dp"
android:scrollbarStyle="outsideOverlay"
android:fastScrollEnabled="true"
android:overScrollMode="never"/>
android:headerDividersEnabled="true"
android:footerDividersEnabled="true"
android:overScrollMode="never"/>

<TextView
android:id="@+id/empty"
Expand All @@ -43,7 +45,7 @@

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<se.emilsjolander.stickylistheaders.views.UnderlineTextView
Expand Down Expand Up @@ -110,6 +112,20 @@
android:text="@string/fast_scroll"
android:id="@+id/fast_scroll_checkBox"
android:checked="true"/>

<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/header_dividers"
android:id="@+id/header_dividers_checkBox"
android:checked="true"/>

<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/footer_dividers"
android:id="@+id/footer_dividers_checkBox"
android:checked="true"/>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.DrawerLayout>
2 changes: 2 additions & 0 deletions sample/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@
<string name="fade_header">Fade header</string>
<string name="draw_behind_header">Draw behind header</string>
<string name="fast_scroll">Fast scroll</string>
<string name="header_dividers">Header dividers</string>
<string name="footer_dividers">Footer dividers</string>

</resources>
Loading