gif跟随ProgressBar一起动/pk进度条gif特效

最近要做个小功能,现在市面上面的直播平台,像虎牙、斗鱼、抖音和快手等等,两个主播PK的时候进度条上面会有一个动画一直在重复,挺好看的,直接上图。

pk

简单说下思路,因为ProgressBar已经基本够使用了,所以就懒得重写个自定义ProgressBar,因为现在做的是分开的两个view,到时候想替换的话,会方便很多。

整体思路是ProgressBar因为是均分的进度条,会根据始末位置算百分比,我们同样可以根据进度条移动的距离,算出控件需要对应移动的距离。举个栗子:
进度条的最小单位 / 进度条的范围 = 移动的最小距离 / 总的距离(也就是控件的总宽度)

下面开始上代码:这个是播放gif写的自定义view

package com.cgg.pkprogress;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;

public class GifView extends View {


    //gif图片资源ID
    protected int progress_gif;
    protected int progress_width;

    //gif动态效果总时长,在未设置时长时默认为1秒
    private static final int DEFAULT_MOVIE_DURATION = 1000;
    //Movie实例,用来显示gift图片
    private Movie mMovie;
    //显示gift图片的动态效果的开始时间
    private long mMovieStart;
    //动态图当前显示第几帧
    private int mCurrentAnimationTime = 0;
    //图片离屏幕左边的距离
    private float mLeft;
    //图片离屏幕上边的距离
    private float mTop;
    //图片的缩放比例
    private float mScale;
    //图片在屏幕上显示的宽度
    private int mMeasuredMovieWidth;
    //图片在屏幕上显示的高度
    private int mMeasuredMovieHeight;
    //是否显示动画,为true表示显示,false表示不显示
    private boolean mVisible = true;
    //动画效果是否被暂停
    private volatile boolean mPaused = false;

    public GifView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GifView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        obtainStyledAttributes(attrs);
    }

    private void obtainStyledAttributes(AttributeSet attrs) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        // 获取自定义属性
        final TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.GifProgressBar);
        progress_gif = attributes.getResourceId(R.styleable.GifProgressBar_progress_gif, -1);
        progress_width = (int) attributes.getDimension(R.styleable.GifProgressBar_progress_width, 10);
        attributes.recycle();

        if (progress_gif != -1) {
            mMovie = Movie.decodeStream(getResources().openRawResource(
                    progress_gif));
        }

    }

    /**
     * 设置gif图资源
     *
     * @param giftResId
     */
    public void setMovieResource(int giftResId) {
        progress_gif = giftResId;
        mMovie = Movie.decodeStream(getResources().openRawResource(
                progress_gif));
        requestLayout();
    }

    /**
     * 手动设置 Movie对象
     *
     * @param movie Movie
     */
    public void setMovie(Movie movie) {
        this.mMovie = movie;
        requestLayout();
    }

    /**
     * 得到Movie对象
     *
     * @return Movie
     */
    public Movie getMovie() {
        return mMovie;
    }

    /**
     * 设置要显示第几帧动画
     *
     * @param time
     */
    public void setMovieTime(int time) {
        mCurrentAnimationTime = time;
        invalidate();
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mMovie != null) {
            int movieWidth = mMovie.width();
            int movieHeight = mMovie.height();
            int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
            float scaleW = (float) movieWidth / (float) maximumWidth;
            mScale = 1f / scaleW;
            mMeasuredMovieWidth = maximumWidth;
            mMeasuredMovieHeight = (int) (movieHeight * mScale);
            setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
        } else {
            setMeasuredDimension(getSuggestedMinimumWidth(),
                    getSuggestedMinimumHeight());
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
        mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
        mVisible = getVisibility() == View.VISIBLE;
    }


    @Override
    protected synchronized void onDraw(Canvas canvas) {
        if (mMovie != null) {
            if (!mPaused) {
                updateAnimationTime();
                drawMovieFrame(canvas);
                invalidateView();
            } else {
                drawMovieFrame(canvas);
            }
        }

    }

    /**
     * 重绘
     */
    @SuppressLint("NewApi")
    private void invalidateView() {
        if (mVisible) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                postInvalidateOnAnimation();
            } else {
                invalidate();
            }
        }
    }

    /**
     * 更新当前显示进度
     */
    private void updateAnimationTime() {
        long now = android.os.SystemClock.uptimeMillis();
        // 如果第一帧,记录起始时间
        if (mMovieStart == 0) {
            mMovieStart = now;
        }
        // 取出动画的时长
        int dur = mMovie.duration();
        if (dur == 0) {
            dur = DEFAULT_MOVIE_DURATION;
        }
        // 算出需要显示第几帧
        mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
    }

    // 绘制图片
    private void drawMovieFrame(Canvas canvas) {
        // 设置要显示的帧,绘制即可
        mMovie.setTime(mCurrentAnimationTime);
//        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
        canvas.scale(mScale, mScale);
        mMovie.draw(canvas, mLeft / mScale, mTop / mScale);
        canvas.restore();
    }

    @SuppressLint("NewApi")
    @Override
    public void onScreenStateChanged(int screenState) {
        super.onScreenStateChanged(screenState);
        mVisible = screenState == SCREEN_STATE_ON;
        invalidateView();
    }

    @SuppressLint("NewApi")
    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        mVisible = visibility == View.VISIBLE;
        invalidateView();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = visibility == View.VISIBLE;
        invalidateView();
    }

}

