Android selector changes color but not size
Asked Answered
M

3

6

I am trying to use a selector to implement a simple dot indicator for a ViewPager.

The dots are supposed to change color and size when the page is changed, but they only change color, while the size stays the same.

The selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <shape android:shape="oval">
             <size
               android:height="12dp"
               android:width="12dp"/>
            <solid android:color="@color/highlight"/>
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <size
              android:height="6dp"
              android:width="6dp"/>
            <solid android:color="@color/light_grey"/>
        </shape>
    </item>
</selector>

which is used in a layout

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:padding="@dimen/margin_2x"
  android:src="@drawable/dot_selector">
</ImageView>

which is used in onCreate() to show the initial state:

for (int i = 0; i < mPagerAdapter.getCount(); i++) {
    getLayoutInflater().inflate(R.layout.dot_indicator, viewPagerCountDots);
}
viewPagerCountDots.getChildAt(0).setSelected(true);

This looks as expected:

enter image description here

But when I change the selector state when the page is changed like this...

@Override
public void onPageSelected(int position) {
    for (int i = 0; i < mPagerAdapter.getCount(); i++) {
        viewPagerCountDots.getChildAt(i).setSelected(i == position);
    }
}

..it looks like this:

enter image description here

Invalidating the child or the ViewGroup does not do anything. Any clue what is going wrong here?

Update

Calling requestLayout on the child fixes it, as suggested by Bartek Lipinski

@Override
public void onPageSelected(int position) {
    for (int i = 0; i < mPagerAdapter.getCount(); i++) {
        View dotView = viewPagerCountDots.getChildAt(i);
        dotView.setSelected(i == position);
        dotView.invalidate();
        dotView.requestLayout();
    }
}
Melodrama answered 4/7, 2016 at 7:51 Comment(0)
P
8

add requestLayout() calls in your onPageSelected callback:

@Override
public void onPageSelected(int position) {
    for (int i = 0; i < mPagerAdapter.getCount(); i++) {
        viewPagerCountDots.getChildAt(i).setSelected(i == position);
        viewPagerCountDots.getChildAt(i).requestLayout();
    }
}

EDIT:

alternatively you can change your drawable to have always the same size, so the setSelected is enough:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <shape android:shape="oval">
            <size
                android:width="12dp"
                android:height="12dp"/>
            <solid android:color="@color/highlight"/>
        </shape>
    </item>
    <item>
        <layer-list>
            <item android:bottom="3dp"
                  android:left="3dp"
                  android:right="3dp"
                  android:top="3dp">
                <shape android:shape="oval">
                    <size
                        android:width="6dp"
                        android:height="6dp"/>
                    <solid android:color="@color/light_grey"/>
                </shape>
            </item>
        </layer-list>
    </item>
</selector>
Peursem answered 4/7, 2016 at 8:10 Comment(5)
This works, but there is a slight delay between the color and the size changeMelodrama
Calling requestLayout() is like asking Android to perform a layout pass on a View. It doesn't mean it's instant.Peursem
Calling invalidate() on the view before calling requestLayout() removes the delay.Melodrama
@PhilippE. I edited my answer to provide you with another approach you can takePeursem
The second one is probably the better solution as it does not require additional layout passes. Thanks!Melodrama
A
0

try like this

//if you container is LinearLayout ,change to LinearLayout.params 
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    //as you wish
    params.width=200;
    params.height=200;
    @Override
    public void onPageSelected(int position) {
        for (int i = 0; i < mPagerAdapter.getCount(); i++) {
           ImageView dotIv= (ImageView)viewPagerCountDots.getChildAt(i);
            dotIv.setImageResource("//normalPic");
            if(i==position){
                dotIv.setImageResource("//selectPic");
                dotIv.setLayoutParams(params);
            }

        }
    }
Alcheringa answered 4/7, 2016 at 8:9 Comment(0)
I
-1

View sizes are measured once when they are created. Since you set the layout width and height of the ImageView to "wrap_content", non-selected circles will have 6dp size and their size won't change. You can set the layout width and height "12dp".

BTW, I suggest using a library for indicator instead of implementing it. ViewPagerIndicator is a good one

Irremeable answered 4/7, 2016 at 8:5 Comment(1)
Thanks, but I am actually trying to get away from ViewPagerIndicator, as it is no longer maintainedMelodrama

© 2022 - 2024 — McMap. All rights reserved.