web-dev-qa-db-fra.com

Comment créer une barre de progression circulaire dans Android qui tourne dessus?

J'essaie de créer une barre de progression arrondie. C'est ce que je veux réaliser

Il y a un anneau de fond de couleur grise. En plus de cela, une barre de progression de couleur bleue apparaît, qui se déplace dans une trajectoire circulaire de 0 à 360 en 60 ou quel que soit le nombre de secondes.

enter image description here

Voici mon exemple de code.

<ProgressBar
            Android:id="@+id/ProgressBar"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            style="?android:attr/progressBarStyleLarge"
            Android:indeterminateDrawable="@drawable/progressBarBG"
            Android:progress="50"
            />

Pour ce faire, je crée une liste de couches dans la barre de progression "progressBarBG". Dans cette liste, je donne deux éléments comme indiqué.

<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
<item Android:id="@Android:id/background">
    <shape
            Android:shape="ring"
            Android:innerRadius="64dp"
            Android:thickness="8dp"
            Android:useLevel="false">

        <solid Android:color="@color/grey" />
    </shape>
</item>

<item Android:id="@Android:id/progress">
    <clip>
        <shape
                Android:shape="ring"
                Android:innerRadius="64dp"
                Android:thickness="8dp"
                Android:useLevel="false">

            <solid Android:color="@color/blue" />
        </shape>
    </clip>
</item>

Maintenant, le premier anneau gris est bien généré. L'anneau bleu commence cependant à gauche du dessinable et va à droite, tout comme le fonctionnement d'une barre de progression linéaire. C’est ainsi qu’il affiche une progression de 50% avec la flèche de couleur rouge indiquant la direction.

enter image description here

Je souhaite déplacer la barre de progression bleue dans le tracé circulaire comme prévu.

123
Saurav Srivastava

Voici mes deux solutions.

Réponse courte:

Au lieu de créer un layer-list, je l'ai séparé en deux fichiers. Un pour ProgressBar et un pour son arrière-plan.

Ceci est le fichier ProgressDrawable (dossier @drawable): circular_progress_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:fromDegrees="270"
    Android:toDegrees="270">
    <shape
        Android:innerRadiusRatio="2.5"
        Android:shape="ring"
        Android:thickness="1dp"
        Android:useLevel="true"><!-- this line fixes the issue for Lollipop api 21 -->

        <gradient
            Android:angle="0"
            Android:endColor="#007DD6"
            Android:startColor="#007DD6"
            Android:type="sweep"
            Android:useLevel="false" />
    </shape>
</rotate>

Et ceci pour sa background (@ dossier dessnable): circle_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:shape="ring"
    Android:innerRadiusRatio="2.5"
    Android:thickness="1dp"
    Android:useLevel="false">

    <solid Android:color="#CCC" />

</shape>

Et à la fin, dans la mise en page que vous travaillez:

<ProgressBar
        Android:id="@+id/progressBar"
        Android:layout_width="200dp"
        Android:layout_height="200dp"
        Android:indeterminate="false"
        Android:progressDrawable="@drawable/circular_progress_bar"
        Android:background="@drawable/circle_shape"
        style="?android:attr/progressBarStyleHorizontal"
        Android:max="100"
        Android:progress="65" />

Voici le résultat:

result 1

Réponse longue:

Utilisez une vue personnalisée qui hérite du Android.view.View

result 2

Voici le projet complet sur github

267
M. Reza Nasirloo

J'ai fait de manière simple:

S'il vous plaît vérifier la capture d'écran pour la même chose.

CustomProgressBarActivity.Java :

public class CustomProgressBarActivity extends AppCompatActivity {

    private TextView txtProgress;
    private ProgressBar progressBar;
    private int pStatus = 0;
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_progressbar);

        txtProgress = (TextView) findViewById(R.id.txtProgress);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (pStatus <= 100) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            progressBar.setProgress(pStatus);
                            txtProgress.setText(pStatus + " %");
                        }
                    });
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    pStatus++;
                }
            }
        }).start();

    }
}

activity_custom_progressbar.xml :

<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:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.skholingua.Android.custom_progressbar_circular.MainActivity" >


    <RelativeLayout
        Android:layout_width="wrap_content"
        Android:layout_centerInParent="true"
        Android:layout_height="wrap_content">

        <ProgressBar
            Android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            Android:layout_width="250dp"
            Android:layout_height="250dp"
            Android:layout_centerInParent="true"
            Android:indeterminate="false"
            Android:max="100"
            Android:progress="0"
            Android:progressDrawable="@drawable/custom_progressbar_drawable"
            Android:secondaryProgress="0" />


        <TextView
            Android:id="@+id/txtProgress"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_alignBottom="@+id/progressBar"
            Android:layout_centerInParent="true"
            Android:textAppearance="?android:attr/textAppearanceSmall" />
    </RelativeLayout>



