自定义View和圆形进度条

本文探讨了如何在Android开发中创建自定义View,重点在于设计和实现一个美观实用的圆形进度条。从布局设计到绘制逻辑,详细解析了自定义组件的全过程,帮助开发者提升UI定制能力。
摘要由CSDN通过智能技术生成
/*一、自定义属性的声明与获取。
重写方法 TestView(Context context, AttributeSet attrs) 自定义属性并获取
新建一个  #values#  布局文件声明需要的属性
       <declare-styleable 跟自定义控件名 用 <attr 添加属性 <attr 里 <enum ></enum> 枚举
自定义的属性,之后可以在 #主布局# 文件使用,
              使用前布局文件要自定义前缀  xmlns:hyman="http://schemas.android.com/apk/res-auto"
之后可以在 #自定义View#  里用 TypedArray获取或者第二种循环判断(对于有预定值的)
回收  ta.recycle();
二、测量 onMeasure 。MeasureSpec、、setMeasuredDimension(width, height);
三、绘制 onDraw 。 Paint、Canvas、mode、size
四、状态的存储与恢复。固定模板
 */

public class TestView extends View {

    private String mText = "Imooc";

    private Paint mPaint;//声明画笔

    //重写
    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);//父类的属性设置

        //在方法中实例化
        initPaint();

        //第一种自定义属性
        //类型数组,环境对象.获得类型属性集的方法,定义的对象用来装TestView属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TestView);

        //挨个   defValue 定义值
        boolean booleanTest = ta.getBoolean(R.styleable.TestView_test_boolean, false);
        int integerTest = ta.getInteger(R.styleable.TestView_test_integer, -1);
        float dimensionTest = ta.getDimension(R.styleable.TestView_test_dimension, 0);
        int enumTest = ta.getInt(R.styleable.TestView_test_enum, 1);
//        mText = ta.getString(R.styleable.TestView_test_string);//getString不需要默认值


        //第二种
        //当预定义的有值,判断只有用户声明了这个值,才会去获取
        int count = ta.getIndexCount();//返回数组中实际包含数据(有值的属性)的索引数
        for (int i = 0; i < count; i++) {
            int index = ta.getIndex(i);//取索引id,判断,如果是它就赋值
            switch (index) {
                case R.styleable.TestView_test_string:
                    mText = ta.getString(R.styleable.TestView_test_string);
                    break;
            }
        }

        Log.e("TAG", booleanTest + " , "
                + integerTest + " , "
                + dimensionTest + " , " + enumTest + " ," + mText);

        //回收
        ta.recycle();
    }



    //测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //通过  MeasureSpec  拿到mode和size
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        //宽
        int width = 0;//if确定地,直接给值
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;//
        } else {
            //自己测量自己所需要的宽度有三个方法组成:测量宽度(自定义)、离左内边距、离右内边距
            int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight();
            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(needWidth, widthSize);//如果是最多,则不能超过给的值widthSize
            } else {
                width = needWidth;
            }
        }

        //高,和宽方法一样
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int height = 0;
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            int needHeight = measureHeight() + getPaddingTop() + getPaddingBottom();
            if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(needHeight, heightSize);
            } else {
                height = needHeight;
            }
        }
        //设置宽高
        setMeasuredDimension(width, height);

    }

    private int measureHeight() {
        return 0;
    }

    private int measureWidth() {
        return 0;
    }


    //绘制           Canvas 画,油画
    @Override
    protected void onDraw(Canvas canvas) {
        //画圆、线  需要声明一个画笔paint
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2 - mPaint.getStrokeWidth() / 2, mPaint);//画笔太宽就减一下
        mPaint.setStrokeWidth(1);//线细一点
        canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mPaint);
        canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), mPaint);

        //设置文本
        mPaint.setTextSize(72);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        //画文本
        canvas.drawText(mText, 0, mText.length(), 0, getHeight(), mPaint);
    }


    //设置画笔
    private void initPaint() {
        mPaint = new Paint();//先实例化出来
        mPaint.setStyle(Paint.Style.STROKE);//空心圆
        mPaint.setStrokeWidth(6);//宽度
        mPaint.setColor(0xFFFF0000);
        mPaint.setAntiAlias(true);//抗锯齿
    }


    //重写
    @Override//点击事件,点击后文本改变
    public boolean onTouchEvent(MotionEvent event) {
        mText = "8888";
        invalidate();//重绘
        return true;
    }


    //固定模板
    //定义两个常量
    private static final String INSTANCE = "instance";
    private static final String KEY_TEXT = "key_text";

    //
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putString(KEY_TEXT, mText);//存自己的。
        //存父View的。
        bundle.putParcelable(INSTANCE, super.onSaveInstanceState());
        return bundle;
    }

    //
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;//
            Parcelable parcelable = bundle.getParcelable(INSTANCE);//取
            super.onRestoreInstanceState(parcelable);//设
            mText = bundle.getString(KEY_TEXT);//设
            return;
        }
        super.onRestoreInstanceState(state);
    }//最后最关键要给自定义view设id才能完成。
}

