7

I've been using this basic wrapper around a SeekBar but find it hides the thumb, or does something funky like make it white on a white background, under Marshmallow.

I used the AS "BlankActivity" wizard to create a project to illustrate this, changing nothing except what is described here, from the defaults. On the left is Lollipop, and the same code is running on the right under Marshmallow:

enter image description here

There is a custom horizontal SeekBar to test if there was a general problem customizing them, which there isn't. The first vertical one on the left has no style, which is fine pre-Marshmallow but not otherwise, the central one explicity uses the Widget.Material.Light.SeekBar style to test if the default somehow wasn't being picked up, and the last one gives a big clue because it uses the old Widget.Holo.SeekBar style where it then appears, albeit looking like it came out a few years ago.

Here's the layout for this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    >

    <com.otamate.seekbarmarshbug.CustomSeekBar
        android:id="@+id/seekBarCustom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

    <com.otamate.seekbarmarshbug.VerticalSeekBar
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_below="@+id/seekBarCustom"
        />

    <com.otamate.seekbarmarshbug.VerticalSeekBar
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:layout_below="@+id/seekBarCustom"
        android:layout_centerHorizontal="true"
        style="@android:style/Widget.Material.Light.SeekBar"
        />

    <com.otamate.seekbarmarshbug.VerticalSeekBar
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/seekBarCustom"
        style="@android:style/Widget.Holo.SeekBar"
        />

</RelativeLayout>

The CustomSeekBar:

package com.otamate.seekbarmarshbug;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.SeekBar;

public class CustomSeekBar extends SeekBar {

    public CustomSeekBar(Context context) {
        super(context);
    }

    public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public CustomSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

and the VerticalSeekBar:

package com.otamate.seekbarmarshbug;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;

public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }

}
2
  • This bug is almost fixed in com.android.support:appcompat-v7:23.1.0. Using that, the thumb looks the same as the horizontal one with the code as presented here, except there is no animation when it is selected and moved (the groovy "bulge and expanding ripple" which flashes up briefly from its border), you just see the boring but functional circle. Commented Oct 16, 2015 at 14:50
  • 1
    using appcompat-v7:23.1.1 here. The thumb is completely invisible on marshmallow. Ended up creating a custom thumb, which works fine.
    – Matthias
    Commented Jan 19, 2016 at 15:56

4 Answers 4

7

Try this code. It works fine!

<seekBar>
   .....
   android:splitTrack="false"
</seekBar>
3
  • It helps me on Android 7.0 Nougat :) Commented Mar 14, 2017 at 9:56
  • Saved my day ! Thank you. Commented Jun 20, 2017 at 9:32
  • I have no idea how you figured that out, but yes, it works... Thanks!
    – hlascelles
    Commented Sep 25, 2017 at 19:36
7

It seems like the marshmallow OS somehow broke the draw on the canvas. I implemented a simple fix.

I made a modification in two methods of my custom VerticalSeekBar

protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(), 0);
    drawThumb(c); //redrawing thumb

    super.onDraw(c);
}

void drawThumb(Canvas canvas) {
    Drawable thumb = getThumb();
    if (thumb != null) {
        Rect thumbBounds = thumb.getBounds();
        canvas.save();
        canvas.rotate(90, thumbBounds.exactCenterX(), thumbBounds.exactCenterY());
        thumb.draw(canvas);
        canvas.restore();
    }
}

Edit: This will only work on Marshmallow devices. If you try to use this on devices that are pre M, they will show two thumbs. If you want this to work on all devices, you will need to completely redraw the thumb by using a custom attribute for the thumb.

Check this github repo for a VerticalSeekBar that works with M https://github.com/3drobotics/AndroidWidgets

1
  • 1
    Works great, just use: if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) drawThumb(c); and will work like a charm except cause your thumbwill be upside down. I fixed this with a new version of my thumb into drawables-v24 folder: <layer-list xmlns:android="schemas.android.com/apk/res/android" > <item> <rotate android:drawable="@drawable/ic_search" android:fromDegrees="180" /> </item> </layer-list> Commented Nov 28, 2017 at 10:10
1

I am using a Vertical Seekbar too and had the same problem. This worked for me:

In onCreate:

    ViewTreeObserver vto = mSeekBar.getViewTreeObserver();
    vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            Resources res = getResources();
            Drawable thumb_image = res.getDrawable(R.drawable.vertseekbarthumb);
            int h = (int) (mSeekBar.getMeasuredWidth());
            int w = (int) (h);
            Bitmap bmpOrg = ((BitmapDrawable) thumb_image).getBitmap();
            Bitmap bmpScaled = Bitmap.createScaledBitmap(bmpOrg, w, h, true);
            mNewThumb = new BitmapDrawable(res, bmpScaled);
            mNewThumb.setBounds(0, 0, mNewThumb.getIntrinsicWidth(), mNewThumb.getIntrinsicHeight());
            mSeekBar.setThumb(mNewThumb);
            mSeekBar.getViewTreeObserver().removeOnPreDrawListener(this);
            mSeekBar.setProgressAndThumb(45);
            return true;
        }
    });

I had the onPreDraw call before marshmallow already and after it stopped showing the thumb I actually only added this call:

mSeekBar.setProgressAndThumb(45);

And it started working on marsmallow too. 45 is just the progress I want to set the Thumb to, as my Seekbar has a maximum progress of 90 I want it to be in the middle. So you have to change this number to match your progress.

1
  • This is what I was looking for! Commented Oct 27, 2016 at 4:45
-1

Just use a normal SeekBar and use android:rotation="270" that helped for me.

<SeekBar
android:id="@+id/seekBar"
android:rotation="270"
/>

Not the answer you're looking for? Browse other questions tagged or ask your own question.