动态设置Listview高度后正确检测Listview中的setOnScrollListener

Posted

技术标签:

【中文标题】动态设置Listview高度后正确检测Listview中的setOnScrollListener【英文标题】:Detect setOnScrollListener in Listview properly after setting Listview height dynamically 【发布时间】:2016-09-12 02:37:49 【问题描述】:

我在 Viewpager 中使用 listview,我需要根据子级设置 ListView 高度,并且当用户滚动到 Listview 的最后一个位置时,我需要添加新项目。但问题是当我动态设置列表视图高度时,它使当前列表视图项目可见(或选中)。这就是自动获取(调用方法获取数据)的原因。 代码如下:

int index = lvNetwork.getFirstVisiblePosition();
    View v = lvNetwork.getChildAt(0);
    int top = (v == null) ? 0 : v.getTop();

    adapter = new NetworkAdapter(activity, R.layout.network_custom_row, networkDataArrayList);
    adapter.notifyDataSetChanged();
    lvNetwork.setAdapter(adapter);
    Utils.setlistViewHeight(lvNetwork, activity);

    lvNetwork.setSelectionFromTop(index, top);

    lvNetwork.setOnScrollListener(new AbsListView.OnScrollListener() 
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) 

        

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 

            int finalItem = firstVisibleItem + visibleItemCount;

            Log.d("dataCalling", "visible " + finalItem);
            Log.d("dataCalling", "total " + totalItemCount);

            if (finalItem == totalItemCount) 

                if (preLast != finalItem) 
                    preLast = finalItem;
                    Log.d("dataCalling", String.valueOf(totalItemCount));
                    Log.d("dataCalling", "Page " + nextid);
                    progressBar.setVisibility(View.VISIBLE);

                      getNetworkFeed();

                
            
        
    );

Utils 中的 setlistviewHeight 方法,

public static void setlistViewHeight(ListView listView, Context context) 

    ListAdapter myListAdapter = listView.getAdapter();

    if (myListAdapter == null) 
        return;
    

    int totalHeight = 0;
    for (int size = 0; size < myListAdapter.getCount(); size++) 

        View listItem = myListAdapter.getView(size, null, listView);
        if (listItem instanceof ViewGroup)
            listItem.setLayoutParams(new RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.WRAP_CONTENT,
                    RelativeLayout.LayoutParams.WRAP_CONTENT));

        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        @SuppressWarnings("deprecation")
        int screenWidth = display.getWidth();
        int listViewWidth = screenWidth - 65;
        int widthSpec = View.MeasureSpec.makeMeasureSpec(listViewWidth,
                View.MeasureSpec.AT_MOST);
        listItem.measure(widthSpec, 0);

        totalHeight += listItem.getMeasuredHeight();
    

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (myListAdapter.getCount()));
    listView.setLayoutParams(params);
    listView.requestLayout();

***如果我不需要动态设置列表视图高度,此代码效果很好。 我应该在这里更改什么以使其正常工作或任何替代解决方案以获得期望的结果?

任何帮助将不胜感激。

【问题讨论】:

你能把代码贴在 Utils.setlistViewHeight(lvNetwork, activity); ? 已发,请查收。 日志是否正确? visibleItemCount 的值正确吗? 为什么要动态设置ListView的高度? 因为我需要在滚动视图中使用它和其他一些布局元素。 【参考方案1】:

但问题是当我动态设置列表视图高度时,它使当前列表视图项可见

动态设置高度不是问题。相反,问题是您通过计算列表中每个项目的高度将列表视图的高度设置为列表视图的最大可能高度。所以将会发生的是列表视图的所有项目将立即填充并在列表中保持膨胀。(注意:现在不会发生视图回收)

这就是为什么自动获取(调用方法获取数据)

调用发生是因为您正在根据列表中的项目总数设置列表视图的高度。因此发生的事情是,列表视图中的所有元素在任何给定时间点都将处于可见状态。这意味着你的状况

if (finalItem == totalItemCount)

将始终为真,因为您的 visibleItemCount 将始终为 totalItemCount,这使您的最终项目始终等于 totalItemCount。 (您可以通过调试您的应用来验证这一点)。

我应该在此处更改什么以使其正常工作或任何替代解决方案以获得期望的结果?

我能想到的最佳解决方案是当且仅当您根据所有项目的高度计算的总高度小于屏幕高度时才设置列表视图的高度。否则,将列表视图的高度设置为MATCH_PARENT

ViewGroup.LayoutParams params = listView.getLayoutParams();
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        int height = size.y;
        if(totalHeight > height)
            params.height = ViewGroup.LayoutParams.MATCH_PARENT;
        else 
            Log.d("", "");
            params.height = totalHeight
                    + (listView.getDividerHeight() * (myListAdapter.getCount()));
        