</RelativeLayout>

custom_progressbar_drawable.xml :

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:fromDegrees="-90"
    Android:pivotX="50%"
    Android:pivotY="50%"
    Android:toDegrees="270" >

    <shape
        Android:shape="ring"
        Android:useLevel="false" >
        <gradient
            Android:centerY="0.5"
            Android:endColor="#FA5858"
            Android:startColor="#0099CC"
            Android:type="sweep"
            Android:useLevel="false" />
    </shape>

</rotate>

J'espère que cela vous aidera.

16
Hiren Patel

J'ai écrit des exemples détaillés sur barre de progression circulaire sous Android ici sur mon blog demonuts.com . Vous pouvez aussi y trouver le code source complet et l'explication.

Voici comment j'ai créé progressbar circulaire avec pourcentage à l'intérieur du cercle en code pur sans aucune bibliothèque.

enter image description here

commencez par créer un fichier dessinable appelé circular.xml

<?xml version="1.0" encoding="utf-8"?>

<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item Android:id="@Android:id/secondaryProgress">
        <shape
            Android:innerRadiusRatio="6"
            Android:shape="ring"
            Android:thicknessRatio="20.0"
            Android:useLevel="true">


            <gradient
                Android:centerColor="#999999"
                Android:endColor="#999999"
                Android:startColor="#999999"
                Android:type="sweep" />
        </shape>
    </item>

    <item Android:id="@Android:id/progress">
        <rotate
            Android:fromDegrees="270"
            Android:pivotX="50%"
            Android:pivotY="50%"
            Android:toDegrees="270">

            <shape
                Android:innerRadiusRatio="6"
                Android:shape="ring"
                Android:thicknessRatio="20.0"
                Android:useLevel="true">


                <rotate
                    Android:fromDegrees="0"
                    Android:pivotX="50%"
                    Android:pivotY="50%"
                    Android:toDegrees="360" />

                <gradient
                    Android:centerColor="#00FF00"
                    Android:endColor="#00FF00"
                    Android:startColor="#00FF00"
                    Android:type="sweep" />

            </shape>
        </rotate>
    </item>
</layer-list>

Maintenant, dans votre activity_main.xml, ajoutez ce qui suit:

  <?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:background="@color/dialog"
    tools:context="com.example.parsaniahardik.progressanimation.MainActivity">

    <ProgressBar

        Android:id="@+id/circularProgressbar"
        style="?android:attr/progressBarStyleHorizontal"
        Android:layout_width="250dp"
        Android:layout_height="250dp"
        Android:indeterminate="false"
        Android:max="100"
        Android:progress="50"
        Android:layout_centerInParent="true"
        Android:progressDrawable="@drawable/circular"
        Android:secondaryProgress="100"
        />

    <ImageView
        Android:layout_width="90dp"
        Android:layout_height="90dp"
        Android:background="@drawable/whitecircle"
        Android:layout_centerInParent="true"/>

    <TextView
        Android:id="@+id/tv"
        Android:layout_width="250dp"
        Android:layout_height="250dp"
        Android:gravity="center"
        Android:text="25%"
        Android:layout_centerInParent="true"
        Android:textColor="@color/colorPrimaryDark"
        Android:textSize="20sp" />

</RelativeLayout>

Dans activity_main.xml, j'ai utilisé une image circulaire avec un fond blanc pour afficher un fond blanc autour du pourcentage. Voici l'image:

enter image description here

Vous pouvez changer la couleur de cette image pour définir une couleur personnalisée autour du texte en pourcentage.

Enfin, ajoutez enfin le code suivant à MainActivity.Java:

