web-dev-qa-db-fra.com

Ajouter du texte dynamique sur Android SeekBar thumb

J'essaie de créer cette barre de recherche personnalisée dans Android 2.2 et tout ce que je fais semble être faux! J'essaie d'afficher la valeur de la barre de recherche sur l'image du pouce de la barre de recherche. Est-ce que quelqu'un a des expériences avec ça?

Je suppose que vous avez déjà étendu la classe de base, vous avez donc quelque chose comme:

public class SeekBarHint extends SeekBar {
  public SeekBarHint (Context context) {
      super(context);
  }

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

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

Maintenant, vous remplacez la méthode onDraw par votre propre code. Insérer ce qui suit:

@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
}

Maintenant, vous voulez dessiner du texte près du pouce, mais il n’ya pas de moyen pratique d’obtenir la position x du pouce. Nous avons juste besoin d'un peu de maths.

@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
    int thumb_x = ( (double)this.getProgress()/this.getMax() ) * (double)this.getWidth();
    int middle = this.getHeight()/2;
    // your drawing code here, ie Canvas.drawText();
}
20
Snailer

J'ai suivi une approche différente qui offre plus de possibilités pour personnaliser le pouce. La sortie finale ressemblera à ceci:

 enter image description here

Vous devez d’abord concevoir la mise en page qui sera configurée de manière à pouvoir être dessinée par le pouce.

layout_seekbar_thumb.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:orientation="vertical">

    <LinearLayout
        Android:layout_width="@dimen/seekbar_thumb_size"
        Android:layout_height="@dimen/seekbar_thumb_size"
        Android:background="@drawable/ic_seekbar_thumb_back"
        Android:gravity="center"
        Android:orientation="vertical">

        <TextView
            Android:id="@+id/tvProgress"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="0"
            Android:textColor="#000000"
            Android:textSize="14sp" />

    </LinearLayout>

</LinearLayout>

Ici seekbar_thumb_size peut être de petite taille selon vos besoins. J'ai utilisé 30dp ici. Pour le fond, vous pouvez utiliser n’importe quelle icône/dessinable de votre choix.

Maintenant, vous avez besoin que cette vue soit configurée de manière à ce qu'elle puisse être dessinée par le pouce. Vous devez donc l'obtenir avec le code suivant:

View thumbView = LayoutInflater.from(YourActivity.this).inflate(R.layout.layout_seekbar_thumb, null, false);

Ici, je suggère d’initialiser cette vue dans onCreate() donc pas besoin de la gonfler encore et encore.

Définissez maintenant cette vue comme pouvant être dessinée par le pouce lorsque la progression de seekBar est modifiée. Ajoutez la méthode suivante dans votre code:

public Drawable getThumb(int progress) {
        ((TextView) thumbView.findViewById(R.id.tvProgress)).setText(progress + "");

        thumbView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        Bitmap bitmap = Bitmap.createBitmap(thumbView.getMeasuredWidth(), thumbView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        thumbView.layout(0, 0, thumbView.getMeasuredWidth(), thumbView.getMeasuredHeight());
        thumbView.draw(canvas);

        return new BitmapDrawable(getResources(), bitmap);
}

Appelez maintenant cette méthode à partir de onProgressChanged().

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                // You can have your own calculation for progress                    
                seekBar.setThumb(getThumb(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

Remarque: Appelez également la méthode getThumb() lorsque vous initialisez seekBar pour l'initialiser avec la valeur par défaut.

Avec cette approche, vous pouvez avoir n'importe quelle vue personnalisée sur le changement de progression.

12
Chintan Shah
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
                int val = (progress * (seekBar.getWidth() - 2 * seekBar.getThumbOffset())) / seekBar.getMax();
                text_seekbar.setText("" + progress);
                text_seekbar.setX(seekBar.getX() + val + seekBar.getThumbOffset() / 2);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                text_seekbar.setVisibility(View.VISIBLE);

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                text_seekbar.setVisibility(View.GONE);
            }
        });