因此,此代码将阻止使列表视图的所有视图变得可见,因此您将收到 onScroll visibleItemCount,当前可见的项目数。

【讨论】:

在任何时间点填充的所有项目都没有意义时使用ListView。 工作但在其他地方列表视图只显示一行。你能帮我多一点吗?【参考方案2】:

Ankit 已经向您解释了您的代码有什么问题,让我与您分享一个替代解决方案。

当您已经在填充列表视图的项目时使用它没有好处,最好使用滚动视图并动态添加项目。 Scrollview 没有滚动监听器,因此我们对其进行了定制。

MyScrollView.Java

public class MyScrollView extends ScrollView 

public interface OnScrollListener 
    void onScrollChanged(int l, int t, int oldl, int oldt);


private OnScrollListener onScrollListener;

public OnScrollListener getOnScrollListener() 
    return onScrollListener;


public void setOnScrollListener(OnScrollListener onScrollListener) 
    this.onScrollListener = onScrollListener;


public MyScrollView(Context context) 
    super(context);


public MyScrollView(Context context, AttributeSet attrs) 
    super(context, attrs);


public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) 
    super(context, attrs, defStyleAttr);


@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) 
    super.onScrollChanged(l, t, oldl, oldt);

    if (onScrollListener != null) 
        onScrollListener.onScrollChanged(l, t, oldl, oldt);
    


我们在这样的活动中使用滚动监听器 -

import android.graphics.Rect;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

public class NewScrollActivity extends AppCompatActivity 

private MyScrollView scrollView;
private LinearLayout container;
private ProgressBar progressBar;
int maxItem = 20;
private View lastItemView;
boolean alreadyExecutingRequest = false;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_new_scroll);

    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    scrollView = (MyScrollView) findViewById(R.id.scrollView);
    scrollView.setOnScrollListener(scrollListener);
    container = (LinearLayout) findViewById(R.id.container);

    addItemsAsynchronously();


private MyScrollView.OnScrollListener scrollListener = new    MyScrollView.OnScrollListener() 
    @Override
    public void onScrollChanged(int l, int t, int oldl, int oldt) 

        if (lastItemView != null && !alreadyExecutingRequest) 
            Rect scrollBounds = new Rect();
            scrollView.getHitRect(scrollBounds);
            if (lastItemView.getLocalVisibleRect(scrollBounds)) 
                // Any portion of the lastitem view, even a single pixel, is within the visible window
                addItemsAsynchronously();
            
        
    
;

private void addItemsAsynchronously() 

    new AsyncTask<Void,Void,Void>() 

        @Override
        protected void onPreExecute() 
            super.onPreExecute();
            progressBar.setVisibility(View.VISIBLE);
            alreadyExecutingRequest = true;
        

        @Override
        protected Void doInBackground(Void... voids) 
            try 
                Thread.sleep(3000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            return null;
        

        @Override
        protected void onPostExecute(Void aVoid) 
            super.onPostExecute(aVoid);

            progressBar.setVisibility(View.GONE);
            addItemsToContainer();
            alreadyExecutingRequest = false;
        


    .execute();


private void addItemsToContainer() 
    int lastAddedItem = container.getChildCount();
    for (int i=lastAddedItem;i<maxItem;i++) 
        View view = LayoutInflater.from(this).inflate(R.layout.new_item, null);
        TextView textView = (TextView) view.findViewById(R.id.textView);
        textView.setText("Item - " + i);

        container.addView(view);
    
    lastItemView = container.getChildAt(container.getChildCount() -1);
    maxItem+=10;


这里我们检查了与滚动视图边界绑定的最后一个项目,因此视图是可见的,然后我们在底部,所以添加更多项目。

activity_new_scroll.xml

<?xml version="1.0" encoding="utf-8"?>
<com.sj.textinputlayout.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/scrollView"
    android:layout_
    android:layout_
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.sj.textinputlayout.NewScrollActivity">

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/container"
            android:layout_
            android:layout_
            android:orientation="vertical" />

        <ProgressBar android:id="@+id/progressBar"
            android:layout_
            android:layout_
            android:visibility="gone"
            android:layout_gravity="center_horizontal"/>
    </LinearLayout>

</com.sj.textinputlayout.MyScrollView>

new_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_
    android:padding="10dp"
    android:layout_>

    <TextView android:id="@+id/textView"
        android:layout_
        android:layout_ />
</LinearLayout>

【讨论】:

以上是关于动态设置Listview高度后正确检测Listview中的setOnScrollListener的主要内容,如果未能解决你的问题,请参考以下文章

QML ListView:检测到属性“高度”的绑定循环

android动态设置listview高度

动态检测 UIScrollView 的高度

动态更改ListView ItemsPanel高度

如何正确设置列表视图的高度

在ListView中动态更新列表元素高度