Android. Scrolling 2 listviews together

前端 未结 6 1553
情深已故
情深已故 2020-11-28 05:13

OK. What I\'m trying to achieve is a layout that does the same effect as frozen panes in Excel. That is I want a header row that scrolls horizontally with the main ListView

6条回答
  •  旧巷少年郎
    2020-11-28 06:02

    Rewrite

    I didn't have much luck with passing the scrolling actions in one ListView to another. So I chose a different method: passing the MotionEvent. This lets each ListView calculate their own smooth scroll, fast scroll, or anything else.

    First, we'll need some class variables:

    ListView listView;
    ListView listView2;
    
    View clickSource;
    View touchSource;
    
    int offset = 0;
    

    Every method that I add to listView will be almost identical for listView2, the only difference is that listView2 will reference listView (not itself). I didn't include the repetitive listView2 code.

    Second, let's start with the OnTouchListener:

    listView = (ListView) findViewById(R.id.engNameList);
    listView.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(touchSource == null)
                touchSource = v;
    
            if(v == touchSource) {
                listView2.dispatchTouchEvent(event);
                if(event.getAction() == MotionEvent.ACTION_UP) {
                    clickSource = v;
                    touchSource = null;
                }
            }
    
            return false;
        }
    });
    

    To prevent circular logic: listView calls listView2 calls listView calls... I used a class variable touchSource to determine when a MotionEvent should be passed. I assumed that you don't want a row click in listView to also click in listView2, so I used another class variable clickSource to prevent this.

    Third, the OnItemClickListener:

    listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            if(parent == clickSource) {
                // Do something with the ListView was clicked
            }
        }
    });
    

    Fourth, passing every touch event isn't perfect because occasional discrepancies appear. The OnScrollListener is perfect for eliminating these:

    listView.setOnScrollListener(new OnScrollListener() {
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if(view == clickSource) 
                listView2.setSelectionFromTop(firstVisibleItem, view.getChildAt(0).getTop() + offset);
        }
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {}
    });
    

    (Optional) Lastly, you mentioned that you have trouble since listView and listView2 begin at different heights in your layout... I highly recommend modifying your layout to balance the ListViews, but I found a way to address this. However it is a little tricky.
    You cannot calculate the difference in height between the two layouts until after the entire layout have been rendered, but there is no callback for this moment... so I use a simple handler:

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // Set listView's x, y coordinates in loc[0], loc[1]
            int[] loc = new int[2];
            listView.getLocationInWindow(loc);
    
            // Save listView's y and get listView2's coordinates
            int firstY = loc[1];
            listView2.getLocationInWindow(loc);
    
            offset = firstY - loc[1];
            //Log.v("Example", "offset: " + offset + " = " + firstY + " + " + loc[1]);
        }
    };
    

    I assume that a half second delay is long enough to render the layout and start the timer in onResume():

    handler.sendEmptyMessageDelayed(0, 500);
    

    If you do use an offset I want to be clear that listView2's OnScroll method subtracts the offset rather than adds it:

    listView2.setSelectionFromTop(firstVisibleItem, view.getChildAt(0).getTop() - offset);
    

    Hope that helps!

提交回复
热议问题