安卓:自定义水平进度条动画

13

我正在尝试创建一个进度条,其中进度条本身在水平进度过程中以垂直旋转的方式进行动画。我已经成功地通过以下方式使用我的进度可绘制对象作为可绘制对象:

<ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:progressDrawable="@drawable/custom_progress"
        android:layout_marginRight="5dp" />

这是我的可绘制对象:

enter image description here

但我希望它在前进时具有轻微的翻滚效果,看起来像垂直线条正在向后移动。明白吗?任何帮助都非常感谢。谢谢。

编辑: 我尝试将animation-list作为我的进度可绘制对象,但仍然无法看到动画。动画列表可以放在进度项目的剪辑中吗?

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background" android:drawable="@drawable/gutter"></item>

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

<clip>
    <animation-list android:oneshot="false">
        <item android:drawable="@drawable/progress_bar_animate" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate2" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate3" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate4" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate5" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate6" android:duration="100" />
        <item android:drawable="@drawable/progress_bar_animate7" android:duration="100" />
    </animation-list>
</clip>

</item>
</layer-list>

1
请直接翻译图片链接,这是一张程序相关内容的GIF动态图。 - Oleg Vaskevich
9个回答

10

雅虎终于能用了!(但是,对于大图像可能会导致内存泄漏。在下面的帖子中已经修复了这个问题)

此代码获取瓷砖图片(tile1),重复显示它(TileMode.REPEAT),实现平移动画(10个片段),并将其添加到动画集合中。

输入图像描述

private void initAnimation() {
//      R.drawable.tile1 is PNG
        Bitmap b = BitmapFactory.decodeResource(getResources(),R.drawable.tile1);
        AnimationDrawable shiftedAnimation = getAnimation(b);

//      R.id.img_3 is ImageView in my application
        View v = findViewById(R.id.img_3);
        v.setBackground(shiftedAnimation);
        shiftedAnimation.start();
}

private Bitmap getShiftedBitmap(Bitmap bitmap, int shiftX) {
    Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
    Canvas newBitmapCanvas = new Canvas(newBitmap);

    Rect srcRect1 = new Rect(shiftX, 0, bitmap.getWidth(), bitmap.getHeight());
    Rect destRect1 = new Rect(srcRect1);
    destRect1.offset(-shiftX, 0);
    newBitmapCanvas.drawBitmap(bitmap, srcRect1, destRect1, null);

    Rect srcRect2 = new Rect(0, 0, shiftX, bitmap.getHeight());
    Rect destRect2 = new Rect(srcRect2);
    destRect2.offset(bitmap.getWidth() - shiftX, 0);
    newBitmapCanvas.drawBitmap(bitmap, srcRect2, destRect2, null);

    return newBitmap;
}

private List<Bitmap> getShiftedBitmaps(Bitmap bitmap) {
    List<Bitmap> shiftedBitmaps = new ArrayList<Bitmap>();
    int fragments = 10;
    int shiftLength = bitmap.getWidth() / fragments;

    for(int i = 0 ; i < fragments; ++i){
        shiftedBitmaps.add( getShiftedBitmap(bitmap,shiftLength * i));
    }

    return shiftedBitmaps;
}

private AnimationDrawable getAnimation(Bitmap bitmap) {
    AnimationDrawable animation = new AnimationDrawable();
    animation.setOneShot(false);

    List<Bitmap> shiftedBitmaps = getShiftedBitmaps(bitmap);
    int duration = 50;

    for(Bitmap image: shiftedBitmaps){
        BitmapDrawable navigationBackground = new BitmapDrawable(getResources(), image);
        navigationBackground.setTileModeX(TileMode.REPEAT);

        animation.addFrame(navigationBackground, duration);
    }
    return animation;
}

我刚刚在我的一个项目按钮上应用了这个,它完美地工作了,谢谢! :) - Qian JIN
v.setBackground(shiftedAnimation) - 仅适用于 API 级别 16 及以上 - AndrewS

2
我觉得按照我之前的想法无法完成这个任务。原因在于,我无法引用动画列表(即ClipDrawable),并将其强制转换为AnimateDrawable。只有这样才能通过编程的方式启动动画。此外,必须将其包含在clip元素中,因为这是ProgressBar遮罩图像的方法,以便在进度进行时仅显示部分图像。
除了使用ImageView来模拟自己的进度条并对其进行动画处理之外,我没有看到其他解决方案适用于这种特定的ProgressBar。

1

如果想要使进度条确定,我会在user1269737的答案中提供我的改进方法。

