web-dev-qa-db-ja.com

Androidリング型プログレスバーの丸いエッジ

Androidで円形のプログレスバーを作成しようとしていますが、これは非常に簡単な作業のようですが、進行状況と2次進行状況の端を丸めるのに苦労しています。

カスタムビューを作成せずにそれを行う方法はありますか?コーナー半径を使用しますか?または9つのパッチドローアブル?

このビューでは(添付ファイルを参照)、単純なxmlファイルを使用しています

<item Android:id="@Android:id/progress">


    <shape
        Android:useLevel="true"
        Android:innerRadius="@dimen/sixty_dp"
        Android:shape="ring"
        Android:thickness="@dimen/seven_dp">

        <solid Android:color="#477C5B"/>

        <stroke Android:width="1dip"
            Android:color="#FFFF"/>
    </shape>






</item>

enter image description here

24
moujib

パッケージにMyProgressというクラスを作成し、次のコードを貼り付けるだけです。

import Android.content.Context;
import Android.content.res.TypedArray;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.RectF;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.view.View;

public class MyProgress extends View {

    private Paint mPrimaryPaint;
    private Paint mSecondaryPaint;
    private RectF mRectF;
    private TextPaint mTextPaint;
    private Paint mBackgroundPaint;

    private boolean mDrawText = false;

    private int mSecondaryProgressColor;
    private int mPrimaryProgressColor;
    private int mBackgroundColor;

    private int mStrokeWidth;

    private int mProgress;
    private int mSecodaryProgress;

    private int mTextColor;

    private int mPrimaryCapSize;
    private int mSecondaryCapSize;
    private boolean mIsPrimaryCapVisible;
    private boolean mIsSecondaryCapVisible;

    private int x;
    private int y;
    private int mWidth = 0, mHeight = 0;


    public MyProgress(Context context) {
        super(context);
        init(context, null);
    }

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

    public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    void init(Context context, AttributeSet attrs) {
        TypedArray a;
        if (attrs != null) {
            a = context.getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.MyProgress,
                    0, 0);
        } else {
            throw new IllegalArgumentException("Must have to pass the attributes");
        }

        try {
            mDrawText = a.getBoolean(R.styleable.MyProgress_showProgressText, false);

            mBackgroundColor = a.getColor(R.styleable.MyProgress_backgroundColor, Android.R.color.darker_gray);
            mPrimaryProgressColor = a.getColor(R.styleable.MyProgress_progressColor, Android.R.color.darker_gray);
            mSecondaryProgressColor = a.getColor(R.styleable.MyProgress_secondaryProgressColor, Android.R.color.black);

            mProgress = a.getInt(R.styleable.MyProgress_progress, 0);
            mSecodaryProgress = a.getInt(R.styleable.MyProgress_secondaryProgress, 0);

            mStrokeWidth = a.getDimensionPixelSize(R.styleable.MyProgress_strokeWidth, 20);
            mTextColor = a.getColor(R.styleable.MyProgress_textColor, Android.R.color.black);

            mPrimaryCapSize = a.getInt(R.styleable.MyProgress_primaryCapSize, 20);
            mSecondaryCapSize = a.getInt(R.styleable.MyProgress_secodaryCapSize, 20);

            mIsPrimaryCapVisible = a.getBoolean(R.styleable.MyProgress_primaryCapVisibility, true);
            mIsSecondaryCapVisible = a.getBoolean(R.styleable.MyProgress_secodaryCapVisibility, true);
        } finally {
            a.recycle();
        }

        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setStyle(Paint.Style.STROKE);
        mBackgroundPaint.setStrokeWidth(mStrokeWidth);
        mBackgroundPaint.setColor(mBackgroundColor);

        mPrimaryPaint = new Paint();
        mPrimaryPaint.setAntiAlias(true);
        mPrimaryPaint.setStyle(Paint.Style.STROKE);
        mPrimaryPaint.setStrokeWidth(mStrokeWidth);
        mPrimaryPaint.setColor(mPrimaryProgressColor);

        mSecondaryPaint = new Paint();
        mSecondaryPaint.setAntiAlias(true);
        mSecondaryPaint.setStyle(Paint.Style.STROKE);
        mSecondaryPaint.setStrokeWidth(mStrokeWidth - 2);
        mSecondaryPaint.setColor(mSecondaryProgressColor);