自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--progressbar带gif进度-->
    <declare-styleable name="GifProgressBar">
        <attr name="progress_gif" format="reference" />
        <attr name="progress_width" format="dimension" />
    </declare-styleable>

</resources>

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/pk_progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="14dp"
        android:layout_marginTop="150dp"
        android:max="100"
        android:progress="50"
        android:progressDrawable="@drawable/auchor_pk_progress" />

    <com.cgg.pkprogress.GifView
        android:id="@+id/gif_view"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="149dp"
        app:progress_gif="@raw/pking" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@color/black"
        android:padding="8dp"
        android:text="随机进度 50%"
        android:textColor="@color/white" />

</RelativeLayout>

主要代码:

package com.cgg.pkprogress;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;
    private GifView gifView;
    private TextView textView;

    //每个百分比 的距离
    private float scrollDistance;

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

        progressBar = findViewById(R.id.pk_progressBar);
        gifView = findViewById(R.id.gif_view);
        textView = findViewById(R.id.tv);

        // 得到progressBar控件的宽度
        ViewTreeObserver vto2 = progressBar.getViewTreeObserver();
        vto2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                progressBar.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                //得到屏幕的总宽度
                int width = progressBar.getWidth();
                scrollDistance = (float) ((1.0 / progressBar.getMax()) * width);
            }
        });

        textView.setOnClickListener(view -> {
            // 5 ~ 95 之间
            setPKProgress(5 + new Random().nextInt(91));
        });

    }

    private void setPKProgress(int value){
        progressBar.setProgress(value);
        gifView.setTranslationX((value - 50) * scrollDistance);
        textView.setText("随机进度 " + value + "%");
    }
}

下面来两个地址啦:
csdn地址:https://download.csdn.net/download/CGG92/19405631
github地址:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果想要在进度条末端显示百分比,可以通过自定义 ProgressBar 的绘制方式来实现。具体做法如下: 1. 在窗体上添加一个 ProgressBar 控件和一个 Label 控件,设置 ProgressBar 的 Style 属性为 ProgressBarStyle.Continuous,Label 控件用于显示进度百分比。 2. 在窗体的 Load 事件中添加以下代码: ```csharp private void Form1_Load(object sender, EventArgs e) { // 设置 ProgressBar 的最大值和初始值 progressBar1.Maximum = 100; progressBar1.Value = 0; // 设置 ProgressBar 的 Style 为 Continuous progressBar1.Style = ProgressBarStyle.Continuous; } ``` 3. 重写 ProgressBar 的 Paint 方法,绘制进度条末端的百分比。代码如下: ```csharp private void progressBar1_Paint(object sender, PaintEventArgs e) { // 获取 ProgressBar 上下文 var g = e.Graphics; // 获取 ProgressBar 客户区域 var rect = progressBar1.ClientRectangle; // 计算进度条的宽度 var progressBarWidth = (int)(rect.Width * ((double)progressBar1.Value / progressBar1.Maximum)); // 绘制进度条 var progressBarRect = new Rectangle(rect.X, rect.Y, progressBarWidth, rect.Height); g.FillRectangle(Brushes.Green, progressBarRect); // 绘制进度百分比 var percent = (int)(((double)progressBar1.Value / progressBar1.Maximum) * 100); var percentText = $"{percent}%"; var textSize = g.MeasureString(percentText, progressBar1.Font); var textX = rect.X + progressBarWidth - (int)textSize.Width; var textY = rect.Y + (rect.Height - (int)textSize.Height) / 2; var textPoint = new PointF(textX, textY); g.DrawString(percentText, progressBar1.Font, Brushes.White, textPoint); } ``` 在上面的代码中,我们首先获取 ProgressBar 的客户区域和当前进度条的宽度,然后绘制进度条。接着计算进度百分比并绘制在进度条末端。最后在窗体的 Load 事件中将 Paint 事件和 ProgressBar 控件关联即可。 这样就可以实现在进度条末端显示百分比的效果了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值