6

In my application I have a ViewPager with each one a custom ListView in it linked toghether like row in a table. The problem is that when I scroll a ListView in the first page and then I scroll the ViewPager the ListView of the first page and the ListView of the second page are misaligned. The listView must be aligned because in the first one i have a TextView with a description and then 4 coloumns with datas and in the next ListViews 6 coloumns without the description so if they are misaligned it's very complicated. So how can I scroll all the listViews at once ??

EDIT:

Thank you very much to Rahul Parsani... I've finally managed how to do this... If you have the listView in separate fragment and the number of the fragment is undefined this is the answer:

final ArrayList<ListView> lvs = new ArrayList<ListView>();
        for (int j = 0; j < adapter.getCount(); j++) {
            YourFragment fragJ = (YourFragment) adapter.getItem(j);
            final ListView listViewJ = fragJ.lv;
            lvs.add(listViewJ);
        }

        for (int j = 0; j < lvs.size(); j++) {
            final int x = j;
            lvs.get(j).setOnTouchListener(new OnTouchListener() {

                boolean dispatched = false;
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (!dispatched) {
                        dispatched = true;
                        if (x != 0) {
                            lvs.get(x - 1).dispatchTouchEvent(event);
                        };
                        if (x != lvs.size() - 1) {
                            lvs.get(x + 1).dispatchTouchEvent(event);
                        }

                    }
                    dispatched = false;
                    return false;
                }
            });
        }
Matteo Cardellini
  • 876
  • 2
  • 17
  • 41

3 Answers3

5

What you are trying to do is synchronize the listviews. There is a library that already implements this for two listviews here. I will try to explain the part of the source code relevant to you which is located in the activity here.

Suppose if you have access to all your listviews through variables:

listViewOne = (ListView) findViewById(R.id.list_view_one);
listViewTwo = (ListView) findViewById(R.id.list_view_two);
listViewThree = (ListView) findViewById(R.id.list_view_three);
listViewFour = (ListView) findViewById(R.id.list_view_four);

Set the same touch listeners on them:

listViewOne.setOnTouchListener(touchListener);
listViewTwo.setOnTouchListener(touchListener);
// similarly for listViewThree & listViewFour

The basic idea of the touch listener is to dispatch events to the other views so that they also scroll synchronously with the listview being touched.

// Passing the touch event to the opposite list
OnTouchListener touchListener = new OnTouchListener() {                                        
    boolean dispatched = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (v.equals(listViewOne) && !dispatched) {
            dispatched = true;
            listViewTwo.dispatchTouchEvent(event);
            listViewThree.dispatchTouchEvent(event);
            listViewFour.dispatchTouchEvent(event);
        } else if (v.equals(listViewTwo) && !dispatched) {
            dispatched = true;
            listViewOne.dispatchTouchEvent(event);
            listViewThree.dispatchTouchEvent(event);
            listViewFour.dispatchTouchEvent(event);
         } // similarly for listViewThree & listViewFour 
         dispatched = false;
         return false;
    }
};

The library also has set a scroll listener, which I believe is used because the views may be of differing heights, which you don't have. Try and let me know if this works.

rarp
  • 1,122
  • 10
  • 20
  • First of all Thank you for your reply... The code seems very easy but the problem is that I don't have all the ListView declared.. They are each one in a fragment called LineSchedule_Fragment extends Fragment... How I can extract all the ListView from that fragment and then call dispatchTouchEvent(event) on each one ? – Matteo Cardellini Nov 14 '13 at 18:01
  • 1
    In each fragment you can define the touch listener, and set it on the listview. Have a public method in each fragment that takes one parameter, MotionEvent, and that just calls dispatchTouchEvent(MotionEvent). Then you need references for all your fragments, which you can get from your pager adapter. – rarp Nov 15 '13 at 02:26
  • 1
    Or you can get a reference via interface that the Activity will implement that will get called once a touch is performed. like: `public void onTouch(int fragmentNumber, MotionEvent);` – user2558461 Nov 18 '13 at 06:21
  • @RahulParsani You can't get reference of `Fragment` from `PagerAdapter` without some hack: http://stackoverflow.com/questions/10849552/android-viewpager-cant-update-dynamically/17855730#17855730 – M-Wajeeh Nov 20 '13 at 06:41
  • Check my answer below for a trick that removes the need to keep a reference to yourself. – M-Wajeeh Nov 20 '13 at 06:50
  • 1
    Thank you very much I finally made it. See my edit !!! (P.S: Sorry for the bounty but I wasn't in town and I couldn't even try) – Matteo Cardellini Nov 29 '13 at 00:59
4

The problem you are having is similar to this one Update ViewPager dynamically?. It has nothing to do with setting onTouchListener because you don't have access to all ListViews inside ViewPager.

Following is a working and tested code. Key point is getItemPosition() method in PagerAdapter.

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.ListFragment;
import android.support.v4.view.ViewPager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends FragmentActivity implements ScrollListener {
private MyPagerAdapter mPagerAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    MyPagerAdapter pagerAdapter = mPagerAdapter = new MyPagerAdapter(
            getSupportFragmentManager(), this);
    pagerAdapter.addFragmentInfo(DummyFragment.class.getName(), "First",
            new Bundle());
    pagerAdapter.addFragmentInfo(DummyFragment.class.getName(), "Second",
            new Bundle());
    pagerAdapter.addFragmentInfo(DummyFragment.class.getName(), "Third",
            new Bundle());

    final ViewPager pager = (ViewPager) findViewById(R.id.pager);
    pager.setOffscreenPageLimit(2);
    pager.setAdapter(pagerAdapter);
}

