画像インジケータ付きの進行状況バー

エフェクト画像

ここに写真の説明を書いてください

原理分析

  1. カスタムビューのカスタムxml設定の属性値を取得します。
  2. プログレスバーの開始位置と終了位置のx値とy値、および描画された画像インジケーターの左、上、右、下の位置の値を決定します。
  3. 外部使用の進捗状況を設定する方法で渡された値を取得します。
  4. ValueAnimatorを介して表示効果を動的に更新し、ニーズを満たすアニメーション補間器を作成します。

コード

  • ビューのxml属性の設定をカスタマイズしvaluesフォルダーの下のattrs.xmlファイル次の属性を宣言します
<declare-styleable name="ProgressScore">
        <!-- 进度条的高度 -->
        <attr name="progressHeight" format="dimension" />
        <!-- 进度条的背景色 -->
        <attr name="progressColorBack" format="color" />
        <!-- 进度条的前景色 -->
        <attr name="progressColorFore" format="color" />
        <!-- 进度条的数据最大值 -->
        <attr name="progressMaxValue" format="integer" />
    </declare-styleable>
  • カスタムビューでカスタムxml属性の値を読み取ります。そうでない場合は、デフォルト値を使用します
/**
     * 初始化读取xml自定义属性
     */
    private void initDefineAttrs(Context context, AttributeSet attrs) {

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressScore);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {

            int attr = array.getIndex(i);
            switch (attr) {

                case R.styleable.ProgressScore_progressHeight:

                    progressHeight = array.getDimension(attr, DEFAULT_PROGRESS_HEIGHT);
                    break;

                case R.styleable.ProgressScore_progressColorFore:

                    colorProgressFore = array.getColor(attr, ContextCompat.getColor(context, R.color.colorWhite));
                    break;

                case R.styleable.ProgressScore_progressColorBack:

                    colorProgressBack = array.getColor(attr, ContextCompat.getColor(context, R.color.white_alpha_2));
                    break;

                case R.styleable.ProgressScore_progressMaxValue:

                    progressMaxValue = array.getInt(attr, DEFAULT_PROGRESS_MAX_VALUE);
                    break;
            }
        }

        array.recycle();
    }
  • onSizeChanged()メソッドでは、進行状況バーの開始位置と終了位置の座標が決定され、インジケーターの座標が決定され、各値に対応する進行状況バーの幅が決定されます。
@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        progressStartX = progressStartX + progressHeight / 2;
        progressEndX = w - DEFAULT_PROGRESS_MARGIN_START_END - progressHeight / 2;
        progressY = (float) (h - (progressHeight / 2.0));
        progressSingleWidth = (progressEndX - progressStartX) / progressMaxValue;

        bottomBg = (int) (progressY - progressHeight);
        topBg = bottomBg - heightBg;
        topHead = (int) (topBg + DEFAULT_HEAD_MARGIN);

    }
  • onDraw()メソッドで進行状況バーとインジケーターを描画します。
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawProgressBar(canvas);

        leftBg = (int) (progressCurrent - widthBg / 2);
        rightBg = leftBg + widthBg;
        leftHead = (int) (leftBg + DEFAULT_HEAD_MARGIN);
        rightHead = (int) (rightBg - DEFAULT_HEAD_MARGIN);
        bottomHead = (int) (topHead + (rightBg - leftBg - DEFAULT_HEAD_MARGIN * 2));

        drawIndicator(canvas);
    }
  • アイコンと進行状況バーを移動させる方法は、ValueAnimatorを介してデータを更新するためにアルゴリズム補間器をカスタマイズし、最新の値を取得して、Animator.addUpdateListener()メソッドでインターフェイスを更新することです。
/**
     * 插值器
     * */
    private class PositiveEvaluator implements TypeEvaluator<Float> {
    
    

        @Override
        public Float evaluate(float v, Float startValue, Float endValue) {

            return startValue + v * (endValue - startValue);
        }
    }

/**
     * 数据为正数的时候动画
     */
    public void doAnimation(float startX, long delay, long duration) {

        float endX = startX + progressSingleWidth * progress;
        ValueAnimator animator = ValueAnimator.ofObject(new PositiveEvaluator(), startX, endX);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                progressCurrent = (float) valueAnimator.getAnimatedValue();

                invalidate();

            }
        });

        animator.setStartDelay(delay);
        animator.setDuration(duration);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }
  • このカスタムビューで使用されるメソッドの説明。

