ExpandableListView - border around parent and its children

杀马特。学长 韩版系。学妹 提交于 2020-02-03 08:11:27

问题


I have a view that uses the ExpandableListView that has a ton of logic around it and in the adapters. For e.g., it looks like this

I have a requirement to display the same view with a different skin that has the expand/collapse hidden and has a border around parent and its children, something like this

I see attributes to have border for the whole control or just parent or individual child but nothing to have a border around parent and its children.

Has anyone done something like this? Short of not using Expandablelistview and recreating the view, is there anyway I can achieve the border?

Edit 1:

Here is a gist that has the template for what I am trying to do.

Edit 2:

I have a solution playing with parent and child borders,

setting parent to            ┎─┒ 

and all-but-last children to ┃ ┃

and last child to            ┖─┚

Here is the gist for the solution I have so far

I am still open to a better solution and will offer the bounty to anything that is less kludge than my solution.


回答1:


EDIT So I've added ItemDecoration feature to ExpandableListView, It's pretty much works like the RecyclerView's ItemDecoration, here is the code:

Subclass the ExpandableListView

public class ExpandableListViewItemDecoration extends ExpandableListView {
private List<ItemDecorationListView> itemDecorations = new ArrayList<>(1);

/* ... */

public void addItemDecoration(ItemDecorationListView item){
    itemDecorations.add(item);
}

@Override
public void draw(Canvas canvas) {
    super.draw(canvas);
    final int count = itemDecorations.size();
    for (int i = 0; i < count; i++) {
        itemDecorations.get(i).onDrawOver(canvas, this);
    }
}

ItemDecorationListView:

public abstract class ItemDecorationListView {

    public void onDrawOver(Canvas c, ListView parent) {
    }
}

The ItemDecorator:

public class ItemDecoratorBorderListView extends ItemDecorationListView {

private final Paint paint = new Paint();
private final int size;

public ItemDecoratorBorderListView(int size, @ColorInt int color) {
    this.size = size;
    paint.setColor(color);
    paint.setStrokeWidth(size);
    paint.setStyle(Paint.Style.STROKE);
}

public static final String TAG = ItemDecoratorBorderListView.class.getSimpleName();

@Override
public void onDrawOver(Canvas c, ListView parent) {
    super.onDrawOver(c, parent);
    int childCount = parent.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = parent.getChildAt(i);
        if (isHeader(child, parent, i)) {
            for (int j = i + 1; j < childCount; j++) {
                View childEnd = parent.getChildAt(j);
                boolean end = isHeader(childEnd, parent, i) || j == childCount - 1;
                if (end) {
                    if (BuildConfig.DEBUG) { Log.d(TAG, String.format(Locale.ENGLISH, "Draw called i: %d, j: %d", i, j)); }
                    childEnd = parent.getChildAt(j - 1);
                    if (j == childCount - 1) { childEnd = parent.getChildAt(j); }
                    float top = child.getTop() + child.getTranslationY() + size + child.getPaddingTop();
                    float bottom = childEnd.getBottom() + childEnd.getTranslationY() - size - childEnd
                            .getPaddingBottom();

                    float right = child.getRight() + child.getTranslationX() - size - child.getPaddingRight();
                    float left = child.getLeft() + child.getTranslationX() + size + child.getPaddingLeft();
                    c.drawRect(left, top, right, bottom, paint);
                    i = j - 1;
                    break;
                }
            }
        }
    }
}

public boolean isHeader(View child, ListView parent, int position) {
    //You need to set an Id for your layout
    return child.getId() == R.id.header;
}

}

And just add it to your ExpandableListView:

expandableList.addItemDecoration(new ItemDecoratorBorderListView(
            getResources().getDimensionPixelSize(R.dimen.stroke_size),
            Color.GRAY
    ));

Old Answer:

This is an implementation with RecyclerView and ItemDecoration, I've already written this solution before knowing you're stuck with legacy code, So I'm sharing this anyway.

Item Decoration:

