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

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

一、自定义属性

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

【Android】之【自定义View实践】_第1张图片
内容如下:


<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属性的命名空间的方式


<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地址输入框

你可能感兴趣的:(#,Android,android)