Android set View.GONE does not “release” space in listview

前端 未结 7 532
[愿得一人]
[愿得一人] 2021-01-18 11:01

I have a listview with some items that can be marked as \"done\". There is also a togglebutton that says \"hide done items\".

However, when I do hide the items by s

相关标签:
7条回答
  • 2021-01-18 11:29

    After checking many solutions none of which was solved my issue with the empty space so I decided to came up with my solution.

    I had two main issues: 1) I had an empty space because of the view that I set its visibility to gone 2) I had also dividerHeight of 12dp, even if I had the first issue solved I still had the fixed divider height of the listview

    Solution:

    1.1) I added a boolean to the data of the list, to notify the adapter which of the items are skipped

    1.2) I created an empty layout to simulate an "skipped item"

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

    1.3) I have several types of views in my listview, selected item, regular item and now skipped item

        public class AdvancedTestAdapter extends BaseAdapter
    {
    
        private static final int        REGULAR_STEP    = 0;
        private static final int        SELECTED_STEP   = 1;
        private static final int        SKIPPED_STEP    = 2;
    
        private static final int        TYPE_MAX_COUNT  = 3;
    
    
        private List<AdvancedTestData>  _data;
        private Context                 _context;
        private Typeface                _fontTypeFace;
    
        public AdvancedTestAdapter(Context context, List<AdvancedTestData> data)
        {
            _context = context;
            _data = data;
            _fontTypeFace = Typeface.createFromAsset(_context.getResources().getAssets(), Consts.Fonts.UniversLTStdBoldCn);
        }
    
        @Override
        public AdvancedTestData getItem(int position)
        {
            return _data.get(position);
        }
    
        @Override
        public int getCount()
        {
            return _data.size();
        }
    
        @Override
        public long getItemId(int position)
        {
            return 0;
        }
    
        @Override
        public int getItemViewType(int position)
        {
            AdvancedTestData step = getItem(position);
            if(step.isSkipped())
            {
                return SKIPPED_STEP;
            }
            return _data.get(position).isStepSelected() ? SELECTED_STEP : REGULAR_STEP;
        }
    
        @Override
        public int getViewTypeCount()
        {
            return TYPE_MAX_COUNT;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            RegularViewHolder regHolder;
            SelectedViewHolder selectHolder;
    
            AdvancedTestData item = getItem(position);
            int currentStepType = getItemViewType(position);
    
            switch (currentStepType)
            {
                case SKIPPED_STEP:
                    convertView = LayoutInflater.from(_context).inflate(R.layout.skipped_item_layout, parent, false);
                    break;
                case REGULAR_STEP:
                    if (convertView == null)
                    {
                        regHolder = new RegularViewHolder();
                        convertView = LayoutInflater.from(_context).inflate(R.layout.advanced_test_layout, parent, false);
                        regHolder._regTestUpperHeader = (TextView) convertView.findViewById(R.id.advanced_test_upper_name);
                        regHolder._regTestLowerHeader = (TextView) convertView.findViewById(R.id.advanced_test_lower_name);
                        regHolder._regTestImage = (ImageView) convertView.findViewById(R.id.advanced_test_image);
                        regHolder._regTestWithoutLowerHeader = (TextView) convertView.findViewById(R.id.step_without_lower_header);
    
                        regHolder._regTestUpperHeader.setTypeface(_fontTypeFace);
                        regHolder._regTestLowerHeader.setTypeface(_fontTypeFace);
                        regHolder._regTestWithoutLowerHeader.setTypeface(_fontTypeFace);
    
                        convertView.setTag(regHolder);
                    }
                    else
                    {
                        regHolder = (RegularViewHolder) convertView.getTag();
                    }
    
                    String upperHeader = item.getTestUpperHeader();
                    String lowerHeader = item.getTestLowerHeader();
    
                    if(lowerHeader.isEmpty())
                    {
                        regHolder._regTestUpperHeader.setVisibility(View.GONE);
                        regHolder._regTestLowerHeader.setVisibility(View.GONE);
                        regHolder._regTestWithoutLowerHeader.setVisibility(View.VISIBLE);
                        regHolder._regTestWithoutLowerHeader.setText(upperHeader);
                    }
                    else
                    {
                        regHolder._regTestUpperHeader.setVisibility(View.VISIBLE);
                        regHolder._regTestLowerHeader.setVisibility(View.VISIBLE);
                        regHolder._regTestWithoutLowerHeader.setVisibility(View.GONE);
    
                        regHolder._regTestUpperHeader.setText(upperHeader);
                        regHolder._regTestLowerHeader.setText(lowerHeader);
                    }
    
                    regHolder._regTestImage.setBackgroundResource(item.getResourceId());
                    break;
    
                case SELECTED_STEP:
    
                    if (convertView == null)
                    {
                        selectHolder = new SelectedViewHolder();
                        convertView = LayoutInflater.from(_context).inflate(R.layout.advanced_selected_step_layout, parent, false);
    
                        selectHolder._selectedTestName = (TextView) convertView.findViewById(R.id.selected_header_text);
                        selectHolder._selectedTestDesc = (TextView) convertView.findViewById(R.id.selected_desc_text);
                        selectHolder._selectedPreFinishControllers = (RelativeLayout) convertView.findViewById(R.id.prefinish_step_controllers);
                        selectHolder._selectedFvEndControllers = (RelativeLayout) convertView.findViewById(R.id.advanced_fv_controllers);
                        selectHolder._selectedNvEndControllers = (RelativeLayout) convertView.findViewById(R.id.advanced_nv_controllers);
    
                        convertView.setTag(selectHolder);
                    }
                    else
                    {
                        selectHolder = (SelectedViewHolder) convertView.getTag();
                    }
    
                    selectHolder._selectedPreFinishControllers.setVisibility(View.INVISIBLE);
                    selectHolder._selectedFvEndControllers.setVisibility(View.INVISIBLE);
                    selectHolder._selectedNvEndControllers.setVisibility(View.INVISIBLE);
    
    
                    int testIndex = item.getTestIndex();
                    ADVANCED_QUICK_TEST_TESPS currentStep = ADVANCED_QUICK_TEST_TESPS.valueOf(testIndex);
    
                    //show action buttons in each step in advanced mode
                    switch (currentStep)
                    {
                        case QUESTIONS://nothing to show
                            break;
                        case RIGHT_VERIFICATION:
                        case LEFT_VERIFICATION:
                        case BINOCULAR_BALANCE:
                        case SPHERE_VERIFICATION:
                        case ADD_VERIFICATION:
                            if(item.isStepPreFinished())
                            {
                                selectHolder._selectedPreFinishControllers.setVisibility(View.VISIBLE);
                            }
    
                            break;
    
                        case RIGHT_VA:
                        case LEFT_VA:
                        case BINO_VA:
                            selectHolder._selectedPreFinishControllers.setVisibility(View.VISIBLE);
                            break;
                        case FV_DONE:
                            selectHolder._selectedFvEndControllers.setVisibility(View.VISIBLE);
                            break;
                        case FULL_EXAM_DONE:
                            selectHolder._selectedNvEndControllers.setVisibility(View.VISIBLE);
                            break;
                    }
    
                    String textHeader = String.format("%s\n%s", item.getTestUpperHeader(),item.getTestLowerHeader());
                    selectHolder._selectedTestName.setText(textHeader);
                    selectHolder._selectedTestDesc.setText(item.getTestDescription());
    
                    break;
            }
    
            return convertView;
        }
    
        public void setData(List<AdvancedTestData> data)
        {
            _data = data;
            notifyDataSetChanged();
        }
    
        public static class RegularViewHolder
        {
            public TextView     _regTestWithoutLowerHeader;
            public TextView     _regTestUpperHeader;
            public TextView     _regTestLowerHeader;
            public ImageView    _regTestImage;
        }
    
        public static class SelectedViewHolder
        {
            public TextView         _selectedTestName;
            public TextView         _selectedTestDesc;
            public RelativeLayout   _selectedPreFinishControllers;
            public RelativeLayout   _selectedFvEndControllers;
            public RelativeLayout   _selectedNvEndControllers;
        }
    

    only if the item is skipped the adapter inflate to an empty layout as shown on previous step, still I had the divider height issue

    2) To fix divider height I changed the divider height to 0 instead of 12dp, each item that is not skipped I added another layout with transparent background (the divier color in my case should be transparent) and added bottom padding of 12dp

    for example one of my items

        <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:orientation="vertical"
        android:paddingBottom="12dp" >
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/quick_test_background_selector" >
    
            <ImageView
                android:id="@+id/advanced_test_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/done_step" />
    
            <TextView
                android:id="@+id/advanced_test_upper_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="5dp"
                android:layout_toRightOf="@id/advanced_test_image"
                android:gravity="center_vertical"
                android:text="ETAPE 1"
                android:textColor="@android:color/black"
                android:textSize="14sp"
                android:textStyle="bold" />
    
            <TextView
                android:id="@+id/advanced_test_lower_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/advanced_test_image"
                android:layout_marginLeft="5dp"
                android:layout_toRightOf="@id/advanced_test_image"
                android:gravity="center_vertical"
                android:text="ETAPE 1"
                android:textColor="@android:color/black"
                android:textSize="14sp"
                android:textStyle="bold" />
    
            <TextView
                android:id="@+id/step_without_lower_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/advanced_test_image"
                android:layout_alignTop="@id/advanced_test_image"
                android:layout_centerVertical="true"
                android:layout_marginLeft="5dp"
                android:layout_toRightOf="@id/advanced_test_image"
                android:gravity="center_vertical"
                android:text="123"
                android:textColor="@android:color/black"
                android:textSize="14sp"
                android:textStyle="bold" />
        </RelativeLayout>
    
    </RelativeLayout>
    

    maybe its not elegant but this solution worked for me

    0 讨论(0)
  • 2021-01-18 11:30

    View.GONE is actually releasing space but the other elements might have been confined to their current positions. Try this. In the custom layout file (which acts as view for the list items),

    Suppose if X is the UI element you want to be GONE, W is the element below X and Y is the element above X

    In the custom Layout of your ListView, (Assuming it's a relative layout) Attach the top of W to the bottom of X. And then attach the top of the element X to the bottom of Y.

    0 讨论(0)
  • 2021-01-18 11:32

    Are you trying to hide the whole list item? If so I guess that the list view won't like that because it is still calculating with the same amount of items. I don't think it will just ignore it because it's gone.

    The clean solution would be to return another getCount and just ignore the items you want to hide. Or remove items from the internal used list. Call notifyDataSetChanged on the adapter when you modified the amount of items in the list.

    0 讨论(0)
  • 2021-01-18 11:37

    I was able to solve this problem using Knickedi's solution and the comments under it. Just wanted to show my relatively complete adapter to clarify it a bit.

    I have the class StockItem with fields to hold a range of data for a single stock item. For the custom ArrayAdapter, the constructor takes the complete list of StockItems retrieved from a database table, and any add/remove methods I may add in the future will also operate on this list (mList). However, I overrode getView(), and getCount() to read from a second list (mFilteredList) produced using the method filterList():

    public class StockItemAdapter extends ArrayAdapter<StockItem> {
    
        ...
        ArrayList<StockItem> mList;
        ArrayList<StockItem> mFilteredList;
    
        public StockItemAdapter(Context context, int resource, ArrayList<StockItem> list) {
                super(context, resource, list);
    
        ...
            mList = list;
            mFilteredList = list;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            View row = convertView;
            StockItemHolder holder = null;
    
            if (row == null) {
                LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
                row = inflater.inflate(mResource, parent, false);
    
                holder = new StockItemHolder();
                holder.imageView = (ImageView)row.findViewById(R.id.imageView);
                ...
                row.setTag(holder);
            } else {
                holder = (StockItemHolder)row.getTag();
            }
            StockItem stockItem = mFilteredList.get(position);
            if (stockItem.getImage() != null) {
                holder.imageView.setImageBitmap(stockItem.getImage());
            } else {
                holder.imageView.setImageResource(R.drawable.terencephilip);
            }
            ...
            return row;
        }
    
        @Override
        public int getCount() {
            return mFilteredList.size();
        }
    
        static class StockItemHolder {
    
            ImageView imageView;
            ...
        }
    
        public void filterList(String search) {
    
            mFilteredList = new ArrayList<StockItem>();
            for (StockItem item : mList) {
                if (item.getDescription().toLowerCase(Locale.ENGLISH)
                        .contains(search.toLowerCase(Locale.ENGLISH))) {
                    mFilteredList.add(item);
                }
            }
            notifyDataSetChanged();
        }
    }
    

    filterList(String search) is called from an OnQueryTextListener and removes list items whose descriptions don't match the entered text.

    In the case of a large list, filterList() may be a problem on the main thread, but that is irrelevant for this question.

    EDIT: The getItem(position) method must also be overridden to return the item from mFilteredList.

    @Override
    public StockItem getItem(int position) {
    
        return mFilteredList.get(position);
    }
    
    0 讨论(0)
  • 2021-01-18 11:46
    convertView = inflater.inflate(R.layout.custom_layout, parent, false);
    
    if (CONDITION) {
        holder.wholeLayout.getLayoutParams().height = 1; // visibility Gone not working && 0 height crash app.
    } else {
        holder.wholeLayout.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT;
    }
    
    0 讨论(0)
  • 2021-01-18 11:50

    you should operate on the list adapter also...

    0 讨论(0)
提交回复
热议问题