圆形进度条


public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final View view = findViewById(R.id.id_pb);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator.ofInt(view, "progress", 0, 100).setDuration(3000).start();
            }
        });

    }
}
//圆形进度条
/* */
public class RoundProgressBar extends View {

    private int mRadius;//半径
    private int mColor;
    private int mLineWidth;
    private int mTextSize;
    private int mProgress;

    private Paint mPaint;//画类


    //参数:环境,属性集
    public RoundProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);//View的构造函数

        //类型数组,环境对象.获得类型属性集的方法,定义的对象用来装圆形进度条的属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);

        mRadius = (int) ta.getDimension(R.styleable.RoundProgressBar_radius, dp2px(30));
        mColor = ta.getColor(R.styleable.RoundProgressBar_color, 0xffff0000);
        mLineWidth = (int) ta.getDimension(R.styleable.RoundProgressBar_line_width, dp2px(3));
        mTextSize = (int) ta.getDimension(R.styleable.RoundProgressBar_android_textSize, dp2px(36));
        mProgress = ta.getInt(R.styleable.RoundProgressBar_android_progress, 30);

        ta.recycle();

        initPaint();
    }

    //
    private float dp2px(int dpVal) {
        return TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics());
    }

    //
    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mColor);
    }

    public void setProgress(int progress) {
        mProgress = progress;
        //
        invalidate();
    }

    public int getProgress() {
        return mProgress;
    }

    /*

     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0;
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight();
            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(needWidth, widthSize);
            } else {
                width = needWidth;
            }
        }

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int height = 0;

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            int needHeight = measureHeight() + getPaddingTop() + getPaddingBottom();
            if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(needHeight, heightSize);
            } else //MeasureSpec.UNSPECIFIED
            {
                height = needHeight;
            }
        }
        setMeasuredDimension(width, height);

    }

    private int measureHeight() {
        return mRadius * 2;
    }

    private int measureWidth() {
        return mRadius * 2;
    }


    /*

     */
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth * 1.0f / 4);

        int width = getWidth();
        int height = getHeight();

        canvas.drawCircle(width / 2, height / 2,
                width / 2 - getPaddingLeft() - mPaint.getStrokeWidth() / 2, mPaint);

        mPaint.setStrokeWidth(mLineWidth);
        canvas.save();
        canvas.translate(getPaddingLeft(), getPaddingTop());
        float angle = mProgress * 1.0f / 100 * 360;
        canvas.drawArc(new RectF(0, 0, width - getPaddingLeft() * 2, height - getPaddingLeft() * 2), 0, angle, false, mPaint);
        canvas.restore();

        String text = mProgress + "%";