7
Chirag Darji

Hey j'ai trouvé une autre solution, semble plus simple:

 private void setText(){
    int progress = mSeekBar.getProgress();
    int max= mSeekBar.getMax();
    int offset = mSeekBar.getThumbOffset();
    float percent = ((float)progress)/(float)max;
    int width = mSeekBar.getWidth() - 2*offset;

    int answer =((int)(width*percent +offset - mText.getWidth()/2));
    mText.setX(answer);

}


@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    setText();
    mText.setText(""+progress);
}
6
Fashizel

Cela a fonctionné pour moi

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int val = (progress * (seekBar.getWidth() - 2 * seekBar.getThumbOffset())) / seekBar.getMax();
_testText.setText("" + progress);
_testText.setX(seekBar.getX() + val + seekBar.getThumbOffset() / 2);
}
6
patrickfdsouza

J'ai utilisé cette bibliothèque pour créer une vue textuelle pouvant être dessinée et placée au pouce par programme.

https://github.com/amulyakhare/TextDrawable

Le code ressemble à ceci:

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
            String dynamicText = String.valueOf(progress);
            TextDrawable drawable = TextDrawable.builder()
                    .beginConfig()
                    .endConfig()
                    .buildRoundRect(dynamicText , Color.WHITE ,20);
            seekBar.setThumb(drawable);

        }
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    });
4
user3208599

Ce code suit aligne votre centre TextView sur votre centre de pouce SeekBar. YOUR_TEXT_VIEW width doit être wrap_content in xml.

J'espère que ce code vous aidera.

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
       YOUR_TEXT_VIEW.setText(Integer.toString(progress));
       double pourcent = progress / (double) seekBar.getMax();
       int offset = seekBar.getThumbOffset();
       int seekWidth = seekBar.getWidth();
       int val = (int) Math.round(pourcent * (seekWidth - 2 * offset));
       int labelWidth = YOUR_TEXT_VIEW.getWidth();
       YOUR_TEXT_VIEW.setX(offset + seekBar.getX() + val
                 - Math.round(pourcent * offset)
                 - Math.round(pourcent * labelWidth/2));
}
4
Sigvent

Ça marche pas mal pour moi
il y a un petit hardcode)
s'il vous plaît écrivez les améliorations que smbd peut avoir

import Android.content.Context;
import Android.graphics.Canvas;
import Android.graphics.Color;
import Android.graphics.Paint;
import Android.graphics.Rect;
import Android.support.v7.widget.AppCompatSeekBar;
import Android.util.AttributeSet;
import Android.util.TypedValue;

public class CustomSeekBar extends AppCompatSeekBar {

    @SuppressWarnings("unused")
    private static final String TAG = CustomSeekBar.class.getSimpleName();

    private Paint paint;
    private Rect bounds;

    public String dimension;

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

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

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

    private void init(){
        Paint = new Paint();
        Paint.setColor(Color.WHITE);
        Paint.setStyle(Paint.Style.STROKE);
        Paint.setTextSize(sp2px(14));

        bounds = new Rect();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        String label = String.valueOf(getProgress()) + dimension;
        Paint.getTextBounds(label, 0, label.length(), bounds);
        float x = (float) getProgress() * (getWidth() - 2 * getThumbOffset()) / getMax() +
            (1 - (float) getProgress() / getMax()) * bounds.width() / 2 - bounds.width() / 2
            + getThumbOffset() / (label.length() - 1);
        canvas.drawText(label, x, Paint.getTextSize(), Paint);
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }
}
2
V. Kalyuzhnyu

Le meilleur moyen à l’OMI est de le faire par le code Ce n'est vraiment pas si effrayant et nous sommes tous des programmeurs après tout :)

class ThumbDrawable(context: Context) : Drawable() {

    private val Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val textBounds = Rect()

    private var shadowColor = context.resources.getColor(R.color.wallet_screen_option_shadow)
    private val size = context.resources.getDimensionPixelSize(R.dimen.thumbRadius).toFloat()
    private val textSize = context.resources.getDimensionPixelSize(R.dimen.thumbTextSize).toFloat()
    var progress: Int = 0