import Android.content.res.Resources;
import Android.graphics.drawable.Drawable;
import Android.os.Handler;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.view.animation.DecelerateInterpolator;
import Android.widget.ProgressBar;
import Android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    int pStatus = 0;
    private Handler handler = new Handler();
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Resources res = getResources();
        Drawable drawable = res.getDrawable(R.drawable.circular);
        final ProgressBar mProgress = (ProgressBar) findViewById(R.id.circularProgressbar);
        mProgress.setProgress(0);   // Main Progress
        mProgress.setSecondaryProgress(100); // Secondary Progress
        mProgress.setMax(100); // Maximum Progress
        mProgress.setProgressDrawable(drawable);

      /*  ObjectAnimator animation = ObjectAnimator.ofInt(mProgress, "progress", 0, 100);
        animation.setDuration(50000);
        animation.setInterpolator(new DecelerateInterpolator());
        animation.start();*/

        tv = (TextView) findViewById(R.id.tv);
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while (pStatus < 100) {
                    pStatus += 1;

                    handler.post(new Runnable() {

                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            mProgress.setProgress(pStatus);
                            tv.setText(pStatus + "%");

                        }
                    });
                    try {
                        // Sleep for 200 milliseconds.
                        // Just to display the progress slowly
                        Thread.sleep(8); //thread will take approx 1.5 seconds to finish
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

Si vous voulez faire une barre de progression horizontale, suivez ce lien, il contient de nombreux exemples intéressants avec le code source:
http://www.skholingua.com/Android-basic/user-interface/form-widgets/progressbar

16
Parsania Hardik

J'ai réalisé une bibliothèque Open Source sur GitHub CircularProgressBar qui fait exactement ce que vous voulez de la manière la plus simple possible:

Gif demo CircularProgressBar

USAGE

Pour créer une ProgressBar circulaire, ajoutez CircularProgressBar dans votre présentation XML et ajoutez la bibliothèque CircularProgressBar dans votre projecteur ou vous pouvez également le saisir via Gradle:

compile 'com.mikhaellopez:circularprogressbar:1.0.0'

XML

<com.mikhaellopez.circularprogressbar.CircularProgressBar
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    app:background_progressbar_color="#FFCDD2"
    app:background_progressbar_width="5dp"
    app:progressbar_color="#F44336"
    app:progressbar_width="10dp" />

Vous devez utiliser les propriétés suivantes dans votre XML pour modifier votre CircularProgressBar.

Propriétés:

  • app:progress (entier) >> valeur par défaut 0
  • app:progressbar_color (couleur) >> défaut NOIR
  • app:background_progressbar_color (couleur) >> GRIS par défaut
  • app:progressbar_width (dimension) >> défaut 7dp
  • app:background_progressbar_width (dimension) >> 3dp par défaut

Java

CircularProgressBar circularProgressBar = (CircularProgressBar)findViewById(R.id.yourCircularProgressbar);
circularProgressBar.setColor(ContextCompat.getColor(this, R.color.progressBarColor));
circularProgressBar.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundProgressBarColor));
circularProgressBar.setProgressBarWidth(getResources().getDimension(R.dimen.progressBarWidth));
circularProgressBar.setBackgroundProgressBarWidth(getResources().getDimension(R.dimen.backgroundProgressBarWidth));
int animationDuration = 2500; // 2500ms = 2,5s
circularProgressBar.setProgressWithAnimation(65, animationDuration); // Default duration = 1500ms

Téléchargez ou branchez cette bibliothèque ici >> https://github.com/lopspower/CircularProgressBar

12
lopez.mikhael

Je suis nouveau, je ne peux donc pas commenter, mais j'ai pensé partager le correctif paresseux. J'utilise également l'approche originale de Pedram et je viens de rencontrer le même problème avec Lollipop. Mais alanv dans un autre post avait un correctif d'une ligne. C'est une sorte de bogue ou de surveillance dans API21. Littéralement, ajoutez simplement Android:useLevel="true" à votre progression xml. La nouvelle approche de Pedram reste le correctif approprié, mais je pensais simplement partager le correctif paresseux.

5
Miao Liu

https://github.com/passsy/Android-HoloCircularProgressBar est un exemple de bibliothèque qui effectue cela. Comme Tenfour04 l'a déclaré, il devra être quelque peu personnalisé, en ce sens que cela n'est pas directement pris en charge. Si cette bibliothèque ne se comporte pas comme vous le souhaitez, vous pouvez la modifier et en modifier les détails pour la faire fonctionner à votre guise. Si vous implémentez quelque chose que d'autres peuvent ensuite réutiliser, vous pouvez même envoyer une demande d'extraction pour que cette opération soit fusionnée!

3
Travis

essayez cette méthode pour créer un bitmap et le définir en mode image.