1.インジケーター画像メソッドsetBitmapIndicatorHead()を設定します

/**
     * 通过本地图片资源id设置
     * */
    public void setBitmapIndicatorHead(int resId) {

        //toRoundBitmap()是将图片裁剪成圆形方法
        this.bitmapIndicatorHead = toRoundBitmap(BitmapFactory.decodeResource(getResources(), resId));
    }

    /**
     * 直接传入bitmap
     * */
    public void setBitmapIndicatorHead(Bitmap bitmapIndicatorHead) {
        this.bitmapIndicatorHead = toRoundBitmap(bitmapIndicatorHead);
    }

2.進行状況の設定方法setProgress(int progress)

public void setProgress(int progress) {

        if (progress < 0) {

            this.progress = 0;
        } else if (progress > DEFAULT_PROGRESS_MAX_VALUE) {

            this.progress = DEFAULT_PROGRESS_MAX_VALUE;
        } else {
            this.progress = progress;
        }
    }

3.アニメーションメソッドstartAnimation()を呼び出します

public void startAnim() {

        doAnimation(progressStartX);
    }

4.進行状況を設定し、アニメーションメソッドsetProgressAndStartAnim(int progress)を呼び出します

public void setProgressAndStartAnim(int progress) {

        if (progress < 0) {

            this.progress = 0;
            Toast.makeText(mContext, "数值过小", Toast.LENGTH_SHORT).show();
        } else if (progress > DEFAULT_PROGRESS_MAX_VALUE) {

            this.progress = DEFAULT_PROGRESS_MAX_VALUE;
            Toast.makeText(mContext, "数值过大", Toast.LENGTH_SHORT).show();
        } else {

            this.progress = progress;
        }

        doAnimation(progressStartX, 200, 800);
    }

完全なカスタムビューコード

カスタム属性attrs.xmlファイルコード

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ProgressScore">
        <!-- 进度条的高度 -->
        <attr name="progressHeight" format="dimension" />
        <!-- 进度条的背景色 -->
        <attr name="progressColorBack" format="color" />
        <!-- 进度条的前景色 -->
        <attr name="progressColorFore" format="color" />
        <!-- 进度条的数据最大值 -->
        <attr name="progressMaxValue" format="integer" />
    </declare-styleable>
</resources>

ビューコードをカスタマイズします。プログレスバーで使用されている背景色、前景色、インジケーターの水滴の画像は送信されません。必要に応じて、自分で見つけて置き換えることができます。

package customview;

import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.Toast;

import com.example.zpf.animmenu.R;


/**
 * Created by zpf on 2017/7/25.
 * 积分页面的打败..%用户进度条
 */

public class ProgressScore extends View {
    
    

