Подтвердить что ты не робот

Круглые края Android на кольцевой панели прогресса

Я пытаюсь сделать круговой индикатор выполнения на андроиде, и это кажется довольно простой задачей, но я борюсь с округлением краев прогресса и дополнительного прогресса.

Есть ли способ сделать это без создания пользовательского представления? Используя радиус углов? или девять патчей доступны?

Для этого представления (см. прикрепление) Я использую простой 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

4b9b3361

Ответ 1

Просто создайте класс 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

не стесняйтесь спрашивать что-нибудь.. Желаем удачи.

[Обновить 23-01-2016]

Наконец, я загрузил код на github. Вы можете отсылать его здесь https://github.com/msquare097/MProgressBar

Теперь вы можете использовать этот ProgressBar, просто написав следующую строку в файле build.gradle приложения. Вам не нужно копировать код выше.

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

Ответ 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

Ответ 3

Простой и эффективный класс, расширяющий представление для рисования кругового прогресса, с закругленными углами в качестве опции. Цвет прогресса, цвет фона, ширина обводки также настраиваются. Как видно из моего другого ответа.

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

Ответ 5

Я смог добиться этого, используя список слоев и добавив точку с каждой стороны линии. Первый будет зависать сверху, а второй будет следить за ходом процесса, когда вставка добавляется внутрь элемента поворота. Вы должны будете отрегулировать его в соответствии с размером макета вашего индикатора выполнения. Мой 250dp x 250dp.

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 here enter image description here