当前位置: 首页 > article >正文

【Android】之【自定义View实践】

这里以一个进度条的加载为例子,先看效果(运行效果是动态变化的)

一、自定义属性

首先在res->values目录下新建attrs资源文件,如下图:

在这里插入图片描述
内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="ProgressBar">
<!--       圆弧的颜色-->
       <attr name="innerBackground" format="color"/>
       <attr name="outerBackground" format="color"/>
<!--       圆弧的大小-->
       <attr name="roundWidth" format="dimension"/>
<!--       字体大小、颜色-->
       <attr name="progressTextSize" format="dimension"/>
       <attr name="progressTextColor" format="color"/>

   </declare-styleable>
</resources> 

二、在Layout文件中引用自定义View

这里要注意引入自定义View属性的命名空间的方式

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="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"
    android:orientation="vertical">

    <com.example.viewtest.ProgressBar
        android:id="@+id/progress_bar"
        custom:progressTextSize="20sp"
        custom:progressTextColor="#ff7788"
        custom:innerBackground="#ee9933"
        custom:outerBackground="#229933"
        custom:roundWidth="5dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

<!--    测试按钮-->
    <Button
        android:id="@+id/test"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_marginTop="30dp"
        android:layout_gravity="center"
        android:text="测试"
        android:background="@color/black"
        android:textColor="@color/white"/>

</LinearLayout> 

三、 创建一个类继承View

public class ProgressBar extends View {

    private static final String TAG = "ProgressBar";

    //声明自定义属性
    private int mInnerBackground = Color.RED;
    private int mOuterBackground = Color.GREEN;
    private int mRoundWidth = 30;
    private int mProgressTextSize = 70;
    private int mProgressTextColor = Color.GREEN;

    //创建画笔
    private Paint mInnerPaint=new Paint();
    private Paint mOutterPaint = new Paint();
    private Paint mTextPaint = new Paint();

    //进度条的最大进度
    private int mMax=100;
    //当前进度
    private int mCurrentProgress=50;

    //new对象的时候调用该方法
    public ProgressBar(Context context) {
        super(context);
    }

    //在布局中使用   xml文件中使用
    public ProgressBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //  在layout中调用  自定义样式、有固定style的时候使用
    public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取自定义属性
        TypedArray array= context.obtainStyledAttributes(attrs,R.styleable.ProgressBar);
        mInnerBackground=array.getColor(R.styleable.ProgressBar_innerBackground,mInnerBackground);
        mOuterBackground=array.getColor(R.styleable.ProgressBar_outerBackground,mOuterBackground);
        mProgressTextColor= array.getColor(R.styleable.ProgressBar_progressTextColor,mProgressTextColor);
        mProgressTextSize= (int) array.getDimension(R.styleable.ProgressBar_progressTextSize,sp2px(mProgressTextSize));
        mRoundWidth= (int) array.getDimension(R.styleable.ProgressBar_roundWidth,dip2px(mRoundWidth));
        array.recycle();

//        mInnerPaint = new Paint();

    }

    private float sp2px(int sp) {
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,getResources().getDisplayMetrics());
    }

    private float dip2px(int dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
    }

    public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    /**
     * onMeasure
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //拿到宽高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        //存储view 的宽高 在这里取最小值 保证是一个正方形即可
        setMeasuredDimension(Math.min(width,height),Math.min(width,height));
    }

    /**
     * onDraw
     */

    @Override
    protected void onDraw(Canvas canvas) {

        mInnerPaint.setAntiAlias(true);//抗锯齿
        mInnerPaint.setColor(mInnerBackground);
        mInnerPaint.setStrokeWidth(mRoundWidth);//线条宽度
        mInnerPaint.setStyle(Paint.Style.STROKE);//空心

        mOutterPaint.setAntiAlias(true);//抗锯齿
        mOutterPaint.setColor(mOuterBackground);
        mOutterPaint.setStrokeWidth(mRoundWidth);//线条宽度
        mOutterPaint.setStyle(Paint.Style.STROKE);//空心

        mOutterPaint.setAntiAlias(true);//抗锯齿
        mTextPaint.setColor(mProgressTextColor);
        mTextPaint.setStrokeWidth(mRoundWidth);
        mTextPaint.setTextSize(mProgressTextSize);

//        super.onDraw(canvas);
        //先画内圆
        int center = getWidth()/2;
        Log.d(TAG, "onDraw: "+center);

       canvas.drawCircle(center,center,center-mRoundWidth/2,mInnerPaint);

        //再画外层圆
        RectF rectF=new RectF(0+mRoundWidth/2,0+mRoundWidth/2,getWidth()-mRoundWidth/2,getHeight()-mRoundWidth/2);
        if (mCurrentProgress==0){
            return;
        }
        float percent = (float) mCurrentProgress/mMax;
        canvas.drawArc(rectF,0,percent*360,false,mOutterPaint);

        //画进度文字
        String text=((int)(percent*100))+"%";
        Rect textBounds=new Rect();
        mTextPaint.getTextBounds(text,0,text.length(),textBounds);

        int x= getWidth()/2-textBounds.width()/2;
        Paint.FontMetricsInt fontMetrics=mTextPaint.getFontMetricsInt();
        int dy=(fontMetrics.bottom-fontMetrics.top)/2 - fontMetrics.bottom;
        int baseLineY=getHeight()/2+dy;

        canvas.drawText(text,x,baseLineY,mTextPaint);
    }


    public synchronized void setMax(int max){
        if (max<0){
            Log.d(TAG, "setMax: max<0 不合法"+max);
        }
        this.mMax=max;
    }

    public synchronized void setProgress(int process){
        if (process<0){
            Log.d(TAG, "setMax: max<0 不合法"+process);
        }
        this.mCurrentProgress=process;
        invalidate();//刷新
    }
} 

