3

I need to display infinite scroll of horizontal list view items(means need to repeat the items) for e.g

a,b,c,d,e,f,a,b,c,d,e,f,a,b,....

Bolded are Visible items on screen

The process is to repeat the list items after reaching the last list items, I tried to add list items on scrolling the list, so that I customized the horizontal list view class. But while scrolling to left after adding the list items on right, I get ArrayIndexOutOfBoundsException. I tried to add Integer.MAX VALUE option in base adapter to get infinite no of items but it has not helped while positioning the items(set selection()). If anyone know how to add and remove horizontal list items means,please share it.

Nandkumar Tekale
  • 16,024
  • 8
  • 58
  • 85
Surej
  • 358
  • 1
  • 2
  • 16
  • Is a Gallery not sufficient to achieve what you want? Help us understand why you would want the same item over and over again in sequence without it actually just being the same item in a turnstyle Gallery? – Jonathan Schneider May 25 '12 at 13:15
  • @jkschneider: TO feel that there are many items are present in the list to end user – Surej May 25 '12 at 13:21

2 Answers2

2

Instead of extending BaseAdapter extend ArrayAdapter.

Solution1::

Note::: Make the boolean variable isHorizontalListView = true in AgarwalActivity.java for horizontal listview.

HorizontalListView.java

public class HorizontalListView extends AdapterView<ListAdapter> {

    public boolean mAlwaysOverrideTouch = true;
    protected ListAdapter mAdapter;
    private int mLeftViewIndex = -1;
    private int mRightViewIndex = 0;
    protected int mCurrentX;
    protected int mNextX;
    private int mMaxX = Integer.MAX_VALUE;
    private int mDisplayOffset = 0;
    protected Scroller mScroller;
    private GestureDetector mGesture;
    private Queue<View> mRemovedViewQueue = new LinkedList<View>();
    private OnItemSelectedListener mOnItemSelected;
    private OnItemClickListener mOnItemClicked;
    private OnItemLongClickListener mOnItemLongClicked;
    private boolean mDataChanged = false;


    public HorizontalListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    private synchronized void initView() {
        mLeftViewIndex = -1;
        mRightViewIndex = 0;
        mDisplayOffset = 0;
        mCurrentX = 0;
        mNextX = 0;
        mMaxX = Integer.MAX_VALUE;
        mScroller = new Scroller(getContext());
        mGesture = new GestureDetector(getContext(), mOnGesture);
    }

