Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created August 4, 2013 01:23
Show Gist options
  • Save slightfoot/6148668 to your computer and use it in GitHub Desktop.
Save slightfoot/6148668 to your computer and use it in GitHub Desktop.
ScrollingSyncListView lets a ListView position its sibiling views based on the postions of its children.
import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.WrapperListAdapter;
public class ScrollingSyncListView extends ListView
{
private SparseIntArray mScrollingSiblings = new SparseIntArray();
public ScrollingSyncListView(Context context)
{
this(context, null);
}
public ScrollingSyncListView(Context context, AttributeSet attrs)
{
this(context, attrs, android.R.attr.listViewStyle);
}
public ScrollingSyncListView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
updateSiblingPositions();
}
}
);
}
public void addScrollingSibling(int position, int viewId)
{
mScrollingSiblings.append(position, viewId);
}
public boolean removeScrollingSibling(int position)
{
int index = mScrollingSiblings.indexOfKey(position);
if(index < 0){
return false;
}
mScrollingSiblings.removeAt(index);
return true;
}
public void clearScrollingSiblings()
{
mScrollingSiblings.clear();
}
@Override
public void setAdapter(ListAdapter adapter)
{
if(adapter != null){
adapter = new ScrollingSyncAdapter(adapter);
}
super.setAdapter(adapter);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
super.onScrollChanged(l, t, oldl, oldt);
updateSiblingPositions();
}
private void updateSiblingPositions()
{
ViewGroup parent = (ViewGroup)getParent();
final int firstPos = getFirstVisiblePosition();
final int lastPos = getLastVisiblePosition();
final int size = mScrollingSiblings.size();
for(int i = 0; i < size; i++){
View view = parent.findViewById(mScrollingSiblings.valueAt(i));
if(view == null){
continue;
}
int pos = mScrollingSiblings.keyAt(i);
if(pos >= firstPos && pos <= lastPos){
View child = getChildAt(pos - firstPos);
int left = child.getLeft();
int top = child.getTop();
int width = view.getWidth();
int height = view.getHeight();
if(child instanceof PlaceHolderView){
((PlaceHolderView)child).setFixedDimensions(width, height);
}
view.layout(left, top, left + width, top + height);
view.setVisibility(View.VISIBLE);
}
else{
view.setVisibility(View.INVISIBLE);
}
}
}
private class ScrollingSyncAdapter implements WrapperListAdapter
{
private ListAdapter mWrapped;
private SparseArray<PlaceHolderView> mPlaceHolders = new SparseArray<PlaceHolderView>();
public ScrollingSyncAdapter(ListAdapter wrapped)
{
mWrapped = wrapped;
}
@Override
public ListAdapter getWrappedAdapter()
{
return mWrapped;
}
@Override
public void registerDataSetObserver(DataSetObserver observer)
{
mWrapped.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer)
{
mWrapped.unregisterDataSetObserver(observer);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View sibling;
int viewId = mScrollingSiblings.get(position);
if(viewId == 0 || (sibling = ((ViewGroup)parent.getParent()).findViewById(viewId)) == null){
return mWrapped.getView(position, convertView, parent);
}
PlaceHolderView view = mPlaceHolders.get(position);
if(view == null){
view = new PlaceHolderView(parent.getContext());
}
view.setFixedDimensions(sibling.getWidth(), sibling.getHeight());
return view;
}
@Override
public Object getItem(int position)
{
return mWrapped.getItem(position);
}
@Override
public long getItemId(int position)
{
return mWrapped.getItemId(position);
}
@Override
public boolean hasStableIds()
{
return mWrapped.hasStableIds();
}
@Override
public boolean areAllItemsEnabled()
{
return mWrapped.areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position)
{
return mWrapped.isEnabled(position);
}
@Override
public int getItemViewType(int position)
{
int viewId = mScrollingSiblings.get(position);
if(viewId != 0){
return IGNORE_ITEM_VIEW_TYPE;
}
return mWrapped.getItemViewType(position);
}
@Override
public int getViewTypeCount()
{
return mWrapped.getViewTypeCount();
}
@Override
public int getCount()
{
return mWrapped.getCount();
}
@Override
public boolean isEmpty()
{
return mWrapped.isEmpty();
}
}
private static class PlaceHolderView extends View
{
private int mWidth;
private int mHeight;
public PlaceHolderView(Context context)
{
super(context);
setWillNotDraw(true);
}
public void setFixedDimensions(int width, int height)
{
mWidth = width;
mHeight = height;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension(getDefaultSize(mWidth, widthMeasureSpec), getDefaultSize(mHeight, heightMeasureSpec));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment