Expandable list view move group icon indicator to right
Asked Answered
S

16

67

As regards to expandable list view, the group icon indicator is positioned to the left side, can I move the indicator and positioned it to the right? Thanks.

EDITED: Since I don't want to extend the view, I got this workaround of getting the width dynamically. Just sharing my solution.

Display newDisplay = getWindowManager().getDefaultDisplay(); 
int width = newDisplay.getWidth();
newListView.setIndicatorBounds(width-50, width);
Styracaceous answered 27/4, 2011 at 6:46 Comment(1)
Not working on Nexus 7. Check https://mcmap.net/q/293410/-expandable-list-view-move-group-icon-indicator-to-right before implementing this solution.Arnica
M
20

According to ExpandableListView's source code, the only way to move an indicator to the right side is to change its bounds using ExpandableListView.setIndicatorBounds() method. You can calculate bound in onSizeChanged() method, for example.

Monosymmetric answered 27/4, 2011 at 7:9 Comment(3)
Thanks Pixie. I tried newListView.setIndicatorBounds(newListView.getWidth()-40, newListView.getWidth()); but the indicator disappears. When I get the value of getWidth(), it returns zero. Does that mean that I have to extend the expandable list view just to override the onSizeChanged()? Thanks.Styracaceous
By the way, I tried setting a static value newListView.setIndicatorBounds(140, 150); and it works. But how could I get the width dynamically? Thanks.Styracaceous
I think its width is zero because it haven't measured it yet. So, the most obvious way to get its width and react on size changes is to override onSizeChanged() method. Maybe, there're simpler ways to do that, but I don't know any.Monosymmetric
S
71

setIndicatorBounds(int, int) does not work properly for Android 4.3. They introduced a new method setIndicatorBoundsRelative(int, int) which works ok for 4.3.

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
       mExpandableListView.setIndicatorBounds(myLeft, myRight);
    } else {
       mExpandableListView.setIndicatorBoundsRelative(myLeft, myRight);
    }
}
Stoffel answered 2/8, 2013 at 9:53 Comment(5)
I was going insane as to why the code everyone on the net seemed to suggest works was not working!!! This answer definitely needs more visibilityAcciaccatura
I have set the target api level to 18, but i get expandablelistview does not have this method. What could be wrong?Cardialgia
i got the solution here thanksCardialgia
You should set API 18 as project BUILD target - which is different from the AndroidManifest.xml "targetSdk" attribute. For example, you can have targetSdk="14" and project BUILD target = "18".Stoffel
oh gosh!! finally its working, but any specification why this?Neuberger
E
46

All XML solution! I found a better way of tackling this issue. This does not require you to mess with display metrics, no need to programmatically hack it.

This is what you need to do:

1) set layout direction to right-to-left

<ExpandableListView
...
android:layoutDirection="rtl" />

2) make sure your list item layout includes this (yes, you'll need custom layout):

android:gravity="left" //to adjust the text to align to the left again
android:layoutDirection="ltr"  //OR this line, based on your layout

And you are good to go!

Empale answered 28/6, 2016 at 14:52 Comment(2)
it does effect on child items, make sure to update child item layout tooFlush
Good solution, but not for everyone. Scrollbar will be on the left. Plus, changing of the child Views. A workaround, I should say.Shalna
D
39

The best way to do this is below but not for all Android Version So use 2nd or third method specified below

ViewTreeObserver vto = mExpandableListView.getViewTreeObserver();

    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
                 mExpandableListView.setIndicatorBounds(mExpandableListView.getRight()- 40, mExpandableListView.getWidth());
        }
    });

or this:

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    mExpandableListView.setIndicatorBounds(mExpandableListView.getRight()- 40, mExpandableListView.getWidth());
} 

will move that indicator to right of list view even on device and on tab also.

or you can do this in a postdelayed thread.

 (new Handler()).post(new Runnable() {

        @Override
        public void run() {
              mExpandableListView.setIndicatorBounds(mExpandableListView.getRight()- 40, mExpandableListView.getWidth());
        }
    });
Drumlin answered 13/2, 2012 at 11:1 Comment(4)
Great solution! However, note that setIndicatorBounds() sets the left and right bounds in pixels and not DIP. Your answer won't work across devices with different screen densities. Make sure you convert your offset 40 from DP to pixels before subtracting.Shinbone
@Shinbone yeah you are right i did that in my project but forgot to mention it +1 for your comment.Drumlin
Neither the onWindowFocusChanged code nor the getViewTreeObserver code work here. While getRight and getWidth both return correct values, setIndicatorBounds seems to have no effect whatsoever. Could there be something else required? Some sort of layout rule for the items and groups?Lin
Where i can Override onWindowFocusChanged(boolean hasFocus) methodVoiceless
M
20

According to ExpandableListView's source code, the only way to move an indicator to the right side is to change its bounds using ExpandableListView.setIndicatorBounds() method. You can calculate bound in onSizeChanged() method, for example.

Monosymmetric answered 27/4, 2011 at 7:9 Comment(3)
Thanks Pixie. I tried newListView.setIndicatorBounds(newListView.getWidth()-40, newListView.getWidth()); but the indicator disappears. When I get the value of getWidth(), it returns zero. Does that mean that I have to extend the expandable list view just to override the onSizeChanged()? Thanks.Styracaceous
By the way, I tried setting a static value newListView.setIndicatorBounds(140, 150); and it works. But how could I get the width dynamically? Thanks.Styracaceous
I think its width is zero because it haven't measured it yet. So, the most obvious way to get its width and react on size changes is to override onSizeChanged() method. Maybe, there're simpler ways to do that, but I don't know any.Monosymmetric
P
11

I have tried to using setIndicatorBounds and setIndicatorBoundsRelative but the icon not showing as nice as expected. So I change my code as per below :

create a group layout :

    <?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="48dp"
    android:background="@drawable/header_selector" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="25dp"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="12dp"
        android:layout_marginRight="12dp"
        android:contentDescription="@string/desc_list_item_icon"
        android:src="@drawable/ic_home" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_toRightOf="@id/icon"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:paddingRight="40dp"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:textColor="@color/list_item_title" />

    <TextView
        android:id="@+id/counter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginRight="8dp"
        android:background="@drawable/counter_bg"
        android:textColor="@color/counter_text_color" />

    <ImageView
        android:id="@+id/icon_expand"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="200dp"
        android:layout_toRightOf="@+id/counter"
        android:contentDescription="@string/desc_list_item_icon"
        android:src="@drawable/navigation_expand"
        android:visibility="visible" />

    <ImageView
        android:id="@+id/icon_collapse"
        android:layout_width="25dp"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="200dp"
        android:layout_toRightOf="@+id/counter"
        android:contentDescription="@string/desc_list_item_icon"
        android:src="@drawable/navigation_collapse"
        android:visibility="gone" /> 

</RelativeLayout>

and using this layout in adapter class inside getGroupView method :

 @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        NavDrawerItem itemHeader = (NavDrawerItem) getGroup(groupPosition);

        LayoutInflater inflater = (LayoutInflater) this.context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View view = null;
        if (convertView == null) {
            view = (View) inflater.inflate(R.layout.drawer_header_item, null);
        } else {
            view = convertView;
        }

        ImageView icon = (ImageView) view.findViewById(R.id.icon);

        if (itemHeader.isIconVisible()) {
            icon.setImageResource(itemHeader.getIcon());
        } else {
            icon.setVisibility(View.GONE);
        }

        TextView textTitle = (TextView) view.findViewById(R.id.title);
        textTitle.setText(" " + itemHeader.getTitle());

        TextView textCount = (TextView) view.findViewById(R.id.counter);

        if (itemHeader.getCounterVisibility()) {
            textCount.setText(itemHeader.getCount());
        } else {
            textCount.setVisibility(View.GONE);
        }

        ImageView iconExpand = (ImageView) view.findViewById(R.id.icon_expand);
        ImageView iconCollapse = (ImageView) view
                .findViewById(R.id.icon_collapse);

        if (isExpanded) {
            iconExpand.setVisibility(View.GONE);
            iconCollapse.setVisibility(View.VISIBLE);
        } else {
            iconExpand.setVisibility(View.VISIBLE);
            iconCollapse.setVisibility(View.GONE);
        }

        if (getChildrenCount(groupPosition) == 0) {
            iconExpand.setVisibility(View.GONE);
            iconCollapse.setVisibility(View.GONE);
        }

        return view;
    }

With this method you can adjust the position of the expand/collapse icon properly. hope it helps.

Penza answered 5/12, 2013 at 8:16 Comment(1)
This user-managed approach is better and cleaner. The default one using 9-patch underlay indicators feels outdated to me.Gond
S
5

Expandable list view move group icon indicator to right

The setIndicatorBounds(int left, int right) is used to set the indicator bounds for the group view of an expandable list view.

explvList.setIndicatorBounds(width-GetDipsFromPixel(35), width-GetDipsFromPixel(5));
Here width means device width.

/Convert pixel to dip 
public int GetDipsFromPixel(float pixels)
{
        // Get the screen's density scale
        final float scale = getResources().getDisplayMetrics().density;
        // Convert the dps to pixels, based on density scale
        return (int) (pixels * scale + 0.5f);
} 

FYI: The width is equal to width specified in the setBounds method. Here in my snippet it is 35.Other wise the icon is disturbed. For more details you may visit here:

Sluiter answered 8/6, 2015 at 8:49 Comment(0)
G
3

Write this code in Expandable Base adapter.

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
                         View convertView, ViewGroup parent) {
    ImageView imgs;

    String headerTitle = (String) getGroup(groupPosition);
    if (convertView == null) {
        LayoutInflater infalInflater = (LayoutInflater) this._context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.list_group, null);
    }

    TextView lblListHeader = (TextView) convertView
            .findViewById(R.id.lblListHeader);
     imgs = (ImageView) convertView
            .findViewById(R.id.img_list);
    lblListHeader.setTypeface(null, Typeface.BOLD);
    lblListHeader.setText(headerTitle);
    if (isExpanded) {
  imgs.setImageResource(R.drawable.up_arrow);
    }
    else {
        imgs.setImageResource(R.drawable.downs_arrow);
    }

    return convertView;
}
  • List item
Guzman answered 2/8, 2017 at 7:24 Comment(1)
Its nice and easy way to do it.Offshore
D
2
FIX: 

They introduced a new method in the API 18 and onwards, a method called setIndicatorBoundsRelative(int, int). You should check for Android version and use the respective methods with the API's:

expListView = (ExpandableListView) v.findViewById(R.id.laptop_list);

metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels;

if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
    expListView.setIndicatorBounds(width - GetDipsFromPixel(50), width - GetDipsFromPixel(10));

} else {
    expListView.setIndicatorBoundsRelative(width - GetDipsFromPixel(50), width - GetDipsFromPixel(10));

}
Debtor answered 3/7, 2015 at 13:31 Comment(0)
R
2

if it is in Fragment - onViewCreated()

    ViewTreeObserver vto = Expnlist.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new      ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Expnlist.setIndicatorBounds(Expnlist.getMeasuredWidth() - 80, Expnlist.getMeasuredWidth());
        }
    });

its works for me!. Cheers!

Rochdale answered 31/3, 2016 at 11:43 Comment(0)
S
2

First you have to remove group indicator from your xml

<ExpandableListView
            android:id="@+id/expandableListView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/nav_header_height"
            android:background="@android:color/white"
            android:dividerHeight="0dp"
            android:focusable="false"
            android:groupIndicator="@null" />

then do as follows in your custom adapter

public View getGroupView(int groupPosition, boolean isExpanded,
    View convertView, ViewGroup parent) {

if (isExpanded) {
    groupHolder.img.setImageResource(R.drawable.group_down);
} else {
    groupHolder.img.setImageResource(R.drawable.group_up);
}}
Stirps answered 13/8, 2020 at 12:27 Comment(1)
That's how I solved it too, just to clarify for anyone who might have miss this: img is an ImageView in the list item layout file, so you can place it wherever you want and it act as the group indicatorErnesternesta
S
0

The setIndicatorBounds(int left, int right) is used to set the indicator bounds for the group view of an ExpandableListView.