    @Override
    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
        mOnItemSelected = listener;
    }

    @Override
    public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
        mOnItemClicked = listener;
    }

    @Override
    public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
        mOnItemLongClicked = listener;
    }

    private DataSetObserver mDataObserver = new DataSetObserver() {

        @Override
        public void onChanged() {
            synchronized(HorizontalListView.this){
                mDataChanged = true;
            }
            invalidate();
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            reset();
            invalidate();
            requestLayout();
        }

    };

    @Override
    public ListAdapter getAdapter() {
        return mAdapter;
    }

    @Override
    public View getSelectedView() {
        //TODO: implement
        return null;
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        if(mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mDataObserver);
        }
        mAdapter = adapter;
        mAdapter.registerDataSetObserver(mDataObserver);
        reset();
    }

    private synchronized void reset(){
        initView();
        removeAllViewsInLayout();
        requestLayout();
    }

    @Override
    public void setSelection(int position) {
        //TODO: implement
    }

    private void addAndMeasureChild(final View child, int viewPos) {
        LayoutParams params = child.getLayoutParams();
        if(params == null) {
            params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        }

        addViewInLayout(child, viewPos, params, true);
        child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
    }

    @Override
    protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if(mAdapter == null){
            return;
        }

        if(mDataChanged){
            int oldCurrentX = mCurrentX;
            initView();
            removeAllViewsInLayout();
            mNextX = oldCurrentX;
            mDataChanged = false;
        }

        if(mScroller.computeScrollOffset()){
            int scrollx = mScroller.getCurrX();
            mNextX = scrollx;
        }

        if(mNextX <= 0){
            mNextX = 0;
            mScroller.forceFinished(true);
        }
        if(mNextX >= mMaxX) {
            mNextX = mMaxX;
            mScroller.forceFinished(true);
        }

        int dx = mCurrentX - mNextX;

        removeNonVisibleItems(dx);
        fillList(dx);
        positionItems(dx);

        mCurrentX = mNextX;

        if(!mScroller.isFinished()){
            post(new Runnable() {

                public void run() {
                    // TODO Auto-generated method stub
                    requestLayout();
                }
            });

        }
    }

    private void fillList(final int dx) {
        int edge = 0;
        View child = getChildAt(getChildCount()-1);
        if(child != null) {
            edge = child.getRight();
        }
        fillListRight(edge, dx);

        edge = 0;
        child = getChildAt(0);
        if(child != null) {
            edge = child.getLeft();
        }
        fillListLeft(edge, dx);


    }

    private void fillListRight(int rightEdge, final int dx) {
        while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {

            View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
            addAndMeasureChild(child, -1);
            rightEdge += child.getMeasuredWidth();

            if(mRightViewIndex == mAdapter.getCount()-1) {
                mMaxX = mCurrentX + rightEdge - getWidth();
            }

            if (mMaxX < 0) {
                mMaxX = 0;
            }
            mRightViewIndex++;
        }

    }

    private void fillListLeft(int leftEdge, final int dx) {
        while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
            View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
            addAndMeasureChild(child, 0);
            leftEdge -= child.getMeasuredWidth();
            mLeftViewIndex--;
            mDisplayOffset -= child.getMeasuredWidth();
        }
    }

    private void removeNonVisibleItems(final int dx) {
        View child = getChildAt(0);
        while(child != null && child.getRight() + dx <= 0) {
            mDisplayOffset += child.getMeasuredWidth();
            mRemovedViewQueue.offer(child);
            removeViewInLayout(child);
            mLeftViewIndex++;
            child = getChildAt(0);

        }

        child = getChildAt(getChildCount()-1);
        while(child != null && child.getLeft() + dx >= getWidth()) {
            mRemovedViewQueue.offer(child);
            removeViewInLayout(child);
            mRightViewIndex--;
            child = getChildAt(getChildCount()-1);
        }
    }

    private void positionItems(final int dx) {
        if(getChildCount() > 0){
            mDisplayOffset += dx;
            int left = mDisplayOffset;
            for(int i=0;i<getChildCount();i++){
                View child = getChildAt(i);
                int childWidth = child.getMeasuredWidth();
                child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
                left += childWidth;
            }
        }
    }

    public synchronized void scrollTo(int x) {
        mScroller.startScroll(mNextX, 0, x - mNextX, 0);
        requestLayout();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = super.dispatchTouchEvent(ev);
        handled |= mGesture.onTouchEvent(ev);
        return handled;
    }

    protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
        synchronized(HorizontalListView.this){
            mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
        }
        requestLayout();

        return true;
    }

    protected boolean onDown(MotionEvent e) {
        mScroller.forceFinished(true);
        return true;
    }

    private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

        @Override
        public boolean onDown(MotionEvent e) {
            return HorizontalListView.this.onDown(e);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
            return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {

            synchronized(HorizontalListView.this){
                mNextX += (int)distanceX;
            }
            requestLayout();

            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            for(int i=0;i<getChildCount();i++){
                View child = getChildAt(i);
                if (isEventWithinView(e, child)) {
                    if(mOnItemClicked != null){
                        mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
                    }
                    if(mOnItemSelected != null){
                        mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
                    }
                    break;
                }

            }
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (isEventWithinView(e, child)) {
                    if (mOnItemLongClicked != null) {
                        mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
                    }
                    break;
                }

            }
        }

        private boolean isEventWithinView(MotionEvent e, View child) {
            Rect viewRect = new Rect();
            int[] childPosition = new int[2];
            child.getLocationOnScreen(childPosition);
            int left = childPosition[0];
            int right = left + child.getWidth();
            int top = childPosition[1];
            int bottom = top + child.getHeight();
            viewRect.set(left, top, right, bottom);
            return viewRect.contains((int) e.getRawX(), (int) e.getRawY());
        }
    };



}