private void circularImageBar(ImageView iv2, int i) {


    Bitmap b = Bitmap.createBitmap(300, 300,Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(b); 
    Paint paint = new Paint();

        Paint.setColor(Color.parseColor("#c4c4c4"));
        Paint.setStrokeWidth(10);
        Paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(150, 150, 140, Paint);

        Paint.setColor(Color.parseColor("#FFDB4C"));
        Paint.setStrokeWidth(10);   
        Paint.setStyle(Paint.Style.FILL);
        final RectF oval = new RectF();
        Paint.setStyle(Paint.Style.STROKE);
        oval.set(10,10,290,290);

        canvas.drawArc(oval, 270, ((i*360)/100), false, Paint);
        Paint.setStrokeWidth(0);    
        Paint.setTextAlign(Align.CENTER);
        Paint.setColor(Color.parseColor("#8E8E93")); 
        Paint.setTextSize(140);

        canvas.drawText(""+i, 150, 150+(Paint.getTextSize()/3), Paint); 

        iv2.setImageBitmap(b);
}
3
mani

Voici une vue personnalisée simple pour la progression du cercle d'affichage. Vous pouvez modifier et optimiser davantage en fonction de votre projet.

class CircleProgressBar @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private val backgroundWidth = 10f
    private val progressWidth = 20f

    private val backgroundPaint = Paint().apply {
        color = Color.LTGRAY
        style = Paint.Style.STROKE
        strokeWidth = backgroundWidth
        isAntiAlias = true
    }

    private val progressPaint = Paint().apply {
        color = Color.RED
        style = Paint.Style.STROKE
        strokeWidth = progressWidth
        isAntiAlias = true
    }

    var progress: Float = 0f
        set(value) {
            field = value
            invalidate()
        }

    private val oval = RectF()
    private var centerX: Float = 0f
    private var centerY: Float = 0f
    private var radius: Float = 0f

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        centerX = w.toFloat() / 2
        centerY = h.toFloat() / 2
        radius = w.toFloat() / 2 - progressWidth
        oval.set(centerX - radius,
                centerY - radius,
                centerX + radius,
                centerY + radius)
        super.onSizeChanged(w, h, oldw, oldh)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawCircle(centerX, centerY, radius, backgroundPaint)
        canvas?.drawArc(oval, 270f, 360f * progress, false, progressPaint)
    }
}

Exemple d'utilisation

xml

<com.example.androidcircleprogressbar.CircleProgressBar
    Android:id="@+id/circle_progress"
    Android:layout_width="200dp"
    Android:layout_height="200dp" />

kotlin

class MainActivity : AppCompatActivity() {
    val TOTAL_TIME = 10 * 1000L

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        timeOutRemoveTimer.start()
    }

    private var timeOutRemoveTimer = object : CountDownTimer(TOTAL_TIME, 10) {
        override fun onFinish() {
            circle_progress.progress = 1f
        }

        override fun onTick(millisUntilFinished: Long) {
            circle_progress.progress = (TOTAL_TIME - millisUntilFinished).toFloat() / TOTAL_TIME
        }
    }
}

Résultat

2
Phan Van Linh

Changement

Android:useLevel="false"

à

Android:useLevel="true"

pour un deuxième sahpe avec id="@Android:id/progress

esperons que ça marche

1
ASHMIL HUSSAIN K

@Pedram, votre ancienne solution fonctionne aussi très bien dans Lollipop (et mieux que la nouvelle car elle est utilisable partout, y compris dans les vues distantes), il suffit de changer votre code circular_progress_bar.xml en ceci:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:fromDegrees="270"
    Android:toDegrees="270">
    <shape
        Android:innerRadiusRatio="2.5"
        Android:shape="ring"
        Android:thickness="1dp"
        Android:useLevel="true"> <!-- Just add this line -->
        <gradient
            Android:angle="0"
            Android:endColor="#007DD6"
            Android:startColor="#007DD6"
            Android:type="sweep"
            Android:useLevel="false" />
    </shape>
</rotate>
1
mim
package com.example.ankitrajpoot.myapplication;

import Android.app.Activity;
import Android.os.Bundle;
import Android.view.View;
import Android.widget.ProgressBar;


public class MainActivity extends Activity {

    private ProgressBar spinner;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        spinner=(ProgressBar)findViewById(R.id.progressBar);
        spinner.setVisibility(View.VISIBLE);
    }
}

xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/loadingPanel"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:gravity="center">

<ProgressBar
    Android:id="@+id/progressBar"

    Android:layout_width="48dp"
    style="?android:attr/progressBarStyleLarge"
    Android:layout_height="48dp"
    Android:indeterminateDrawable="@drawable/circular_progress_bar"
    Android:indeterminate="true" />
</RelativeLayout>





<?xml version="1.0" encoding="utf-8"?>
<rotate
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:pivotX="50%"
    Android:pivotY="50%"
    Android:fromDegrees="0"
    Android:toDegrees="1080">
    <shape
        Android:shape="ring"
        Android:innerRadiusRatio="3"
        Android:thicknessRatio="8"
        Android:useLevel="false">

        <size
            Android:width="56dip"
            Android:height="56dip" />

        <gradient
            Android:type="sweep"
            Android:useLevel="false"
            Android:startColor="@Android:color/transparent"
            Android:endColor="#1e9dff"
            Android:angle="0"
            />

    </shape>
</rotate>
1
Ankit Rajpoot