explvList.setIndicatorBounds(width-GetDipsFromPixel(35), width-GetDipsFromPixel(5));

Here width means device width.

Convert pixel to dip :

public int GetDipsFromPixel(float pixels){
    // Get the screen's density scale
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (pixels * scale + 0.5f);
} 
Straitjacket answered 2/8, 2017 at 7:26 Comment(0)
M
0

I'm using the following short piece inspired by this sample that requires two drawables on the folder:

if (isExpanded) {
    ListHeader.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.up, 0);
} else {
    ListHeader.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.down, 0);
} 

Anyway, I had to adopt goodKode's solution as well "to get rid off" the default arrows.

So, I'd advise to stick with the simple solution previously provided as it's the most minimalist and precise approach.

Makowski answered 13/12, 2017 at 4:18 Comment(0)
C
0
     DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int width = metrics.widthPixels; 

 mExpandableList = (ExpandableListView)findViewById(R.id.expandable_list);
 mExpandableList.setIndicatorBounds(width - GetPixelFromDips(50), width - GetPixelFromDips(10));  

   public int GetPixelFromDips(float pixels) {
    // Get the screen's density scale 
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (pixels * scale + 0.5f);
}

this one worked for me

Compagnie answered 30/1, 2018 at 11:13 Comment(0)
P
0

First set android:groupIndicator="@null" in you Expandable listview as below:

then in your header layout as below:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="@dimen/screen_margin"
    android:paddingRight="@dimen/screen_margin"
    android:paddingTop="@dimen/dot_margin"
    android:paddingBottom="@dimen/dot_margin"
    android:orientation="vertical">




    <TextView
        android:id="@+id/tvQuestion"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="what is jodi?"
        android:textColor="@color/textColor"
        android:textSize="@dimen/font_large"
        app:customFont="@string/font_toolbar"
        android:layout_toLeftOf="@+id/imgDropDown"
        android:layout_marginRight="@dimen/margin"
       />


    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imgDropDown"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:src="@drawable/right_arrow"/>

</RelativeLayout>

Lastly add this in you adapter in getGroupView method add below :

TextView lblListHeader = (TextView) convertView.findViewById(R.id.tvQuestion);

lblListHeader.setText(headerTitle);

ImageView img=convertView.findViewById(R.id.imgDropDown);

if (isExpanded) {
  img.setImageResource(R.drawable.down_arrow);
    lblListHeader.setTextColor(_context.getResources().getColor(R.color.colorPrimary));
} else {
 img.setImageResource(R.drawable.right_arrow);
    lblListHeader.setTextColor(_context.getResources().getColor(R.color.textColor));
}
Pilsner answered 12/4, 2018 at 11:46 Comment(0)
T
0

i know it's too late but here is a solution to put the indicator on the right programmatically, simple and easy to use :

expandableListView = (ExpandableListView) findViewById(R.id.expandableListView);

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        int width = size.x;
        Resources r = getResources();
        int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                50, r.getDisplayMetrics());
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            expandableListView.setIndicatorBounds(width - px, width);
        } else {
            expandableListView.setIndicatorBoundsRelative(width - px, width);
        }

where expandableListView is your ExpandableListview

Trixie answered 23/4, 2018 at 13:11 Comment(0)
T
-2

Try this... this code for adjusting the ExpandableList group indicator into right side of the view.

private ExpandableListView expandableListView;
DisplayMetrics metrics;
int width;
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels;
expandableListView = (ExpandableListView) findViewById(R.id.expandableListView1);
expandableListView.setIndicatorBounds(width - GetDipsFromPixel(50), width - GetDipsFromPixel(10));

And call this method,

public int GetDipsFromPixel(float pixels)
{
 // Get the screen's density scale
 final float scale = getResources().getDisplayMetrics().density;
 // Convert the dps to pixels, based on density scale
 return (int) (pixels * scale + 0.5f);
}

The result is.. ExpandableList right side group indicator

Thorax answered 13/2, 2014 at 12:9 Comment(1)
Unfortunately this does not work. Indicator is still displayed on the left side...Tilley

© 2022 - 2024 — McMap. All rights reserved.