AgarwalActivity.java

public class AgarwalActivity extends Activity{

    private LayoutInflater _inflater;
    private ListView listView;
    private HorizontalListView hListView;
    private RankingsAdapter rankingsAdapter;
    private boolean isHorizontalListView = false;


    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  
            if(isHorizontalListView){
                setContentView(R.layout.listviewdemo);
                hListView = (HorizontalListView) findViewById(R.id.listview);  
            }else{
                setContentView(R.layout.listview);
                listView=(ListView)findViewById(R.id.listView);
            }

        ArrayList<String> rankingArrayList = new ArrayList<String>();
        for(int i=0;i<25;i++)
           rankingArrayList.add("agarwal"+i);

        _inflater= LayoutInflater.from(AgarwalActivity.this);    

        rankingsAdapter = new RankingsAdapter(AgarwalActivity.this,R.id.text,rankingArrayList);
            if(isHorizontalListView)
                hListView.setAdapter(rankingsAdapter);
            else
            listView.setAdapter(rankingsAdapter);

    }


    private class RankingsAdapter extends ArrayAdapter<String>{
        private ArrayList<String> _row_list;

        public static final int HALF_MAX_VALUE = Integer.MAX_VALUE/2;
        public final int MIDDLE;
        public RankingsAdapter(Context context,int textViewResourceId,ArrayList<String> arrL) {
            // TODO Auto-generated constructor stub
            super(context,textViewResourceId, arrL);
            _row_list = arrL;
            MIDDLE = HALF_MAX_VALUE - HALF_MAX_VALUE % _row_list.size();
        }
        public int getCount() {
            // TODO Auto-generated method stub
            return Integer.MAX_VALUE;
        }
        public String getItem(int position) {
            // TODO Auto-generated method stub
            return _row_list.get(position % _row_list.size());
        }
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position % _row_list.size();
        }

        public View getView(final int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder holder = null;

            if(convertView == null){
                holder = new ViewHolder();
                convertView = (LinearLayout)_inflater.inflate(R.layout.text_layout, null);
                //getting the IDs
                holder.driver = (TextView)convertView.findViewById(R.id.text);
                convertView.setTag(holder);
            } else {

                holder=(ViewHolder)convertView.getTag();
            }
            //Settng bean values to row components
            holder.driver.setText(_row_list.get(position % _row_list.size()));

            return convertView;
        }//getView()

        class ViewHolder{
            //holding the components
            TextView driver;
        }//ViewHolder--Class inside CountriesAdapter

    }//RankingsAdapter-inner class

}//AgarwalActivity-class

listview.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

    </ListView>

</RelativeLayout>

listviewdemo.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#fff"
  >

  <com.vl.agarwal.HorizontalListView
    android:id="@+id/listview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#ddd"
  />

</LinearLayout>

text_layout.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
    android:id="@+id/text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:paddingLeft="6dip"
    android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

-------------------------------------------------------------------------------------

Solution2:: Using the gallery as horizontal listview

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >

  <TextView
      android:id="@+id/itemtext"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="20dp"
      android:textColor="@android:color/white"
      android:textStyle="bold"
      android:textSize="30sp"/>

</LinearLayout>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >

  <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/hello" />
  <Gallery
      android:id="@+id/horizontallistview"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content" />

</LinearLayout>

AgarwalActivity.java

public class AgarwalActivity extends Activity {

 Gallery myHorizontalListView;

private RankingsAdapter rankingsAdapter;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
  ArrayList<String> rankingArrayList = new ArrayList<String>();
  for(int i=0;i<25;i++)
     rankingArrayList.add("agarwal"+i);

  DisplayMetrics metrics = new DisplayMetrics();
  this.getWindowManager().getDefaultDisplay().getMetrics(metrics);