实现自定义View有那些方式
① 继承View,例如折线图等。
② 继承ViewGroup,例如流式布局等。
③ 继承现有的View,例如TextView、ListView等。

四、在Activity中调用

public class MainActivity extends AppCompatActivity {

    private ProgressBar mProgressBar;

    private Button mTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();
    }

    private void initData() {
        mTest=findViewById(R.id.test);

        mProgressBar=(ProgressBar) findViewById(R.id.progress_bar);
        mProgressBar.setMax(1000);

        mTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test();
            }
        });
    }

    private void test() {
        //值不断变化
        ValueAnimator animator= ObjectAnimator.ofFloat(0,1000);
        animator.setDuration(3000);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float progress = (float) animation.getAnimatedValue();
                mProgressBar.setProgress((int) progress);
            }
        });
    }
} 

五、参考

  • Android自定义View
  • 【Android学习】—自定义view的流程以及实践
  • Android自定义View入门(一)
  • Android 自定义View 之 Mac地址输入框

http://www.kler.cn/a/6409.html

相关文章:

  • 由于这些关键原因,我总是手边有一台虚拟机
  • ajax中get和post的区别,datatype返回的数据类型有哪些?web开发中数据提交的几种方式,有什么区别。
  • tcp 的重传,流量控制,拥塞控制
  • 大模型(LLM)提示工程(Prompt Engineering)初识
  • Marscode AI辅助编程
  • java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
  • Android 开发 错误 Execution failed for task ‘:app:processDebugMainManifest‘.
  • 【Python实战】Python对中国500强排行榜数据进行可视化分析
  • Qt音视频开发33-不同库版本不同位数的库和头文件的引用
  • 【Ruby学习笔记】7.Ruby 循环及方法
  • 五分钟带你了解 计算机操作系统——内存管理(万字详解·图文)
  • 已经提了离职,还有一周就走,公司突然把我移出企业微信,没法考勤打卡, 还要继续上班吗?...
  • 自定义 Jackson 的 ObjectMapper, springboot多个模块共同引用,爽
  • 章节3 多姿多彩的Python数据可视化
  • 后端开发选择RUST,有被爽到
  • RCIE练习题1之IPSec 配置
  • C++继承
  • 牛客SQL练习篇题解
  • Python tkinter 制作文章搜索软件
  • 多线程初阶——线程安全
  • 【Django配置管理】settings文件配置
  • 40了解云计算平台的高可用架构,如 AWS 的多可用区、GCP 的负载均衡器
  • 【数据结构和算法的概述】-01
  • 前端基础HTML、CSS--6(CSS-3)
  • Java数组
  • 微波雷达人体感应开关模块 智能感应探测器 XBG-M555