心形进度条

废话不多说,先上效果图:

这个进度就是模拟心形填充的过程



实现原理是:首先将空心形图片绘制出来,然后根据进度大小再将实心的心形图片一部分一部分的绘制出来。也就是两张完全相同大小的空心图和圆心图重叠绘制,利用canvas的clipRect绘制实心部分,实现进度条效果。



首先在res->values先新建一个文件atrrs.xml,然后定义两个属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="HeartProgressBar">
        <attr name="max" format="integer"></attr>
        <attr name="progress" format="integer"></attr>
    </declare-styleable>
</resources>

max是最大进度值,progress是当前进度。

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.os.Handler;
import android.util.AttributeSet;
import android.view.View;

import com.helen.funnyview.R;

/**
 * Created by Helen on 2015/5/14.
 */
public class HeartProgressBar extends View{
    private Bitmap mHeartBitmap;//空心图片
    private Bitmap mHeartedBitmap;//实心图
    private Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
    private int progress=0;//当前进度
    private int max=100;//最大进度
    private boolean isFinish=false;//是否填充完成
    private boolean isAutoFill=false;//是否自动填充
    public HeartProgressBar(Context context) {
        super(context);
        init();
    }

    public HeartProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.HeartProgressBar);
        progress=a.getInteger(R.styleable.HeartProgressBar_progress,0);
        max=a.getInteger(R.styleable.HeartProgressBar_max,100);
        if(max<=0){
            max=100;
        }
        a.recycle();
    }

    public HeartProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        if(mHeartBitmap==null){
            mHeartBitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart);
        }
        if(mHeartedBitmap==null){
            mHeartedBitmap= BitmapFactory.decodeResource(getResources(), R.mipmap.hearted);
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        final int width=getWidth();//mHeartBitmap.getWidth();
        final int height=getHeight();//mHeartBitmap.getHeight();
        float percent=progress*1.0f/max;//进度百分比
        if(percent>=1){
            percent=1;
        }
        canvas.save();
        //绘制空心图
        canvas.drawBitmap(mHeartBitmap, 0, 0,mPaint);
        //计算绘制实心图的范围
        canvas.clipRect(0, height * (1 - percent), width, height);
        //绘制实心图
        canvas.drawBitmap(mHeartedBitmap, 0, 0, mPaint);
        canvas.restore();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        //if(widthMode==MeasureSpec.AT_MOST){//layout_width=wrap_content
            //设置控件宽高跟图片一样
            setMeasuredDimension(mHeartBitmap.getWidth(), mHeartBitmap.getHeight());
        //}
        //setMeasuredDimension(getMeasureSize(widthMeasureSpec, true), getMeasureSize(heightMeasureSpec, true));
    }

    private int getMeasureSize(int spec,boolean isWidth){
        int size=MeasureSpec.getSize(spec);
        int mode=MeasureSpec.getMode(spec);
        if(mode==MeasureSpec.AT_MOST){
            if(isWidth) {
                size =mHeartBitmap.getWidth();
            }else{
                size=mHeartBitmap.getHeight();
            }
        }
        return size;
    }

    /**
     * 设置当前进度
     * @param progress
     */
    public void setProgress(int progress) {
        if(isAutoFill) return;
        this.progress = progress;
        if(!isFinish) {

            invalidate();
        }
        if(progress>=max){
            isFinish=true;
        }
    }
    public int getProgress() {
        return progress;
    }
    /**
     * 是否完成
     * @return
     */
    public boolean isFinish() {
        return isFinish;
    }

    public boolean isAutoFill() {
        return isAutoFill;
    }

    /**
     * 设置最大进度值
     */
    public void setMax(int max) {
        this.max = max;
    }

    public int getMax() {
        return max;
    }

    /**
     * 开启自动填充
     */
    public void startAutoFill(){
        isAutoFill=true;
        final int step=10;
        final Handler handler=new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                progress += step;
                invalidate();
                if (progress >= max) {
                    isFinish = true;
                }
                if (!isFinish()) {
                    handler.postDelayed(this, 100 - progress);
                } else {
                    handler.removeCallbacks(this);
                }
            }
        });
    }
}

然后就是使用它了。首先是布局文件:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.helen.funnyview.view.HeartProgressBar
        android:id="@+id/bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:progress="50"
        app:max="150"
        />
</RelativeLayout>

设置了起始进度为50,最大进度为150

接着是activity内容:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

import com.helen.funnyview.R;
import com.helen.funnyview.view.HeartProgressBar;

/**
 * Created by Helen on 2015/5/14 16:56.
 * TODO
 */
public class HeartProgressBarActivity extends Activity{
    private HeartProgressBar bar;
    private Handler handler=new Handler();
    private int progress=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_heart_progress_bar);
        bar=(HeartProgressBar)findViewById(R.id.bar);
        progress=bar.getProgress();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                progress+=5;
                bar.setProgress(progress);
                if(!bar.isFinish()) {
                    handler.postDelayed(this, 500);
                }else{
                    handler.removeCallbacks(this);
                }
            }
        },3000);
        //bar.startAutoFill();
    }
}

