带三角指示器的ProgressBar

学习安卓笔记之自定义控件(一)

------带三角指示器的ProgressBar

  如下图所示:“左图是android自带的进度条,他并没有带有指示的效果。所以我们要实现右图的效果,去自定义一个带有三角指示器的进度条。”

原生的ProgressBar与想要的效果的对比


自定义ProgressBar的实现

【第一步:首先我们来准备自定义属性】
  
  创建attrs.xml文件,用于创建我们需要的自定义属性。示意图如下:
  如何创建attrs.xml文件的示意图

  创建完attrs.xml之后,我们来继续添加我们需要的自定义属性。代码如下(代码颜色有点对不上,谁知道咋调告诉我一下,谢谢了):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyProgressBar" >
        <!-- 进度条默认的样式 -->
        <attr name="back_line_color" format="color"/>
        <attr name="back_line_height" format="dimension"/>
        <!-- 展示进度的样式 -->
        <attr name="fore_line_color" format="color"/>
        <attr name="fore_line_height" format="dimension"/>
        <!-- 三角形的颜色和高度 -->
        <attr name="indicator_color" format="color"/>
        <attr name="indicator_height" format="dimension"/>
    </declare-styleable>
</resources>

【第二步:创建MyProgressBar并继承ProgressBar】

  因为代码量不大,所以此处贴出了整个类。代码如下:

package lyan.test.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ProgressBar;

import lyan.test.R;


/**
 * 自定义进度条
 * Created by Administrator on 2016/9/5.
 */
public class MyProgressBar extends ProgressBar{
    /**
     * 默认的属性
     */
    private static final int DEFAULT_VALUES = 10;//默认的值
    private static final int DEFAULT_INDICATOR_HEIGHT = DEFAULT_VALUES * 2;//三角形的底边宽
    private static final int DEFAULT_INDICATOR_COLOR = 0xffFF0000;//指示器默认的颜色(红)
    private static final int DEFAULT_BACK_LINE_HEIGHT = DEFAULT_VALUES;//进度条默认高度(无进度)
    private static final int DEFAULT_BACK_LINE_COLOR = 0xffFF8080;//进度条默认颜色(粉)
    private static final int DEFAULT_FORE_LINE_HEIGHT = DEFAULT_VALUES;//进度条默认高度(有进度)
    private static final int DEFAULT_FORE_LINE_COLOR = 0xff95CAFF;//进度条默认颜色(蓝)
    /**
     * 变量
     */
    private int indicator_height = dp2px(DEFAULT_INDICATOR_HEIGHT);
    private int indicator_color = DEFAULT_INDICATOR_COLOR;
    private int back_height = dp2px(DEFAULT_BACK_LINE_HEIGHT);
    private int back_color = DEFAULT_BACK_LINE_COLOR;
    private int fore_height = dp2px(DEFAULT_FORE_LINE_HEIGHT);
    private int fore_color = DEFAULT_FORE_LINE_COLOR;
    private int progress_width;//绘制线的宽度
    private int triangle_width = indicator_height;//底边宽的一半
    /**
     * 绘制
     */
    private Paint indicator_paint;//绘制指示器的画笔
    private Paint back_paint;//绘制进度条的底部
    private Paint fore_paint;//绘制进度条的进度
    private int line_endX;//进度条的终点坐标
    /**
     * 构造方法
     * @param context
     */
    public MyProgressBar(Context context) {
        this(context,null);
    }

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