        mTextPaint = new TextPaint();
        mTextPaint.setColor(mTextColor);

        mRectF = new RectF();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRectF.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
        mTextPaint.setTextSize(w / 5);
        x = (w / 2) - ((int) (mTextPaint.measureText(mProgress + "%") / 2));
        y = (int) ((h / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
        mWidth = w;
        mHeight = h;
        invalidate();
    }

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

        mPrimaryPaint.setStyle(Paint.Style.STROKE);
        mSecondaryPaint.setStyle(Paint.Style.STROKE);

        // for drawing a full progress .. The background circle
        canvas.drawArc(mRectF, 0, 360, false, mBackgroundPaint);

        // for drawing a secondary progress circle
        int secondarySwipeangle = (mSecodaryProgress * 360) / 100;
        canvas.drawArc(mRectF, 270, secondarySwipeangle, false, mSecondaryPaint);

        // for drawing a main progress circle
        int primarySwipeangle = (mProgress * 360) / 100;
        canvas.drawArc(mRectF, 270, primarySwipeangle, false, mPrimaryPaint);

        // for cap of secondary progress
        int r = (getHeight() - getPaddingLeft() * 2) / 2;      // Calculated from canvas width
        double trad = (secondarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
        int x = (int) (r * Math.cos(trad));
        int y = (int) (r * Math.sin(trad));
        mSecondaryPaint.setStyle(Paint.Style.FILL);
        if (mIsSecondaryCapVisible)
            canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mSecondaryCapSize, mSecondaryPaint);

        // for cap of primary progress
        trad = (primarySwipeangle - 90) * (Math.PI / 180d); // = 5.1051
        x = (int) (r * Math.cos(trad));
        y = (int) (r * Math.sin(trad));
        mPrimaryPaint.setStyle(Paint.Style.FILL);
        if (mIsPrimaryCapVisible)
            canvas.drawCircle(x + (mWidth / 2), y + (mHeight / 2), mPrimaryCapSize, mPrimaryPaint);


        if (mDrawText)
            canvas.drawText(mProgress + "%", x, y, mTextPaint);
    }

    public void setDrawText(boolean mDrawText) {
        this.mDrawText = mDrawText;
        invalidate();
    }

    public void setBackgroundColor(int mBackgroundColor) {
        this.mBackgroundColor = mBackgroundColor;
        invalidate();
    }

    public void setSecondaryProgressColor(int mSecondaryProgressColor) {
        this.mSecondaryProgressColor = mSecondaryProgressColor;
        invalidate();
    }

    public void setPrimaryProgressColor(int mPrimaryProgressColor) {
        this.mPrimaryProgressColor = mPrimaryProgressColor;
        invalidate();
    }

    public void setStrokeWidth(int mStrokeWidth) {
        this.mStrokeWidth = mStrokeWidth;
        invalidate();
    }

    public void setProgress(int mProgress) {
        this.mProgress = mProgress;
        invalidate();
    }

    public void setSecondaryProgress(int mSecondaryProgress) {
        this.mSecodaryProgress = mSecondaryProgress;
        invalidate();
    }

    public void setTextColor(int mTextColor) {
        this.mTextColor = mTextColor;
        invalidate();
    }

    public void setPrimaryCapSize(int mPrimaryCapSize) {
        this.mPrimaryCapSize = mPrimaryCapSize;
        invalidate();
    }

    public void setSecondaryCapSize(int mSecondaryCapSize) {
        this.mSecondaryCapSize = mSecondaryCapSize;
        invalidate();
    }

    public boolean isPrimaryCapVisible() {
        return mIsPrimaryCapVisible;
    }

    public void setIsPrimaryCapVisible(boolean mIsPrimaryCapVisible) {
        this.mIsPrimaryCapVisible = mIsPrimaryCapVisible;
    }

    public boolean isSecondaryCapVisible() {
        return mIsSecondaryCapVisible;
    }

    public void setIsSecondaryCapVisible(boolean mIsSecondaryCapVisible) {
        this.mIsSecondaryCapVisible = mIsSecondaryCapVisible;
    }


