I'm trying to achieve this effect that can be seen above on StickyListHeaders's sample app:
Basically I need to show a single, static, fixed header view on top of a ListView but bellow its scrollbar. I don't need anything related to sections or alphabetical indexing or anything like that.
I'm unable to figure out how to do this based on the source code of StickyListHeaders. I tried subclassing ListView
and overriding dispatchDraw()
like this:
protected void dispatchDraw(Canvas canvas)
{
View view = LayoutInflater.from(getContext()).inflate(R.layout.header, this, false);
drawChild(canvas, view, getDrawingTime());
super.dispatchDraw(canvas);
}
But it doesn't work, no header is drawn.
Answering my own question. This ListView subclass is able to do what I wanted. The first element of the list can become fixed calling showFixedHeader()
:
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
public class FixedHeaderListView extends ListView
{
private View fixedHeader = null;
private boolean fixedHeaderLayoutDone = false;
private boolean showFixedHeader = true;
@SuppressWarnings("unused")
public FixedHeaderListView(Context context)
{
super(context);
}
@SuppressWarnings("unused")
public FixedHeaderListView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@SuppressWarnings("unused")
public FixedHeaderListView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public void showFixedHeader(boolean show)
{
this.showFixedHeader = show;
requestLayout(); // Will cause layoutChildren() and dispatchDraw() to be called
}
@Override
protected void layoutChildren()
{
super.layoutChildren();
if (!fixedHeaderLayoutDone)
{
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0)
{
// Layout the first item in the adapter's data set as the fixed header
fixedHeader = adapter.getView(0, null, this);
if (fixedHeader != null)
{
// Measure and layout
LayoutParams layoutParams = (LayoutParams)fixedHeader.getLayoutParams();
if (layoutParams == null)
{
layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED)
{
heightMode = MeasureSpec.EXACTLY;
}
int heightSize = MeasureSpec.getSize(layoutParams.height);
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight)
{
heightSize = maxHeight;
}
int widthSpec = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
fixedHeader.measure(widthSpec, heightSpec);
fixedHeader.layout(0, 0, fixedHeader.getMeasuredWidth(), fixedHeader.getMeasuredHeight());
// Flag as layout done
fixedHeaderLayoutDone = true;
}
}
}
}
@Override @SuppressWarnings("NullableProblems")
protected void dispatchDraw(Canvas canvas)
{
super.dispatchDraw(canvas);
if (fixedHeader != null && showFixedHeader)
{
drawChild(canvas, fixedHeader, getDrawingTime());
}
}
}
It's not heavily tested, but it's a good starting point.