Android list view inside a scroll view

后端 未结 30 1926
一向
一向 2020-11-21 13:43

I have an android layout which has a scrollView with a number of elements with in it. At the bottom of the scrollView I have a listView

相关标签:
30条回答
  • 2020-11-21 13:43

    As others had already mentioned, don't use ListView inside a ScrollView.

    To workaround, you can use a LinearLayout, but to still keep things neat - populate your LinearLayout with an Adapter, same as you do with a ListView

    You can use this class as a LinearLayout replacement that supports Adapters

    import android.content.Context;
    import android.database.DataSetObserver;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.LinearLayout;
    
    public class AdaptableLinearLayout extends LinearLayout {
    
    private BaseAdapter mAdapter;
    
    private int mItemCount = 0;
    
    private boolean mDisableChildrenWhenDisabled = false;
    
    private int mWidthMeasureSpec;
    private int mHeightMeasureSpec;
    
    
    public AdaptableLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    
    public BaseAdapter getAdapter() {
        return mAdapter;
    }
    
    public void setAdapter(BaseAdapter adapter) {
        mAdapter = adapter;
        adapter.registerDataSetObserver(new DataSetObserver() {
            @Override
            public void onChanged() {
                updateLayout();
                super.onChanged();
            }
    
            @Override
            public void onInvalidated() {
                updateLayout();
                super.onInvalidated();
            }
        });
        updateLayout();
    }
    
    private void updateLayout() {
        mItemCount = mAdapter.getCount();
        requestLayout();
        invalidate();
    }
    
    /**
     * set size for the current View
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidthMeasureSpec = widthMeasureSpec;
        mHeightMeasureSpec = heightMeasureSpec;
    
        removeAllViewsInLayout();
        for (int i = 0; i < mItemCount; i++) {
            makeAndAddView(i);
        }
    }
    
    private View makeAndAddView(int position) {
        View child;
    
        // Nothing found in the recycler -- ask the adapter for a view
        child = mAdapter.getView(position, null, this);
    
        // Position the view
        setUpChild(child, position);
    
        return child;
    
    }
    
    private void setUpChild(View child, int position) {
    
        ViewGroup.LayoutParams lp = child.getLayoutParams();
        if (lp == null) {
            lp = generateDefaultLayoutParams();
        }
        addViewInLayout(child, position, lp);
    
        // Get measure specs
        int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height);
        int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width);
    
        // Measure child
        child.measure(childWidthSpec, childHeightSpec);
    
        int childLeft;
        int childRight;
    
        // Position vertically based on gravity setting
        int childTop = getPaddingTop() + ((getMeasuredHeight() - getPaddingBottom() - getPaddingTop() - child.getMeasuredHeight()) / 2);
        int childBottom = childTop + child.getMeasuredHeight();
    
        int width = child.getMeasuredWidth();
        childLeft = 0;
        childRight = childLeft + width;
    
        child.layout(childLeft, childTop, childRight, childBottom);
    
        if (mDisableChildrenWhenDisabled) {
            child.setEnabled(isEnabled());
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-21 13:43

    You can put all into linear layout. That is, create linear layout and it will have 2 childs, scrollview and another linear layout. Give them layout weights and here you go :

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="0dip" android:layout_weight="0.8">
    
        <LinearLayout
            android:id="@+id/seTaskActivityRoot"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"
            android:orientation="vertical" >
    
            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/taskName" />
    
    
            <Spinner
                android:id="@+id/seTaskPrioritiesSP"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/textView4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/taskTargetInNumeric" />
    
            <Spinner
                android:id="@+id/seTaskUnitsSP"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/textView6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/newTaskCurrentStatus" />
    
            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:hint="@string/addTaskCurrentStatus"
                android:inputType="numberDecimal" />
    
    
        </LinearLayout>
    </ScrollView>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:orientation="vertical" android:layout_weight="0.2">
    
        <TextView
            android:id="@+id/textView8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />
    
        <ListView
            android:id="@+id/logList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </ListView>
    
    </LinearLayout>
    

    0 讨论(0)
  • 2020-11-21 13:43

    You should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView to display its entire list of items to fill up the infinite container supplied by ScrollView.

    http://developer.android.com/reference/android/widget/ScrollView.html

    0 讨论(0)
  • 2020-11-21 13:43

    Ok, here 's my answer. The method that fixes the ListView height is closed enough, but not perfect. In case that most of the items are the same height, that work well. But in case that's not, then there's a big problem. I've tried many time, and when I put out the value of listItem.getMeasureHeight and listItem.getMeasuerWidth into the log, I saw the width values vary a lot, which is not expected here, since all the item in the same ListView should have the same width. And there go the bug :

    Some used measure(0 ,0), which actually made the view unbound, in both direction, and width run wild. Some tried to getWidth of listView, but then it return 0, meaningless.

    When I read further into how android render the View, I realize that all of this attempt can't reach the answer that I searched for, unless these function run after the view is render.

    This time I use the getViewTreeObserver on the ListView that I want to fix height, then addOnGlobalLayoutListener. Inside this method, I declare a new OnGlobalLayoutListener, in which, this time, getWidth return the actual width of the ListView.

    private void getLayoutWidth(final ListView lv, final int pad){
            //final ArrayList<Integer> width = new ArrayList<Integer>();
    
            ViewTreeObserver vto = lv.getViewTreeObserver();
            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    lv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    //width.add(layout.getMeasuredWidth());
                    int width = lv.getMeasuredWidth();
                    ListUtils.setDynamicHeight(lv, width, pad);
                }
            });
        }
    
    public static class ListUtils {
            //private static final int UNBOUNDED = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            public static void setDynamicHeight(ListView mListView, int width, int pad) {
                ListAdapter mListAdapter = mListView.getAdapter();
                mListView.getParent();
                if (mListAdapter == null) {
                    // when adapter is null
                    return;
                }
                int height = 0;
    
    
                int desiredWidth = View.MeasureSpec.makeMeasureSpec(width - 2*pad, View.MeasureSpec.EXACTLY);
                for (int i = 0; i < mListAdapter.getCount(); i++) {
                    View listItem = mListAdapter.getView(i, null, mListView);
    
                    listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
                    //listItem.measure(UNBOUNDED, UNBOUNDED);
                    height += listItem.getMeasuredHeight() + 2*pad;
                    Log.v("ViewHeight :", mListAdapter.getClass().toString() + " " + listItem.getMeasuredHeight() + "--" + listItem.getMeasuredWidth());
                }
                ViewGroup.LayoutParams params = mListView.getLayoutParams();
                params.height = height + (mListView.getDividerHeight() * (mListAdapter.getCount() - 1));
                mListView.setLayoutParams(params);
                mListView.requestLayout();
            }
        }
    

    The value pad, is the padding that I set in ListView layout.

    0 讨论(0)
  • 2020-11-21 13:44

    best Code

     <android.support.v4.widget.NestedScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/btmlyt"
        android:layout_below="@+id/deshead_tv">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
           android:orientation="vertical"
           >
    
        <TextView
            android:id="@+id/des_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/btmlyt"
            android:background="@android:color/white"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:scrollbars="vertical"
            android:paddingTop="3dp"
            android:text="description"
            android:textColor="@android:color/black"
            android:textSize="18sp" />
        </LinearLayout>
    
    </android.support.v4.widget.NestedScrollView>
    
    0 讨论(0)
  • 2020-11-21 13:45

    Done after lots of R&D:

    fragment_one.xml should looks like:

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/scrollViewParent"
        android:orientation="vertical" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="400dip" >
    
                <ListView
                    android:id="@+id/listView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
    
                <View
                    android:id="@+id/customView"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:background="@android:color/transparent" />
            </RelativeLayout>
    
            <!-- Your other elements are here -->
    
        </LinearLayout>
    
    </ScrollView>
    

    Your Java class of FragmentOne.java looks like:

    private ListView listView;
    private View customView
    

    onCreateView

    listView = (ListView) rootView.findViewById(R.id.listView);
    scrollViewParent = (ScrollView)rootView.findViewById(R.id.scrollViewParent);
    customView = (View)rootView.findViewById(R.id.customView);
    
    customView.setOnTouchListener(new View.OnTouchListener() {
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int action = event.getAction();
                    switch (action) {
                        case MotionEvent.ACTION_DOWN:
                            // Disallow ScrollView to intercept touch events.
                            scrollViewParent.requestDisallowInterceptTouchEvent(true);
                            // Disable touch on transparent view
                            return false;
    
                        case MotionEvent.ACTION_UP:
                            // Allow ScrollView to intercept touch events.
                            scrollViewParent.requestDisallowInterceptTouchEvent(false);
                            return true;
    
                        case MotionEvent.ACTION_MOVE:
                            scrollViewParent.requestDisallowInterceptTouchEvent(true);
                            return false;
    
                        default:
                            return true;
                    }
                }
            });
    
    0 讨论(0)
提交回复
热议问题