web-dev-qa-db-ja.com

Android SeekBarサムにダイナミックテキストを追加する

Android 2.2でこのカスタムSeekBarを作成しようとしていますが、私が行うすべてのことは間違っているようです!SeekBarのサムイメージ上にシークバーの値を表示しようとしています。誰かこれについていくつかの経験がありますか?

私は、親指をカスタマイズするためのより多くの可能性を提供する別のアプローチに従いました。最終的な出力は次のようになります。

enter image description here

まず、サムドローアブルとして設定されるレイアウトをデザインする必要があります。

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>
_

ここで_seekbar_thumb_size_は、要件に応じて任意の小さなサイズにすることができます。ここでは_30dp_を使用しました。背景には、任意のドローアブル/アイコンを使用できます。

次に、このビューをサムドローアブルとして設定する必要があるので、次のコードで取得します。

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

ここでは、このビューをonCreate()で初期化することをお勧めします。そのため、何度もビューを膨らませる必要はありません。

シークバーの進行状況が変更されたときに、このビューをサムドローアブルとして設定します。コードに次のメソッドを追加します。

_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);
}
_

次に、このメソッドを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) {

            }
        });
_

注:また、seekBarを初期化するときにgetThumb()メソッドを呼び出して、デフォルト値で初期化します。

このアプローチを使用すると、進行状況の変更に関するカスタムビューを使用できます。

22
Chintan Shah

私はすでに基本クラスを拡張していると思いますので、あなたは次のようなものを持っています:

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);
  }
}

次に、onDrawメソッドを独自のコードでオーバーライドします。以下を挿入します。

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

ここで、サムの近くにテキストを描画したいのですが、サムのx位置を取得する便利な方法はありません。少し計算が必要です。

@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
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);
            }
        });
8
Chirag Darji

ちょっと私は別の解決策を見つけました、もっと簡単に見えます:

 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

これは私のために働いた

@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

次のコードは、TextViewセンターをSeekBarサムセンターに合わせます。 YOUR_TEXT_VIEW widthは、xmlではwrap_contentでなければなりません。

このコードがお役に立てば幸いです。

@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));
}
5
Sigvent

このライブラリを使用してドローアブルテキストビューを作成し、そのドローアブルをプログラムで親指に入れました。

https://github.com/amulyakhare/TextDrawable

コードは次のようなものです:

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

私にとって悪くない作品
少しハードコードがあります)
smbdの改善点を書いてください

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
Vlad

IMOの最良の方法は、コードを通じてそれを行うことです。それは本当にそれほど怖くはなく、結局のところ私たちはすべてプログラマーです:)

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?) {
    }

}

シークバーの実装で

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

    }



}

最終的な結果はこのようなものです seek bar implementation result

0

この例を作成して、テキストビューをさまざまなタイプの画面サイズでサポートする方法と、Thumbの実際の位置を計算する方法を示しました。

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

TextViewをレイアウトに追加します。 onSeekBarChangeListenerを追加します。

テキストがシークバーのつまみの真ん中にあるように、精度を求めます。少し計算する必要があります。これは、テキストの幅が異なるためです。たとえば、0から150までの数値を表示したいとします。幅188は111とは異なります。このため、表示しているテキストは常に傾いています。

それを解決する方法は、テキストの幅を測定し、それをシークバーのつまみの幅から削除し、2で割り、それを受け入れられた回答で与えられた結果に追加することです。これで、数値範囲の大きさは気になりません。これがコードです:

 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                    val value = progress * (seekBar.width - 2 * seekBar.thumbOffset) / seekBar.max
                    label.text = progress.toString()
                    label.measure(0, 0)
                    val textWidth =  label.measuredWidth
                    val firstRemainder = seekThumbWidth - textWidth
                    val result = firstRemainder / 2
                    label.x = (seekBar.x + value + result)
                }
0
Sermilion