我已经将intAnimation方法更改为以下内容:

private void initAnimation(int tileImageResId) {
    Bitmap b = BitmapFactory.decodeResource(getResources(), tileImageResId);
    if (b != null) {
        shiftedAnimation = getAnimation(b);

        setBackgroundColor(ContextCompat.getColor(getContext(), #14000000));
        mProgressImage.setBackground(shiftedAnimation);
    }
}

并添加了setProgress方法,用于动态改变进度:

 public void setProgress(double progress){
    if (progress > 100){
        progress = 100;
    }
    else if (progress < 0){
        progress = 0;
    }
    int parentWidth = getWidth();
    final int newWidth =  (int)(parentWidth*(progress/100.0));

    widthAnimator = ValueAnimator.ofInt(mProgressImage.getLayoutParams().width, newWidth);
    widthAnimator.setDuration(200);
    widthAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mProgressImage.getLayoutParams().width = (Integer) animation.getAnimatedValue();
            mProgressImage.requestLayout();
        }
    });
    widthAnimator.start();
}

还有一些构造函数方面的更改:

public LProgressBar(Context context, AttributeSet attrs) {
    super(context, attrs);

    mProgressImage = new ImageView(getContext());
    FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(0, FrameLayout.LayoutParams.MATCH_PARENT);
    mProgressImage.setLayoutParams(layoutParams);
    addView(mProgressImage);

    initAnimation(R.drawable.pb_background);
}

谢谢@anro,但这不是垂直旋转的动画。你有什么建议吗? - D G
你的意思是,同样的进度条,但是垂直方向的吗? - anro

1
我的先前方法在内存消耗方面存在问题。我已经改进了它,使用了TranslateAnimation。但是它有一点滞后(请注意ProgressBgView.initAnimation方法中的HACK注释)。
布局XML:
    <com.example.tweenanimation.ProgressBgView
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:id="@+id/progress_db_view"
        />

后端:
    final ProgressBgView prog = (ProgressBgView) findViewById(R.id.progress_db_view);
        prog.setBackgroundAsTile(R.drawable.bg_tile_1);
        prog.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                prog.startAnimation();
            }
        });

并且类:

public class ProgressBgView extends FrameLayout {
    private ImageView mProgressImage;
    private TranslateAnimation mProgressAnimation;

    public ProgressBgView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mProgressImage = new ImageView(getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        mProgressImage.setLayoutParams(layoutParams);
        addView(mProgressImage);
    }

    public void setBackgroundAsTile(int tileImageResId) {
        Bitmap tileBitmap = BitmapFactory.decodeResource(getResources(), tileImageResId);
        BitmapDrawable tileRepeatedBitmap = new BitmapDrawable(getResources(), tileBitmap);
        tileRepeatedBitmap.setTileModeX(TileMode.REPEAT);

        initAnimation(tileBitmap.getWidth());

        mProgressImage.setBackgroundDrawable(tileRepeatedBitmap);
    }

    private void initAnimation(int tileImageWidth) {
        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mProgressImage.getLayoutParams();
        layoutParams.setMargins(-tileImageWidth, 0, 0, 0);

        // *HACK* tileImageWidth-3 is used because of *lags*(slow pause) in the moment
        // of animation END-RESTART.
        mProgressAnimation = new TranslateAnimation(0, tileImageWidth - 3, 0, 0);
        mProgressAnimation.setInterpolator(new LinearInterpolator());
        mProgressAnimation.setDuration(1000);
        mProgressAnimation.setRepeatCount(Animation.INFINITE);
    }

    public void startAnimation() {
        mProgressImage.startAnimation(mProgressAnimation);
    }
}

2
我发现这个解决方案在Android 2.3上有问题(最后一个瓷砖会跳动),而4月3日13:24的解决方案也能在Gingerbread上运行。 - zilinx

1

我找到了一个解决方案。创建一个名为progress_loading.xml的xml文件,并将其赋值给progressbar的进度drawable,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item android:id="@android:id/progress">
    <clip>
      <shape>
        <solid android:color="#33FFFFFF" />
      </shape>
    </clip>
  </item>
</layer-list>

然后在代码中:

AnimationDrawable anim = (AnimationDrawable) getResources()
                .getDrawable(R.drawable.loading);
        ClipDrawable clipDrawable = new ClipDrawable(anim, Gravity.LEFT,
                ClipDrawable.HORIZONTAL);
        LayerDrawable layerDrawable = (LayerDrawable) progressBar
                .getProgressDrawable();
        layerDrawable.setDrawableByLayerId(android.R.id.progress,
                clipDrawable);
        anim.start();