@Override
public void onScrollChanged(int index) {
    mPagerAdapter.setIndex(index);
}

@Override
public int getCurrentScrollIndex() {
    return mPagerAdapter == null ? 0 : mPagerAdapter
            .getCurrentScrollIndex();
}

public static class MyPagerAdapter extends FragmentStatePagerAdapter {

    public static class FragmentInfo {

        public FragmentInfo(String fragmentName, String title, Bundle args) {
            super();
            this.fragmentName = fragmentName;
            this.title = title;
            this.args = args;
        }

        String fragmentName;
        String title;
        Bundle args;
    }

    private List<FragmentInfo> mFragmentInfos = new ArrayList<FragmentInfo>();
    private final Context mContext;
    private int mIndex;

    public MyPagerAdapter(FragmentManager fm, Context context) {
        super(fm);

        mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        FragmentInfo info = mFragmentInfos.get(position);
        Fragment fragment = Fragment.instantiate(mContext,
                info.fragmentName, info.args);
        return fragment;
    }

    @Override
    public int getCount() {
        return mFragmentInfos.size();
    }

    @Override
    public int getItemPosition(Object object) {
        if (object instanceof ScrollRefreshable) {
            ((ScrollRefreshable) object).refreshScroll(mIndex);
        }
        return super.getItemPosition(object);
    }

    public void addFragmentInfo(String fragmentName, String title,
            Bundle args) {
        mFragmentInfos.add(new FragmentInfo(fragmentName, title, args));
    }

    public void setIndex(int index) {
        mIndex = index;
        notifyDataSetChanged();
    }

    public int getCurrentScrollIndex() {
        return mIndex;
    }
}


public static class DummyFragment extends ListFragment implements
        ScrollRefreshable {
    private ScrollListener mRefreshListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (activity instanceof ScrollListener) {
            mRefreshListener = (ScrollListener) activity;
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        String[] values = new String[] { "Android", "iPhone",
                "WindowsMobile", "Blackberry", "WebOS", "Ubuntu",
                "Windows7", "Max OS X", "Linux", "OS/2", "Android2",
                "iPhone2", "WindowsMobile2", "Blackberry2", "WebOS2",
                "Ubuntu2", "Windows72", "Max OS X2", "Linux2", "OS/22" };
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                getActivity(), android.R.layout.simple_list_item_1, values);
        setListAdapter(adapter);
        getListView().setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view,
                    int scrollState) {
                if (scrollState == SCROLL_STATE_IDLE) {
                    if (mRefreshListener != null) {
                        mRefreshListener.onScrollChanged(view
                                .getFirstVisiblePosition());
                    }
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
            }
        });
        // update scroll position at start
        if (mRefreshListener != null) {
            int index = mRefreshListener.getCurrentScrollIndex();
            scrollListViewTo(index);
        }
    }

    @Override
    public void refreshScroll(int index) {
        scrollListViewTo(index);
    }

    private void scrollListViewTo(int index) {
        ListView lv = getListView();
        if (lv != null) {
            lv.setSelection(index);
        }
    }
}
}

interface ScrollRefreshable {
public void refreshScroll(int index);
}

interface ScrollListener {
public void onScrollChanged(int index);

public int getCurrentScrollIndex();
}
Community
  • 1
  • 1
M-Wajeeh
  • 17,204
  • 10
  • 66
  • 103
  • ThankYou for your reply !! A couple of questions... What is Scroll Refrashable and where I set the Scroll Listener ? Can you divide the code in activities ? – Matteo Cardellini Nov 21 '13 at 18:17
  • @SmileApplications You just copy and paste the code and it works. All you need is xml layout `activity_main.xml` with a `ViewPager` with an id `pager`. Have you tried the code? `ScrollRefreshable` is a custom interface defined at the end of file. I have set it for you. I don't understand what you meant by "Can you divide the code in activities ?" – M-Wajeeh Nov 21 '13 at 19:47
  • I can't copy and paste the code because I have houndreds lines of code in each .java so I have to adapt it... My code is divided in: LineSchedule.java that is the FragmentActivity, LineSchedule_Fragment.java that is the fragment manager of each adapter, Adapter_Scroll.java that is the adapter of the ViewPager and Adapter_LineSchedule.java that is the adapter of the CustomListViews inside the ViewPager... So I don't really understand how divide your code to adapt my use – Matteo Cardellini Nov 21 '13 at 22:17
  • 1
    Well, I can't help you more than that. I have posted a simple sample code that you can use to understand how things work by creating another simple project and copy pasting above code. After understanding the code you can modify it the way you want. If you can't do it then I am sorry. – M-Wajeeh Nov 22 '13 at 04:51
-1

Are the listview item heights equal? if so, you could pass the position of the top visible element to the other listview adapters.

 int curTop=listview.getFirstVisiblePosition();

 listview2.setSelection(curTop);