//        text = "张鸿洋";
        mPaint.setStrokeWidth(0);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(mTextSize);
        int y = getHeight() / 2;
        Rect bound = new Rect();
        mPaint.getTextBounds(text, 0, text.length(), bound);
        int textHeight = bound.height();
        canvas.drawText(text, 0, text.length(), getWidth() / 2, y + textHeight / 2, mPaint);

        mPaint.setStrokeWidth(0);
//        canvas.drawLine(0, height / 2, width, height / 2, mPaint);

    }


    //固定模板
    private static final String INSTANCE = "instance";
    private static final String KEY_PROGRESS = "key_progress";

    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putInt(KEY_PROGRESS, mProgress);
        bundle.putParcelable(INSTANCE, super.onSaveInstanceState());
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            Parcelable parcelable = bundle.getParcelable(INSTANCE);
            super.onRestoreInstanceState(parcelable);
            mProgress = bundle.getInt(KEY_PROGRESS);
            return;
        }
        super.onRestoreInstanceState(state);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:hyman="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    tools:context="MainActivity">

    <com.jinanchang.myviewapplication.RoundProgressBar
        android:id="@+id/id_pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
        android:progress="0"
        android:textSize="18sp"
        hyman:color="#ea22e4"
        hyman:radius="36dp"
    />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundProgressBar">
        <attr name="color" format="color"></attr>
        <attr name="line_width" format="dimension"></attr>
        <attr name="radius" format="dimension"></attr>
        <attr name="android:progress"></attr>
        <attr name="android:textSize"></attr>
    </declare-styleable>

    <declare-styleable name="TestView">
        <attr name="test_boolean" format="boolean"></attr>
        <attr name="test_string" format="string"></attr>
        <attr name="test_integer" format="integer"></attr>
        <attr name="test_enum" format="enum">
            <enum name="top" value="1"></enum>
            <enum name="bottom" value="2"></enum>
        </attr>
        <attr name="test_dimension" format="dimension"></attr>
    </declare-styleable>
</resources>




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的圆形进度条自定义View实现: ```java public class CircleProgressBar extends View { private float mProgress = 0; // 当前进度值 private float mMax = 100; // 最大进度值 private int mCircleWidth = 10; // 圆环宽度 private int mCircleColor = Color.GRAY; // 圆环颜色 private int mProgressColor = Color.BLUE; // 进度条颜色 private Paint mPaint; public CircleProgressBar(Context context) { super(context); init(); } public CircleProgressBar(Context context, AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar); mCircleWidth = ta.getDimensionPixelSize(R.styleable.CircleProgressBar_circleWidth, 10); mCircleColor = ta.getColor(R.styleable.CircleProgressBar_circleColor, Color.GRAY); mProgressColor = ta.getColor(R.styleable.CircleProgressBar_progressColor, Color.BLUE); ta.recycle(); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int radius = getWidth() / 2 - mCircleWidth / 2; // 画圆环 mPaint.setColor(mCircleColor); mPaint.setStrokeWidth(mCircleWidth); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(centerX, centerY, radius, mPaint); // 画进度条 mPaint.setColor(mProgressColor); mPaint.setStrokeWidth(mCircleWidth); mPaint.setStyle(Paint.Style.STROKE); RectF rectF = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius); canvas.drawArc(rectF, -90, 360 * mProgress / mMax, false, mPaint); } public void setProgress(float progress) { mProgress = progress; invalidate(); } public void setMax(float max) { mMax = max; invalidate(); } } ``` 其中,我们可以设置圆环的宽度、圆环颜色、进度条颜色等属性。在onDraw()方法中,我们先画出圆环,然后再画出进度条进度条的弧度根据当前进度值和最大进度值计算得出。 使用时,可以在布局文件中添加如下代码: ```xml <com.example.customview.CircleProgressBar android:id="@+id/circle_progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" app:circleColor="#FFA500" app:circleWidth="20dp" app:progressColor="#00BFFF" /> ``` 然后在代码中设置进度值即可: ```java CircleProgressBar circleProgressBar = findViewById(R.id.circle_progress_bar); circleProgressBar.setMax(100); circleProgressBar.setProgress(50); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值