  myHorizontalListView = (Gallery)findViewById(R.id.horizontallistview);
  MarginLayoutParams mlp = (MarginLayoutParams) myHorizontalListView.getLayoutParams();
  mlp.setMargins(-(metrics.widthPixels/2), 
                 mlp.topMargin, 
                 mlp.rightMargin, 
                 mlp.bottomMargin
  );


  rankingsAdapter = new RankingsAdapter(AgarwalActivity.this,R.id.itemtext,rankingArrayList);
  myHorizontalListView.setAdapter(rankingsAdapter);

  myHorizontalListView.setSelection(10,true);

      myHorizontalListView.setOnItemClickListener(new OnItemClickListener(){

   @Override
   public void onItemClick(AdapterView<?> parent, View view, int position,
     long id) {
    Toast.makeText(
      AgarwalActivity.this,
      parent.getItemAtPosition(position).toString() + " Clicked",
      Toast.LENGTH_LONG)
      .show();

   }});

  }




    private class RankingsAdapter extends ArrayAdapter<String>{
        private ArrayList<String> _row_list;

        public static final int HALF_MAX_VALUE = Integer.MAX_VALUE/2;
        public final int MIDDLE;
        public RankingsAdapter(Context context,int textViewResourceId,ArrayList<String> arrL) {
            // TODO Auto-generated constructor stub
            super(context,textViewResourceId, arrL);
            _row_list = arrL;
            MIDDLE = HALF_MAX_VALUE - HALF_MAX_VALUE % _row_list.size();
        }
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return Integer.MAX_VALUE;
        }
        @Override
        public String getItem(int position) {
            // TODO Auto-generated method stub
            return _row_list.get(position % _row_list.size());
        }
        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position % _row_list.size();
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            View rowView = LayoutInflater
                     .from(parent.getContext())
                     .inflate(R.layout.row, null);
                   TextView listTextView = (TextView)rowView.findViewById(R.id.itemtext);
                   listTextView.setText(_row_list.get(position % _row_list.size()));

                   return rowView;
        }//getView()
    }//RankingsAdapter-inner class
}//AgarwalActivity-class
Shankar Agarwal
  • 34,573
  • 7
  • 66
  • 64
  • :Please Tell me this for horizontal list view,i tried this logic(Integer.MAXVALUE),in this we cant display first items in left most. – Surej May 28 '12 at 12:18
  • ok,whether u can see last items on scrolling left side at begining? – Surej May 29 '12 at 04:25
  • Me done the same but i faced the issue on setselction,the items is not properly left aligned.please check that – Surej May 29 '12 at 10:12
  • Above updated version,its ok for inifnite gallery of items,but only issue when using setselction method inside itemclick listener the items moved to left with half of the previous items display.Ex:if i clicked 4th items means,the half of the text from 3rd items is diaplying,How to aviod that? – Surej May 29 '12 at 11:21
  • Using this first item will left align,its ok,but my issue is when using setselction inside the onitemclickListener,the items is not correstly left aligned – Surej May 30 '12 at 05:09
  • Not Left Aligning all items,if i clicked right most button,its left aligned,but if i click middle item is not left aligning..Do u know why? – Surej May 30 '12 at 08:44
  • If still you face any issue can ping me in my chat room – Shankar Agarwal May 31 '12 at 02:36
  • Sorry i can't open chat room right now,can u come to chat in gtalk?my id is nrkarthickkumar@gmail.com – Surej May 31 '12 at 05:33
0

What you are describing is sometimes called a circular gallery, a wrapping gallery, or a turnstyle gallery. There are good code examples on this posting:

android circular gallery?

Community
  • 1
  • 1
Jonathan Schneider
  • 26,852
  • 13
  • 75
  • 99
  • :I dont want to add Integer.MAXVALUE items initially,I need to add and remove items on scroll. – Surej May 25 '12 at 14:14
  • You shouldn't ever have to add an infinite number of items (unless I really don't understand this), only the number of items that you wish to display. In this case, {a,b,c,d,e,f} represents only 6 items. In the circular gallery, when you scroll left to a, f will naturally appear to its left. Similarly, when you scroll right to f, a will appear to its right. – Jonathan Schneider May 25 '12 at 21:37