handler是模拟一个网络请求,进度加载的过程。这样就实现了开始那种效果了。

还有就是,该控件也可以用于类似于‘收藏’的功能,就是一开始没有收藏是空心的,点击收藏后就动态填充了心形,也就是上面注释掉的代码startAutoFill()方法。

需要源码的请戳这里

时间: 2024-11-02 00:30:42

心形进度条的相关文章

Qt编写自定义控件14-环形进度条

前言 环形进度条,用来展示当前进度,为了满足大屏UI的需要特意定制,以前有个叫圆环进度条,不能满足项目需要,只能重新定做,以前的进度间距不能自适应分辨率,而且当前进度对应的反的进度不能单独设置颜色,即当前进度90%,剩余的10%也需要设置成不同的颜色,还有一个重要的功能是,能够指定多个警戒值,一旦超过或者小于该值,则当前进度自动切换到预先设定的警戒值颜色,而不需要用户自己去判断警戒值去设置警戒颜色,用户只需要传入当前值即可,这个功能非常实用,还可以设置警戒判断的标准是超过值还是小于值报警.个人感

[Android]组件-进度条1

多式样ProgressBar 普通圆形ProgressBar 该类型进度条也就是一个表示运转的过程,例如发送短信,连接网络等等,表示一个过程正在执行中. 一般只要在XML布局中定义就可以了. <progressBar android:id="@+id/widget43" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layou

Android -- ProgressBar(进度条的使用)

我们在开发程序是经常会需要软件全屏显示.自定义标题(使用按钮等控件)和其他的需求,今天这一讲就是如何控制Android应用程序的窗体显示. requestWindowFeature可以设置的值有:(具体参考 点击链接查看效果)    1.DEFAULT_FEATURES:系统默认状态,一般不需要指定        // 2.FEATURE_CONTEXT_MENU:启用ContextMenu,默认该项已启用,一般无需指定        // 3.FEATURE_CUSTOM_TITLE:自定义标

Android进度条使用详解(一)

进度条是UI界面中一种非常实用的组件,通常用于向用户像是某个耗时操作完成的百分比.进度条可动态地显示进度,因此避免长时间地执行某个操作时,让用户感觉程序失去了响应,从而更好地提高用户界面的友好性.ProgressBar本身代表着进度条组件,其子类还包括SeekBar(拖动条)和RatingBar(星级评分条). 一.ProgressBar使用详解 1.环形进度条使用方法 (1)进度条样式 (2)代码实现 <ProgressBar android:id="@+id/progressBar&q

Android-高级UI控件_ProgressDialog进度条对话框

代码 package com.lxt008; import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.widget.Button; public class Activity01 extends Activity

自定义view之圆形进度条

本节介绍自定义view-圆形进度条 思路: 根据前面介绍的自定义view内容可拓展得之: 1:新建类继承自View 2:添加自定义view属性 3:重写onDraw(Canvas canvas) 4:实现功能 下面上代码 1.自定义view代码: public class CustomView extends View { //背景圆环颜色 private int circleColor; //进度条颜色&字体颜色(为了美观,所以设计字体颜色和进度条颜色值一致) private int seco

Android——ProgressDialog 进度条对话框

public class ProgressDialogActivity extends Activity {    private Button btn_large_pd, btn_horizontal_pd;    // 声明进度条对话框    ProgressDialog mProgressDialog;    // 进度    int mCount = 0; @Override    protected void onCreate(Bundle savedInstanceState) { 

自定义圆形进度条

关于控件呢,我想大家应该都很熟悉了吧,android应用开发MVC架构中,控件担任着至关重要的作用,感觉可以说是基于控件的事件模型人机交互的基础吧.这种特性感觉在wpf开发中体现得更为直接,感兴趣的同学可以去了解一下.而android框架自身就已经给我们提供了很多控件.那么问题来了?为什么有那么多控件可以用,你还要去屑自定义控件呢?是因为大家闲的蛋疼吗?显然不是.个人认为只要有两方面吧,要么是觉得有些原生控件是在是丑得难以忍受(即使是在你已经自定义了他的shape,圆角,selector等一系列

闲话进度条

对于程序员来说,进度条(progress bar)大家都很熟悉,进度条有不同的展现形式,包括传统的横向进度条以及各种loading图; 也有不同的实现方法,各种黑科技黑魔法.本文探讨两个问题,第一个为什么要有进度条; 第二个,进度条实现的一点点知识.重点在前者. 为什么要有进度条,前些天在知乎日报上看到一个神吐槽: --为什么男生追女生追着追着就放弃了? --没有进度条提示啊 不禁想到前一段时间在阅读<暗时间>的时候,也看到了这个问题.简单来说,进度条让人心里有个预期(有盼头),让人相信总会完