public class ItemDecoratorBorder extends RecyclerView.ItemDecoration {

private final Paint paint = new Paint();
private final int size;

public ItemDecoratorBorder(int size, @ColorInt int color) {
    this.size = size;
    paint.setColor(color);
    paint.setStrokeWidth(size);
    paint.setStyle(Paint.Style.STROKE);
}

public static final String TAG = ItemDecoratorBorder.class.getSimpleName();

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
    if (parent.getLayoutManager() == null) { return; }
    int childCount = parent.getChildCount();
    RecyclerView.LayoutManager lm = parent.getLayoutManager();
    for (int i = 0; i < childCount; i++) {
        View child = parent.getChildAt(i);
        if (isHeader(child, parent)) {
            for (int j = i + 1; j < childCount; j++) {
                View childEnd = parent.getChildAt(j);
                boolean end = isHeader(childEnd, parent) || j == childCount - 1;
                if (end) {
                    if (BuildConfig.DEBUG) { Log.d(TAG, String.format(Locale.ENGLISH, "Draw called i: %d, j: %d", i, j)); }
                    childEnd = parent.getChildAt(j - 1);
                    if (j == childCount - 1) {
                        childEnd = parent.getChildAt(j);
                    }
                    float top = child.getTop() + child.getTranslationY() + size + child.getPaddingTop();
                    float bottom = lm.getDecoratedBottom(childEnd) + childEnd.getTranslationY() - size - childEnd.getPaddingBottom();

                    float right = lm.getDecoratedRight(child) + child.getTranslationX() - size - child.getPaddingRight();
                    float left = lm.getDecoratedLeft(child) + child.getTranslationX() + size + child.getPaddingLeft();
                    c.drawRect(left, top, right, bottom, paint);
                    i = j - 1;
                    break;
                }
            }
        }
    }
}

public boolean isHeader(View child, RecyclerView parent) {
    int viewType = parent.getLayoutManager().getItemViewType(child);
    return viewType == R.layout.layout_header;
}

I'm finding where a group starts and ends using the view types and draw a rectangle around the start and end position.

The code is available at my github repo




回答2:


Well I have a solution for you, but It's better to use recycleView instead of listView, However, We can draw line for every sides e.g:

for parent group it will be something like ┎─┒ and for all child's without the last child it will be something like: ┎ ┒ and for the last child it will be like : ──.

The code: `groupbg.xml:

    <?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#000" />
        </shape>
    </item>
    <item android:left="2dp" android:top="2dp" android:right="2dp">
        <shape android:shape="rectangle">
            <solid android:color="#fff" />
        </shape>
    </item>
</layer-list>

normalchild.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape android:shape="rectangle">
        <solid android:color="#000" />
    </shape>
</item>
<item android:left="2dp" android:right="2dp">
    <shape android:shape="rectangle">
        <solid android:color="#fff" />
    </shape>
</item>

bottomchild.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape android:shape="rectangle">
        <solid android:color="#000" />
    </shape>
</item>
<item android:bottom="2dp">
    <shape android:shape="rectangle">
        <solid android:color="#fff" />
    </shape>
</item>

No set it to your adapter:

private int childrenCount;


    @Override
    public int getChildrenCount(int groupPosition) {

        return childrenCount = data.get(groupPosition).getItems().length;
    }



    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        View view;
        if (convertView == null){
            view = LayoutInflater.from(context).inflate(R.layout.item, parent, false);
        }
        else {
            view = convertView;
        }


        view.setBackground(context.getResources().getDrawable(R.drawable.groupbg));
        TextView lblNumber = view.findViewById(R.id.lblNumber);
        TextView lblName = view.findViewById(R.id.lblName);

        lblNumber.setText((groupPosition + 1) + ".");
        lblName.setText(((TestModel)getGroup(groupPosition)).getCategory());

        return view;
    }



    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

        View view;
        if (convertView == null){
            view = LayoutInflater.from(context).inflate(R.layout.item_child, parent, false);
        }
        else {
            view = convertView;
        }

        TextView lblNumber = view.findViewById(R.id.lblNumber);
        TextView lblName = view.findViewById(R.id.lblName);

        lblNumber.setText((childPosition + 1)+ ".");
        lblName.setText((String)getChild(groupPosition, childPosition));

        if (childPosition < childrenCount)
            view.setBackground(context.getResources().getDrawable(R.drawable.normalchild));
        else view.setBackground(context.getResources().getDrawable(R.drawable.bottomchild));
        return view;
    }



回答3:


You can try using this library. Custom RecyclerView that implement features like ExpandableListView.



来源:https://stackoverflow.com/questions/49408666/expandablelistview-border-around-parent-and-its-children

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!