    public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性
        obtainAttributes(attrs);
        //初始化画笔
        initPaint();
    }

    /**
     * 绘制画笔
     */
    private void initPaint() {
        //绘制指示器的画笔
        indicator_paint = new Paint();
        indicator_paint.setAntiAlias(true);
        indicator_paint.setStyle(Paint.Style.FILL);//填充
        indicator_paint.setColor(indicator_color);//设置颜色
        //绘制进度条的底部
        back_paint = new Paint();
        back_paint.setStyle(Paint.Style.FILL);//填充
        back_paint.setColor(back_color);//设置颜色
        back_paint.setStrokeWidth(back_height);
        //绘制进度条的进度
        fore_paint = new Paint();
        fore_paint.setStyle(Paint.Style.FILL);//填充
        fore_paint.setColor(fore_color);//设置颜色
        fore_paint.setStrokeWidth(fore_height);
    }

    /**
     * 获取自定义属性
     */
    private void obtainAttributes(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(
                attrs, R.styleable.MyProgressBar);
        //指示器
        indicator_height = (int) typedArray.getDimension(
                R.styleable.MyProgressBar_indicator_height,indicator_height);
        indicator_color = typedArray.getColor(
                R.styleable.MyProgressBar_indicator_color,DEFAULT_INDICATOR_COLOR);
        //底部进度条
        back_height = (int) typedArray.getDimension(
                R.styleable.MyProgressBar_back_line_height,back_height);
        back_color = typedArray.getColor(
                R.styleable.MyProgressBar_back_line_color,DEFAULT_BACK_LINE_COLOR);
        //顶部进度
        fore_height = (int) typedArray.getDimension(
                R.styleable.MyProgressBar_fore_line_height,fore_height);
        fore_color = typedArray.getColor(
                R.styleable.MyProgressBar_fore_line_color,DEFAULT_FORE_LINE_COLOR);
        //回收资源
        typedArray.recycle();
    }

    /**
     * 测量
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
        progress_width = getMeasuredWidth()
                - getPaddingRight() - getPaddingLeft();//进度条的宽度
        line_endX = progress_width - triangle_width;//进度条线的终点
    }
    private int measureHeight(int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);//获取测量高度模式
        int specSize = MeasureSpec.getSize(measureSpec);//获取测量高度的值
        if (specMode == MeasureSpec.EXACTLY) {//精确的测量模式
            result = specSize;
        } else {
            result = (getPaddingTop() + getPaddingBottom()
                    + Math.max(back_height, fore_height)) + indicator_height;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
    /**
     * 绘制
     * @param canvas
     */
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(getPaddingLeft(), getHeight()/2);
        canvas.drawLine(triangle_width,0,line_endX,0,back_paint);//底部
        float progress_with = getProgress()*1.0f/getMax();
        float progressPosX = (int) ((line_endX
                - triangle_width) * progress_with);
        canvas.drawLine(triangle_width,0,progressPosX
                + triangle_width,0,fore_paint);//顶部
        //绘制三角形
        setTriangle(canvas , progressPosX);
        canvas.restore();
    }

    /**
     * 绘制三角形
     * @param canvas
     */
    private void setTriangle(Canvas canvas,float progressPosX) {
        // 绘制等边三角形
        float w = triangle_width/2;
        float h = (float) (triangle_width * Math.sin(45));
        Path path = new Path();
        path.moveTo(progressPosX + triangle_width, - back_height/2);//起点
        path.lineTo(w + progressPosX+ triangle_width,-h- back_height/2);
        path.lineTo(-w  + progressPosX+ triangle_width,-h- back_height/2);
        path.close();
        canvas.drawPath(path, indicator_paint);
    }
    /**
     * dp转px
     * @param dpValues
     * @return
     */
    private int dp2px(int dpValues) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, dpValues,
                getResources().getDisplayMetrics());
    }
}

  至此我们的自定义进度条已经编写完成,接下来我们测试一下我们的成果。


测试MyProgressBar的运行效果

我们先看一下效果
  我们在布局文件中添加我们自定义的控件,易用的方式就是包名加上类名。如:我的包名是:lyan.test.view,类名是:MyProgressBar,择控件的引用方式为lyan.test.view.MyProgressBar。让我们看一下布局文件:

<?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"
    tools:context="lyan.test.MainActivity">
    <lyan.test.view.MyProgressBar
        android:id="@+id/test_myprogressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:progress="50"
        android:max="100" />
</RelativeLayout>

  接下来我们运行下程序,效果如下所示:
未添加自定义属性的运行效果

  到现在我们还没用到我们的自定义属性,我们如何使用我们自定义的属性名,只需要添加一个命名控件就可以使用了。我们看一下示例代码:

<?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="lyan.test.MainActivity">
    <lyan.test.view.MyProgressBar
        android:id="@+id/test_myprogressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:progress="50"
        android:max="100"
        app:back_line_color="#ffff00"
        app:fore_line_color="#00ff00"
        app:indicator_color="#00ffff"
        />
</RelativeLayout>

  接下来我们再运行下程序,效果如下所示:

添加自定义属性的运行效果

  看来我们的效果并没有问题,接下来我们测试下我们的功能是否好使。


测试MyProgressBar的功能

  让我们在MainActivity中添加如下的代码:

package lyan.test;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import lyan.test.view.MyProgressBar;

public class MainActivity extends AppCompatActivity {
    private int counts;//计数
    private boolean isRunning;//子线程的开关
    private MyProgressBar myProgressBar;//声明控件
    private Thread thread;//子线程
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 1){
                ++counts;//模拟进度
                if (counts == 100){
                    isRunning = false;//关闭子线程
                    return false;
                }
                myProgressBar.setProgress(counts);
            }
            return false;
        }
    });
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        myProgressBar = (MyProgressBar) findViewById(R.id.test_myprogressbar);
    }
    /**
     * 按钮的点击事件
     * @param view
     */
    public void start(View view){
        if (thread !=null && thread.isAlive()){
             Toast.makeText(this,"子线程没有运行结束!",Toast.LENGTH_SHORT).show();
            return;
        }
        //设置一个子线程来模拟进度
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                isRunning = true;
                counts = 0;
                while (isRunning){
                    try {
                        Thread.sleep(100);
                        handler.sendEmptyMessage(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
        Toast.makeText(this,"子线程开始运行了!",Toast.LENGTH_SHORT).show();
    }
}

  我们在布局中添加一个按钮,代码如下

<?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="lyan.test.MainActivity">
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start"
        android:textSize="20sp"
        android:onClick="start"
        android:textAllCaps="false"/>
    
    <lyan.test.view.MyProgressBar
        android:id="@+id/test_myprogressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:progress="50"
        android:max="100"
        app:back_line_color="#ffff00"
        app:fore_line_color="#00ff00"
        app:indicator_color="#00ffff"
        />
</RelativeLayout>

  最后我们运行一下程序,看一下最后的运行效果

  如图所示,我们的MyProgressBar效果和功能都已经实现。由于我的gif截图绿色图像有问题,所以我这里把进度条换成了紫红色(#FF00FF)。
  
运行结果

  至此,我们的自定义进度条已经完成了。


参考资料

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值