    init {
        textPaint.typeface = Typeface.createFromAsset(context.assets, "font/avenir_heavy.ttf")
        val accentColor = context.resources.getColor(R.color.accent)
        Paint.color = accentColor
        textPaint.color = accentColor
        textPaint.textSize = textSize
        Paint.setShadowLayer(size / 2, 0f, 0f, shadowColor)
    }

    override fun draw(canvas: Canvas) {
        Timber.d("bounds: $bounds")
        val progressAsString = progress.toString()
        canvas.drawCircle(bounds.left.toFloat(), bounds.top.toFloat(), size, Paint)
        textPaint.getTextBounds(progressAsString, 0, progressAsString.length, textBounds)
        //0.6f is cause of the avenirs spacing, should be .5 for proper font
        canvas.drawText(progressAsString, bounds.left.toFloat() - textBounds.width() * 0.6f, bounds.top.toFloat() - size * 2, textPaint)
    }

    override fun setAlpha(alpha: Int) {
    }

    override fun getOpacity(): Int {
        return PixelFormat.OPAQUE
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
    }

}

et dans votre implémentation de seekbar

class CustomSeekBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
        SeekBar(context, attrs, defStyleAttr) {

    init {
        thumb = ThumbDrawable(context)
        setPadding(0, 0, 0, 0);
    }

    override fun invalidate() {
        super.invalidate()
        if (thumb is ThumbDrawable) (thumb as ThumbDrawable).progress = progress

    }



}

le résultat final est quelque chose comme ça  seek bar implementation result

0

J'ai créé cet exemple pour montrer comment textview devrait être pris en charge pour différents types de taille d'écran et comment calculer la position réelle de Thumb car la position peut parfois être 0.

public class CustomProgressBar extends RelativeLayout implements AppCompatSeekBar.OnSeekBarChangeListener {

    @BindView(R.id.userProgressBar)
    protected AppCompatSeekBar progressSeekBar;
    @BindView(R.id.textPorcent)
    protected TextView porcent;
    @BindView(R.id.titleIndicator)
    protected TextView title;

    public CustomProgressBar(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.custom_progressbar_view, this, true);
        ButterKnife.bind(this);
        setColors(R.color.green, R.color.progress_bar_remaining);
        progressSeekBar.setPadding(0, 0, 0, 0);
        progressSeekBar.setOnSeekBarChangeListener(this);
    }

    private void setPorcentTextViewPosition(float widthView) {
        int width = CoreUtils.getScreenSize().x;
        float xPosition = ((float) progressSeekBar.getProgress() / 100) * width;
        float finalPosition = xPosition - (widthView / 2f);
        if (width - xPosition < widthView) {
            porcent.setX(width - widthView);
        } else if (widthView < finalPosition) {
            porcent.setX(finalPosition);
        }
    }

    public void setColors(int progressDrawable, int remainingDrawable) {
        LayerDrawable layer = (LayerDrawable) progressSeekBar.getProgressDrawable();
        Drawable background = layer.getDrawable(0);
        Drawable progress = layer.getDrawable(1);

        background.setColorFilter(ContextCompat.getColor(getContext(), remainingDrawable), PorterDuff.Mode.SRC_IN);
        progress.setColorFilter(ContextCompat.getColor(getContext(), progressDrawable), PorterDuff.Mode.SRC_IN);
    }

    public void setValues(int progress, int remaining) {
        int value = (progress * remaining) / 100;
        progressSeekBar.setMax(remaining);
        porcent.setText(String.valueOf(value).concat("%"));

        porcent.post(new Runnable() {
            @Override
            public void run() {
                setPorcentTextViewPosition(porcent.getWidth());
            }
        });
        progressSeekBar.setProgress(value);
    }

    public void setTitle(String title) {
        this.title.setText(title);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
}
0
Diego Cunha