    public int getSecondaryProgressColor() {
        return mSecondaryProgressColor;
    }

    public int getPrimaryProgressColor() {
        return mPrimaryProgressColor;
    }

    public int getProgress() {
        return mProgress;
    }

    public int getBackgroundColor() {
        return mBackgroundColor;
    }

    public int getSecodaryProgress() {
        return mSecodaryProgress;
    }

    public int getPrimaryCapSize() {
        return mPrimaryCapSize;
    }

    public int getSecondaryCapSize() {
        return mSecondaryCapSize;
    }
}

次の行をres-> values-> attr.xmlのタグの下に追加してビルドします

<declare-styleable name="MyProgress">
    <attr name="showProgressText" format="boolean" />
    <attr name="progress" format="integer" />
    <attr name="secondaryProgress" format="integer" />
    <attr name="progressColor" format="color" />
    <attr name="secondaryProgressColor" format="color" />
    <attr name="backgroundColor" format="color" />
    <attr name="primaryCapSize" format="integer" />
    <attr name="secodaryCapSize" format="integer" />
    <attr name="primaryCapVisibility" format="boolean" />
    <attr name="secodaryCapVisibility" format="boolean" />
    <attr name="strokeWidth" format="dimension" />
    <attr name="textColor" format="color" />
</declare-styleable>

それはそれです....そしてあなたのレイアウトで使用するために..

<Your_Package_Name.MyProgress
    Android:padding="20dp"
    Android:id="@+id/timer1"
    app:strokeWidth="10dp"
    app:progress="30"
    app:secondaryProgress="50"
    app:backgroundColor="@Android:color/black"
    app:progressColor="@Android:color/holo_blue_bright"
    app:secondaryProgressColor="@Android:color/holo_blue_dark"
    app:primaryCapSize="30"
    app:secodaryCapSize="40"
    app:primaryCapVisibility="true"
    app:secodaryCapVisibility="true"
    Android:layout_width="200dp"
    Android:layout_height="200dp" />

setMethods()...を使用してすべてのプロパティをプログラムで変更することもできます enter image description here

何でも気軽に聞いてください..幸運を祈ります

[2016年1月23日更新]

最後に、githubにコードをアップロードしました。ここから参照できます https://github.com/msquare097/MProgressBar

これで、アプリのbuild.gradleファイルに次の行を書き込むだけでこのProgressBarを使用できます。 上記のコードをコピーする必要はありません。

compile 'com.msquare.widget.mprogressbar:mprogressbar:1.0.0'
22
Moinkhan

これは、レイヤーリストを使用し、線の両側にドットを追加することで実現できました。 1つ目は上部に固定され、2つ目は回転要素内にインセットが追加されると進行状況を追跡します。ただし、プログレスバーのレイアウトのサイズに応じて調整する必要があります。鉱山は250dpx250dpです。

progress_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item>
        <rotate Android:fromDegrees="270" Android:toDegrees="270">
        <shape
            Android:innerRadiusRatio="2.55"
            Android:shape="ring"
            Android:thickness="15dp"
            Android:useLevel="true">
            <solid Android:color="@color/main_color" />
        </shape>
        </rotate>
    </item>
    <item Android:bottom="211dp">
        <shape
            Android:innerRadiusRatio="1000"
            Android:shape="ring"
            Android:thickness="7dp"
            Android:useLevel="false">
            <solid Android:color="@color/main_color" />
        </shape>
    </item>
    <item>
        <rotate>
            <inset Android:insetBottom="211dp">
                <shape
                    Android:innerRadiusRatio="1000"
                    Android:shape="ring"
                    Android:thickness="7dp"
                    Android:useLevel="false">
                    <solid Android:color="@color/main_color" />
                </shape>
            </inset>
        </rotate>
    </item>
</layer-list>

ここには二次的なプロセスはありませんが、それに合わせて調整できるはずです。

enter image description hereenter image description here

2
Jon

オプションとして角を丸くして、Viewを拡張して円形の進行状況を描画するシンプルで効率的なクラス。プログレスカラー、背景色、ストローク幅もカスタマイズ可能です。 私の他の答え に見られるように。

