tjerkw / Android-SlideExpandableListView

A better ExpandableListView, with animated expandable views for each list item
Apache License 2.0
1.98k stars 741 forks source link

Expandable View layout_height must be fixed. #2

Closed nathanielwolf closed 11 years ago

nathanielwolf commented 11 years ago

This does not function when using wrap_content for the layout_height of the collapsable view. The animator cannot get a proper value for mEndHeight because the view has not been laid out yet.

tjerkw commented 11 years ago

Yes indeed that is a problem currently. Do you have an idea how to solve this?

nathanielwolf commented 11 years ago

I solved this problem, and the problem of randomly expanded views as result of view recycling in the Adapter.

public class MyListAdapter extends ArrayAdapter<MyObject> 

    int[] mCollapsableHeights;
    boolean[] mOpenned;

    public WhyPuursAdapter(Context context,
            List<WhyPuur> objects) {
        super(context, 0, objects);
        mCollapsableHeights = new int[objects.size()];
        mOpenned = new boolean[objects.size()];
    }

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    final View view = convertView;
    final View collapsable = view.findViewById(R.id.row_whypure_collapsable);

    //measure only once - hide expanded views by returning false in onPreDraw()
    if(mCollapsableHeights[position] == 0){
        view.getViewTreeObserver().addOnPreDrawListener(
            new OnPreDrawListener(){
                @Override
                public boolean onPreDraw() {
                    view.getViewTreeObserver().removeOnPreDrawListener(this);
                    mTextHeights[position] = collapsable.getHeight();
                    setCollapsedView(collapsable, position);
                    return false;
                }
            });

    } else 
            setCollapsedView(collapsable, position);

    view.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {

                mOpenned[position] = !mOpenned[position];
                ExpandAnimation newAnim = new ExpandAnimation(collapsable, 300);
                collapsable.startAnimation(newAnim);
            }
        });

    return view;    
}

/*
* Set visibility of collapsed view
*/
public void setCollapsedView(View collapsable, int position){

        final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)collapsable.getLayoutParams();
        if(!mOpenned[position] ){
            collapsable.setVisibility(View.GONE);
            params.bottomMargin = 0-mTextHeights[position];
        }else{
            collapsable.setVisibility(View.VISIBLE);
            params.bottomMargin = 0;
        }
        collapsable.setLayoutParams(params);
    }
}
myrmidon-media commented 11 years ago

I solved it like this:

public ExpandCollapseAnimation(View view, int duration, int type) {

        setDuration(duration);
        mAnimatedView = view;
        //Force Measure with unspecified specs
        mAnimatedView.measure(0, 0);
        //Get Measured Height
        mEndHeight = mAnimatedView.getMeasuredHeight();
        //mEndHeight = mAnimatedView.getLayoutParams().height;
        mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
        mType = type;
        if(mType == EXPAND) {

            mLayoutParams.bottomMargin = -mEndHeight;
        } else {

            mLayoutParams.bottomMargin = 0;
        }
        view.setVisibility(View.VISIBLE);
    }
tjerkw commented 11 years ago

Hey @nathanielwolf thanks, thats a smart solution. B.t.w. the problem randomly expanded views is just solved in the current master. So that should be fixed now.

For now i'm going to test the solution by @gorilla-maguila

tjerkw commented 11 years ago

@gorilla-maguila your solution does not have the correct measured height for the /sample project. Hmmz i'm going to look for the proper generic solution.

myrmidon-media commented 11 years ago

what's giving you getMeasuredHeight() ? In my project works great the above solution.

tjerkw commented 11 years ago

Issue #6 was related.

I think I tackled it. I solved it by using the OnPreDrawListener and setCollapseView method as @nathanielwolf suggested, smart move btw! However I also updated the ExpandCollapseAnimation to use getHeight instead of the LinearLayout height.

My commit: https://github.com/tjerkw/Android-SlideExpandableListView/commit/4a79ddfa7f5a19c6ae93d8cfbc53429627566eae

B.t.w. the only thing that I dislike is the Map<Integer, Integer> that now has an entry for every item in the list. If the list grows to 1000 items, it means a lot of extra memory is needed. But I don't see a nice solution for you.

@nathanielwolf @gorilla-maguila @grivos could you all confirm that this commit fixes the problem?

Thanks in Advance!! Coudn't do it without your ideas!

grivos commented 11 years ago

Looks good. Nice work!

However, There is a problem if the data set is changed and the current state is invalid. You might want to make WrapperListAdapterImpl extend BaseAdapter, and then in AbstractSlideExpandableListAdapter - override notifyDataSetChanged() and reset the state there.

tjerkw commented 11 years ago

I thought of that problem too. Wanted to solve it with:

registerDataSetObserver(new DataSetObserver() { ... }

However If a user of the library would set its own datasetobserver it would break my listener. Since only one listener is allowed.

I think your solution is the correct one. It should also override notifyDataSetInvalidated() in that case.

Will look at this later.

tjerkw commented 11 years ago

Created issue #11, closed this one.