xml loading.xml 中包含实际的动画列表。这对我很有效。


1

这是我得到的内容。 自定义水平进度条

而这就是方法:

<ProgressBar
    android:layout_width="match_parent"
    android:layout_height="15dp"
    android:indeterminate="true"
    style="@style/IndeterminateProgressBar" />

在styles.xml中添加以下内容:
<style name="IndeterminateProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
    <item name="android:indeterminateDrawable">@anim/progress_bar_indeterminate</item>
</style>

在文件 res/anim/progress_bar_indeterminate.xml 中:

<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/progress_bar_horizontal_1" android:duration="200" />
    <item android:drawable="@drawable/progress_bar_horizontal_2" android:duration="200" />
    <item android:drawable="@drawable/progress_bar_horizontal_3" android:duration="200" />
    <item android:drawable="@drawable/progress_bar_horizontal_4" android:duration="200" />
</animation-list>

这里有4张图片需要放在res/drawable文件夹中:

progress_bar_horizontal_1

progress_bar_horizontal_2

progress_bar_horizontal_3

progress_bar_horizontal_4

附注:如果需要反向显示进度条,只需在progress_bar_indeterminate.xml文件中的图像名称中翻转数字。

你好!=)


您可能希望像这样设置动画列表中的项:<item android:duration="70"> <bitmap android:src="@drawable/seekbar_anim_pic_1" android:tileMode="repeat"/> </item>,这样图像就会重复而不是拉伸。 - HedeH

0

我找到了一个解决方案,有点类似于上面的解释,但并不是源自它。效果很好!

假设下面是我们的progress_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <solid
                android:color="@color/black_20"/>
        </shape>
    </item>

    <item android:id="@android:id/progress">
        <clip>
            <animation-list
                android:oneshot="false">
                <item
                    android:drawable="@drawable/stripe3"
                    android:duration="100"/>
                <item
                    android:drawable="@drawable/stripe2"
                    android:duration="100"/>
                <item
                    android:drawable="@drawable/stripe1"
                    android:duration="100"/>
            </animation-list>
        </clip>
    </item>
</layer-list>

然后在Java代码中,执行以下操作:

mDownloadProgressBar.setProgressDrawable(getResources().getDrawable(R.drawable.download_progress, null));
LayerDrawable drawable = (LayerDrawable)mDownloadProgressBar.getProgressDrawable();
ClipDrawable clipDrawable = (ClipDrawable) drawable.getDrawable(1);
AnimationDrawable animationDrawable =(AnimationDrawable)clipDrawable.getDrawable();
animationDrawable.start();

以上解决方案的限制在于clipDrawable.getDrawable()仅适用于API 23+,因此我被迫按照下面发布的新解决方案进行实现。然后,我不得不手动为我的ProgressBar创建一个LayerDrawable。 - prateek

0

download_progress_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/stripe3"
        android:duration="100"/>
    <item
        android:drawable="@drawable/stripe2"
        android:duration="100"/>
    <item
        android:drawable="@drawable/stripe1"
        android:duration="100"/>
</animation-list>

Java代码:
mDownloadProgressBar.setProgressDrawable(createProgressDrawable(context));

private Drawable createProgressDrawable(Context context) {

    ShapeDrawable shape = new ShapeDrawable();
    shape.getPaint().setStyle(Paint.Style.FILL);
    shape.getPaint()
            .setColor(ContextCompat.getColor(context, R.color.black_20));

    AnimationDrawable animationDrawable = (AnimationDrawable) getDrawable(R.drawable.download_progress_anim, context);
    ClipDrawable clipDrawable = new ClipDrawable(animationDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
    animationDrawable.start();

    LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{shape, clipDrawable});
    layerDrawable.setId(0, android.R.id.background);
    layerDrawable.setId(1, android.R.id.progress);
    return layerDrawable;
}

private Drawable getDrawable(@DrawableRes int drawable, Context context) {
        if (Build.VERSION.SDK_INT < 21) {
            return context.getResources().getDrawable(drawable);
        } else {
            return context.getResources().getDrawable(drawable, null);
        }
    }

希望这有所帮助。您可以轻松修改它以支持您自己的实现。

0

只有当您想要一个自定义颜色和高度的进度条时:

我相信谷歌有一个视图可以做到这一点(LinearProgressIndicator)。您可以自定义颜色和高度。我在这篇文章中解释了它:

https://dev59.com/eFsW5IYBdhLWcg3wAjOm#68505259


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接