import Android.content.Context
import Android.graphics.Canvas
import Android.graphics.Paint
import Android.graphics.RectF
import Android.util.AttributeSet
import Android.view.View
import androidx.annotation.FloatRange

class CircularProgressView : View {
  constructor(context: Context) : super(context)
  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

  private val progressPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    style = Paint.Style.STROKE
  }
  private val backgroundPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    style = Paint.Style.STROKE
  }

  private val rect = RectF()
  private val startAngle = -90f
  private val maxAngle = 360f
  private val maxProgress = 100

  private var diameter = 0f
  private var angle = 0f

  override fun onDraw(canvas: Canvas) {
    drawCircle(maxAngle, canvas, backgroundPaint)
    drawCircle(angle, canvas, progressPaint)
  }

  override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
    diameter = Math.min(width, height).toFloat()
    updateRect()
  }

  private fun updateRect() {
    val strokeWidth = backgroundPaint.strokeWidth
    rect.set(strokeWidth, strokeWidth, diameter - strokeWidth, diameter - strokeWidth)
  }

  private fun drawCircle(angle: Float, canvas: Canvas, Paint: Paint) {
    canvas.drawArc(rect, startAngle, angle, false, Paint)
  }

  private fun calculateAngle(progress: Float) = maxAngle / maxProgress * progress

  fun setProgress(@FloatRange(from = 0.0, to = 100.0) progress: Float) {
    angle = calculateAngle(progress)
    invalidate()
  }

  fun setProgressColor(color: Int) {
    progressPaint.color = color
    invalidate()
  }

  fun setProgressBackgroundColor(color: Int) {
    backgroundPaint.color = color
    invalidate()
  }

  fun setProgressWidth(width: Float) {
    progressPaint.strokeWidth = width
    backgroundPaint.strokeWidth = width
    updateRect()
    invalidate()
  }

  fun setRounded(rounded: Boolean) {
    progressPaint.strokeCap = if (rounded) Paint.Cap.ROUND else Paint.Cap.BUTT
    invalidate()
  }
}
2
EyesClear

以下のコードのように、リングの端と開始側に円/楕円形を追加できます

レイヤーリストドローアブルには、2つの円/楕円形ドローアブルとリング形状ドローアブルが含まれます

round_progress_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item
        Android:bottom="294px"
        Android:left="540px"
        Android:right="48px"
        Android:top="294px">
        <shape
            Android:innerRadius="6px"
            Android:shape="oval">
            <solid Android:color="#FFFFFF"/>
        </shape>
    </item>
    <item>
        <shape
            Android:innerRadius="240px"
            Android:shape="ring"
            Android:thickness="12px">
            <size
                Android:width="600px"
                Android:height="600px"/>
            <solid Android:color="#FFFFFF"/>
        </shape>
    </item>
    <item>
        <rotate>
            <layer-list>
                <item
                    Android:bottom="294px"
                    Android:left="540px"
                    Android:right="48px"
                    Android:top="294px">
                    <shape
                        Android:innerRadius="6px"
                        Android:shape="oval">
                        <solid Android:color="#FFFFFF"/>
                    </shape>
                </item>
            </layer-list>
        </rotate>
    </item>
</layer-list>

round_progress_animation_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item Android:state_window_focused="true">
        <objectAnimator xmlns:Android="http://schemas.Android.com/apk/res/Android"
            Android:duration="5000"
            Android:propertyName="ImageLevel"
            Android:repeatCount="infinite"
            Android:valueFrom="0"
            Android:valueTo="10000"
            Android:valueType="intType">

        </objectAnimator>
    </item>
    <item>
        <objectAnimator xmlns:Android="http://schemas.Android.com/apk/res/Android"
            Android:duration="0"
            Android:propertyName="ImageLevel"
            Android:valueFrom="0"
            Android:valueTo="0"
            Android:valueType="intType">

        </objectAnimator>
    </item>
</selector>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/colorPrimaryDark"
    tools:context=".MainActivity">


    <ImageView
        Android:stateListAnimator="@animator/round_progress_animation_selector"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:src="@drawable/round_progress_drawable" />

</FrameLayout>

demo

1
Yessy