    /**
     * 默认的进度条的高度
     */
    private final float DEFAULT_PROGRESS_HEIGHT = TypedValue
            .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics());
    /**
     * 默认的进度条距离控件左边右边的margin
     */
    private final float DEFAULT_PROGRESS_MARGIN_START_END = TypedValue
            .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics());
    /**
     * 用户头像距离背景指示器的边距
     */
    private final float DEFAULT_HEAD_MARGIN = TypedValue
            .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics());

    /**
     * 默认的进度条的最大数据值为100
     */
    private final int DEFAULT_PROGRESS_MAX_VALUE = 100;

    /**
     * 进度条背景色
     */
    private int colorProgressBack = ContextCompat.getColor(getContext(), R.color.white_alpha_2);

    /**
     * 进度条前景色
     */
    private int colorProgressFore = ContextCompat.getColor(getContext(), R.color.colorWhite);

    /**
     * 进度条的高度
     */
    private float progressHeight = DEFAULT_PROGRESS_HEIGHT;

    /**
     * 进度条开始、结束横坐标/纵坐标
     */
    private float progressStartX = DEFAULT_PROGRESS_MARGIN_START_END;
    private float progressY;
    private float progressEndX;

    /**
     * 进度条最大数据值
     */
    private int progressMaxValue = DEFAULT_PROGRESS_MAX_VALUE;

    /**
     * 每份数据值对应的控件占用的宽度
     */
    private float progressSingleWidth = 0;

    /**
     * 进度条Paint
     * 背景色、前景色使用同一个
     */
    private Paint paintProgress;

    /**
     * 进度条数据值
     */
    private float progress = 0f;
    private float progressCurrent = DEFAULT_PROGRESS_MARGIN_START_END;

    /**
     * 指示器背景图
     */
    private Bitmap bitmapIndicatorBg = BitmapFactory.decodeResource(getResources(),
            R.mipmap.score_indicator_bg);
    private Bitmap bitmapIndicatorHead;
    private int widthBg;
    private int heightBg;
    private int leftBg = 0, topBg = 0, rightBg = 0, bottomBg = 0;
    private int leftHead = 0, topHead = 0, rightHead = 0, bottomHead = 0;
    private Context mContext;


    public ProgressScore(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        initDefineAttrs(context, attrs);
        initPaint();
        confirmBitmapBgWidthHeight();
    }

    public ProgressScore(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        initDefineAttrs(context, attrs);
        initPaint();
        confirmBitmapBgWidthHeight();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        progressStartX = progressStartX + progressHeight / 2;
        progressEndX = w - DEFAULT_PROGRESS_MARGIN_START_END - progressHeight / 2;
        progressY = (float) (h - (progressHeight / 2.0));
        progressSingleWidth = (progressEndX - progressStartX) / progressMaxValue;

        bottomBg = (int) (progressY - progressHeight);
        topBg = bottomBg - heightBg;
        topHead = (int) (topBg + DEFAULT_HEAD_MARGIN);

    }

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

        drawProgressBar(canvas);

        leftBg = (int) (progressCurrent - widthBg / 2);
        rightBg = leftBg + widthBg;
        leftHead = (int) (leftBg + DEFAULT_HEAD_MARGIN);
        rightHead = (int) (rightBg - DEFAULT_HEAD_MARGIN);
        bottomHead = (int) (topHead + (rightBg - leftBg - DEFAULT_HEAD_MARGIN * 2));

        drawIndicator(canvas);
    }

    /**
     * 初始化设置画笔
     */
    private void initPaint() {

        paintProgress = new Paint();
        paintProgress.setAntiAlias(true);
        paintProgress.setColor(colorProgressBack);
        paintProgress.setStrokeCap(Paint.Cap.ROUND);
        paintProgress.setStyle(Paint.Style.FILL);
        paintProgress.setStrokeWidth(progressHeight);
    }

    /**
     * 初始化读取xml自定义属性
     */
    private void initDefineAttrs(Context context, AttributeSet attrs) {

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressScore);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {

            int attr = array.getIndex(i);
            switch (attr) {

                case R.styleable.ProgressScore_progressHeight:

                    progressHeight = array.getDimension(attr, DEFAULT_PROGRESS_HEIGHT);
                    break;

                case R.styleable.ProgressScore_progressColorFore:

                    colorProgressFore = array.getColor(attr, ContextCompat.getColor(context, R.color.colorWhite));
                    break;

                case R.styleable.ProgressScore_progressColorBack:

                    colorProgressBack = array.getColor(attr, ContextCompat.getColor(context, R.color.white_alpha_2));
                    break;

                case R.styleable.ProgressScore_progressMaxValue:

                    progressMaxValue = array.getInt(attr, DEFAULT_PROGRESS_MAX_VALUE);
                    break;
            }
        }

        array.recycle();
    }

    /**
     * 绘制积分数据条
     */
    private void drawProgressBar(Canvas canvas) {

        paintProgress.setColor(colorProgressBack);
        canvas.drawLine(progressStartX, progressY, progressEndX, progressY, paintProgress);

        paintProgress.setColor(colorProgressFore);
        if (progressCurrent != DEFAULT_PROGRESS_MARGIN_START_END)
            canvas.drawLine(progressStartX, progressY, progressCurrent, progressY, paintProgress);
    }

    /**
     * 绘制进度条指示器
     */
    private void drawIndicator(Canvas canvas) {

        if (bitmapIndicatorHead != null) {

            Rect rectDes = new Rect(leftBg, topBg, rightBg, bottomBg);
            canvas.drawBitmap(bitmapIndicatorBg, null, rectDes, paintProgress);

            Rect rectDesHead = new Rect(leftHead, topHead, rightHead, bottomHead);
            canvas.drawBitmap(bitmapIndicatorHead, null, rectDesHead, paintProgress);
        }

    }

    public void doAnimation(float startX) {

        doAnimation(startX, 2000, 1600);
    }

    /**
     * 数据为正数的时候动画
     */
    private void doAnimation(float startX, long delay, long duration) {

        float endX = startX + progressSingleWidth * progress;
        ValueAnimator animator = ValueAnimator.ofObject(new PositiveEvaluator(), startX, endX);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                progressCurrent = (float) valueAnimator.getAnimatedValue();

                invalidate();

            }
        });

        animator.setStartDelay(delay);
        animator.setDuration(duration);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }

    /**
     * 插值器
     * */
    private class PositiveEvaluator implements TypeEvaluator<Float> {
    
    

        @Override
        public Float evaluate(float v, Float startValue, Float endValue) {

            return startValue + v * (endValue - startValue);
        }
    }

    public void setProgress(int progress) {

        if (progress < 0) {

            this.progress = 0;
        } else if (progress > DEFAULT_PROGRESS_MAX_VALUE) {

            this.progress = DEFAULT_PROGRESS_MAX_VALUE;
        } else {
            this.progress = progress;
        }
    }

    public void setProgressAndStartAnim(int progress) {

        if (progress < 0) {

            this.progress = 0;
            Toast.makeText(mContext, "数值过小", Toast.LENGTH_SHORT).show();
        } else if (progress > DEFAULT_PROGRESS_MAX_VALUE) {

            this.progress = DEFAULT_PROGRESS_MAX_VALUE;
            Toast.makeText(mContext, "数值过大", Toast.LENGTH_SHORT).show();
        } else {

            this.progress = progress;
        }

        doAnimation(progressStartX, 200, 800);
    }

    public void startAnim() {

        doAnimation(progressStartX);
    }

    private void confirmBitmapBgWidthHeight() {

        if (bitmapIndicatorBg != null) {

            widthBg = bitmapIndicatorBg.getWidth();
            heightBg = bitmapIndicatorBg.getHeight();
        }
    }

    /**
     * 通过本地图片资源id设置
     * */
    public void setBitmapIndicatorHead(int resId) {

        //toRoundBitmap()是将图片裁剪成圆形方法
        this.bitmapIndicatorHead = toRoundBitmap(BitmapFactory.decodeResource(getResources(), resId));
    }

    /**
     * 直接传入bitmap
     * */
    public void setBitmapIndicatorHead(Bitmap bitmapIndicatorHead) {
        this.bitmapIndicatorHead = toRoundBitmap(bitmapIndicatorHead);
    }

    /**
     * 将传入的bitmap裁剪成圆形
     */
    private Bitmap toRoundBitmap(Bitmap bitmap) {

        if (bitmap == null)
            return null;
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float roundPx;
        float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
        if (width <= height) {
            roundPx = width / 2;
            top = 0;
            bottom = width;
            left = 0;
            right = width;
            height = width;
            dst_left = 0;
            dst_top = 0;
            dst_right = width;
            dst_bottom = width;
        } else {
            roundPx = height / 2;
            float clip = (width - height) / 2;
            left = clip;
            right = width - clip;
            top = 0;
            bottom = height;
            width = height;
            dst_left = 0;
            dst_top = 0;
            dst_right = height;
            dst_bottom = height;
        }

        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        final Rect src = new Rect((int) left, (int) top, (int) right,
                (int) bottom);
        final Rect dst = new Rect((int) dst_left, (int) dst_top,
                (int) dst_right, (int) dst_bottom);
        final RectF rectF = new RectF(dst);

        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, src, dst, paint);
        return output;
    }
}

このコントロールの使用法は他のコントロールと同じです

<customview.ProgressScore
                android:id="@+id/progress_score"
                android:layout_marginTop="8dp"
                android:layout_width="match_parent"
                android:background="#098789"
                android:layout_height="100dp"
                app:progressColorBack="#333333"
                app:progressColorFore="#967766"
                app:progressHeight="2dp"
                app:progressMaxValue="150"/>

わかりました、記事はここにあります!興味のある子供用の靴は、実験用にコードをコピーできます。


  • それでもアニメーションについて質問がある場合は、Android AnimationLearningのブログ投稿を確認してください。

おすすめ

転載: blog.csdn.net/nsacer/article/details/77164447