我正在参加创作者训练营第5期,点击了解活动概况

👨‍🎓作者简介:一位喜欢写作,计科专业大二菜鸟

🏡个人主页:starry陆离

🕒首发日期:2022年8月3日星期三

🌌上期文章:『Android根底入门』dataBinding的简略使用

📚订阅专栏:『Android根底入门』

假如文章有帮到你的话记得点赞👍+收藏💗支撑一下哦


『Android根底入门』自定义view控件画一个五彩斑斓的黑圈圈

1.前言

今天咱们来完成一个好玩的小功用,自定义一个view控件在随机方位画一个随机色彩的圆圈,并完成点击事情监听移除与清空功用

首要来康康作用吧

ad2

2.xml布局规划

首要咱们创立一个空项目,简略的来完成这个xml文件布局

布局主要分为两个部分,上大半部分是咱们用来画图的view布局,咱们的图画最终会烘托到这个部分。

下半部分是一个嵌套的束缚布局,布局中放置三个button,分别来完成在view布局中增加一个圆,移除一个圆以及清空所有的圆

image-20220706094151831

activity_main.xml源代码

 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
 ​
     <View
     android:id="@+id/view"
     android:background="@color/black"
     android:layout_width="match_parent"
     android:layout_height="500dp"
     app:layout_constraintEnd_toEndOf="parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent" />
 ​
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_marginTop="10dp"
         android:layout_marginBottom="10dp"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/view">
 ​
         <Button
             android:id="@+id/moveBtn"
             android:layout_width="300dp"
             android:layout_height="60dp"
             android:text="move"
             app:layout_constraintBottom_toTopOf="@+id/clearBtn"
             app:layout_constraintStart_toStartOf="@+id/addBtn"
             app:layout_constraintTop_toBottomOf="@+id/addBtn" />
 ​
         <Button
             android:id="@+id/addBtn"
             android:layout_width="300dp"
             android:layout_height="60dp"
             android:text="add"
             app:layout_constraintBottom_toTopOf="@+id/moveBtn"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
 ​
         <Button
             android:id="@+id/clearBtn"
             android:layout_width="300dp"
             android:layout_height="60dp"
             android:text="clear"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="@+id/moveBtn"
             app:layout_constraintTop_toBottomOf="@+id/moveBtn" />
     </androidx.constraintlayout.widget.ConstraintLayout>
 ​
 </androidx.constraintlayout.widget.ConstraintLayout>

3.Activity点击事情

image-20220706100914999

 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
     @Override
     protected void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         findViewById(R.id.addBtn).setOnClickListener(this);
         findViewById(R.id.moveBtn).setOnClickListener(this);
         findViewById(R.id.clearBtn).setOnClickListener(this);
     }
     @Override
     public void onClick(View view) {
         if(view.getId()==R.id.addBtn){
             Toast.makeText(MainActivity.this,"add",Toast.LENGTH_SHORT).show();
         }else if(view.getId()==R.id.clearBtn){
             Toast.makeText(MainActivity.this,"clear",Toast.LENGTH_SHORT).show();
         }else if(view.getId()==R.id.moveBtn){
             Toast.makeText(MainActivity.this,"Move",Toast.LENGTH_SHORT).show();
         }
     }
 }

4.自定义view画布

咱们需求自定义一个画布类

image-20220706101345994

用自定义的CircleView继承View父类替换View控件

image-20220706101318959

View类中有onDraw()办法,咱们经过重写此办法完成在view布局上作画

image-20220706101605897

image-20220706101746831

有了画布天然要有画布,创立一支红色的画笔,调用画圆的办法drawCircle()

 //有了画布canvas,咱们还需求一支画笔
 Paint paint=new Paint();
 paint.setColor(Color.RED);

image-20220706102037972

 public class CircleView extends View {
     public CircleView(Context context) {
         super(context);
     }
 ​
     public CircleView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 ​
     @Override
     protected void onDraw(Canvas canvas){
         super.onDraw(canvas);
         //设置画布的背景色为黑色
         this.setBackgroundColor(Color.BLACK);
 ​
         //有了画布canvas,咱们还需求一支画笔
         Paint paint=new Paint();
         paint.setColor(Color.RED);
         //用画笔paint画一个圆心坐标为(400,500)的半径为100的圆
         canvas.drawCircle(400,500,100,paint);
     }
 }
 ​

image-20220706102126176

为什么运行加载,画布上就出现了一个红色的圆呢?由于在加载CircleView的时分会自动履行onDraw()办法且默认只履行一次;

为了不让一运行就出现圆,而是经过咱们的点击事情出现圆,咱们定义一个布尔值变量,在MainActivity中经过按钮的点击事情增加圆

ad1

5.完成画多个圆

画多个圆就需求将圆保存在一个列表List或者是数组中,然后再到onDraw()办法中依次烘托这些圆到view布局上

除此之外,咱们生成的圆目标的色彩和方位应该是随机的,并且圆的方位不应该超出画布的规模

因而咱们创立多个类,分别来完成这些功用

5.1圆目标

 static class Circle{
         float x,y;
         final float RADIUS=120;
         final int color;
 ​
         public Circle(float x, float y,int color) {
             this.x = x;
             this.y = y;
             this.color=color;
         }
     }

5.2随机色彩

Color.argb(200,r,g,b)是回来是一个代表色彩的整数值,argb()里的四个参数就是a(透明度),r,g,b(红,绿,蓝三原色)的值

 public static int getRandomColor(){
         Random random=new Random();
         int r=random.nextInt(256);
         int g=random.nextInt(256);
         int b=random.nextInt(256);
         return Color.argb(200,r,g,b);
     }

5.3随机方位增加一个圆

 /*
     * 增加一个圆
     * */
     public void addCircle(){
         //在view的宽高规模内,随机生成一个坐标
         float x=(float) (Math.random()*viewWidth);
         float y=(float) (Math.random()*viewHeight);
         //束缚条件,不让圆超出画布规模
         //宽度束缚
         if(x<120f){
             x=120f;
         }else if(x>viewWidth-120f){
             x=viewWidth-120f;
         }
         //高度束缚
         if(y<120f){
             y=120f;
         }else if(y>viewHeight-120f){
             y=viewHeight-120f;
         }
         //将圆增加到列表中
         CircleView.Circle circle=new CircleView.Circle(x,y,CircleView.getRandomColor());
         CircleView.circleList.add(circle);
     }

5.4移除与清空

     public void removeCircle(){
         if(!circleList.isEmpty()){
             circleList.remove(circleList.size()-1);
         }
     }
     public void clearCircle(){
         circleList.clear();
     }

5.5在Activity中监听点击事情

留意最后一行代码:在子线程中从头履行一次 circleView.postInvalidate();由于咱们之前说过自定义的的CircleView类中的onDraw()办法在加载这个控件时只会履行一次,而咱们每一次点击都要重写烘托view,所以需求在每一次用户点击操作后在子线程中从头烘托view控件,这样才能让用户看到实时的作用

 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 ​
     private CircleView circleView;
     @Override
     protected void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         findViewById(R.id.addBtn).setOnClickListener(this);
         findViewById(R.id.moveBtn).setOnClickListener(this);
         findViewById(R.id.clearBtn).setOnClickListener(this);
 ​
         circleView=findViewById(R.id.view);
     }
     @Override
     public void onClick(View view) {
         if(view.getId()==R.id.addBtn){
             Toast.makeText(MainActivity.this,"add",Toast.LENGTH_SHORT).show();
             circleView.addCircle();
         }else if(view.getId()==R.id.clearBtn){
             Toast.makeText(MainActivity.this,"clear",Toast.LENGTH_SHORT).show();
             circleView.clearCircle();
         }else if(view.getId()==R.id.moveBtn){
             Toast.makeText(MainActivity.this,"Move",Toast.LENGTH_SHORT).show();
             circleView.removeCircle();
         }
         //在子线程中从头履行一次
         circleView.postInvalidate();
     }
 }

6.作用展示

ad2

7.完好代码

7.1MainActivity

 package com.hnucm.ad0301;
 ​
 import androidx.appcompat.app.AppCompatActivity;
 ​
 import android.os.Bundle;
 import android.view.View;
 import android.widget.Toast;
 ​
 ​
 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 ​
     private CircleView circleView;
     @Override
     protected void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         findViewById(R.id.addBtn).setOnClickListener(this);
         findViewById(R.id.moveBtn).setOnClickListener(this);
         findViewById(R.id.clearBtn).setOnClickListener(this);
 ​
         circleView=findViewById(R.id.view);
     }
     @Override
     public void onClick(View view) {
         if(view.getId()==R.id.addBtn){
             Toast.makeText(MainActivity.this,"add",Toast.LENGTH_SHORT).show();
             circleView.addCircle();
         }else if(view.getId()==R.id.clearBtn){
             Toast.makeText(MainActivity.this,"clear",Toast.LENGTH_SHORT).show();
             circleView.clearCircle();
         }else if(view.getId()==R.id.moveBtn){
             Toast.makeText(MainActivity.this,"Move",Toast.LENGTH_SHORT).show();
             circleView.removeCircle();
         }
         //在子线程中从头履行一次
         circleView.postInvalidate();
     }
 }

7.2CircleView

 package com.hnucm.ad0301;
 ​
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.util.AttributeSet;
 import android.view.View;
 ​
 import androidx.annotation.Nullable;
 ​
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 ​
 public class CircleView extends View {
 ​
     public static List<Circle>circleList=new ArrayList<>();
     public float viewWidth;
     public float viewHeight;
 ​
     public CircleView(Context context) {
         super(context);
     }
 ​
     public CircleView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 ​
     @Override
     protected void onDraw(Canvas canvas){
         super.onDraw(canvas);
 ​
         //获取画布的宽高
         viewWidth=canvas.getWidth();
         viewHeight=canvas.getHeight();
         //设置画布的背景色为黑色
         this.setBackgroundColor(Color.BLACK);
         //有了画布canvas,咱们还需求一支画笔
         Paint paint=new Paint();
 ​
         //遍历列表画圆
         for(int i=0;i<circleList.size();++i){
 ​
             Circle circle=circleList.get(i);
             paint.setColor(circle.color);
             canvas.drawCircle(circle.x,circle.y,circle.RADIUS,paint);
         }
     }
 ​
     /*
     * 增加一个圆
     * */
     public void addCircle(){
         //在view的宽高规模内,随机生成一个坐标
         float x=(float) (Math.random()*viewWidth);
         float y=(float) (Math.random()*viewHeight);
         //束缚条件,不让圆超出画布规模
         //宽度束缚
         if(x<120f){
             x=120f;
         }else if(x>viewWidth-120f){
             x=viewWidth-120f;
         }
         //高度束缚
         if(y<120f){
             y=120f;
         }else if(y>viewHeight-120f){
             y=viewHeight-120f;
         }
         //将圆增加到列表中
         CircleView.Circle circle=new CircleView.Circle(x,y,CircleView.getRandomColor());
         CircleView.circleList.add(circle);
     }
 ​
     public void removeCircle(){
         if(!circleList.isEmpty()){
             circleList.remove(circleList.size()-1);
         }
     }
     public void clearCircle(){
         circleList.clear();
     }
     static class Circle{
         float x,y;
         final float RADIUS=120;
         final int color;
 ​
         public Circle(float x, float y,int color) {
             this.x = x;
             this.y = y;
             this.color=color;
         }
     }
 ​
     public static int getRandomColor(){
         Random random=new Random();
         int r=random.nextInt(256);
         int g=random.nextInt(256);
         int b=random.nextInt(256);
         return Color.argb(200,r,g,b);
     }
 }
 ​

7.3activity_main.xml

 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
 ​
     <com.hnucm.ad0301.CircleView
     android:id="@+id/view"
     android:layout_width="match_parent"
     android:layout_height="500dp"
     app:layout_constraintEnd_toEndOf="parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent" />
 ​
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_marginTop="10dp"
         android:layout_marginBottom="10dp"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/view">
 ​
         <Button
             android:id="@+id/moveBtn"
             android:layout_width="301dp"
             android:layout_height="59dp"
             android:text="move"
             app:layout_constraintBottom_toTopOf="@+id/clearBtn"
             app:layout_constraintStart_toStartOf="@+id/addBtn"
             app:layout_constraintTop_toBottomOf="@+id/addBtn" />
 ​
         <Button
             android:id="@+id/addBtn"
             android:layout_width="300dp"
             android:layout_height="60dp"
             android:text="add"
             app:layout_constraintBottom_toTopOf="@+id/moveBtn"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
 ​
         <Button
             android:id="@+id/clearBtn"
             android:layout_width="300dp"
             android:layout_height="60dp"
             android:text="clear"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="@+id/moveBtn"
             app:layout_constraintTop_toBottomOf="@+id/moveBtn" />
     </androidx.constraintlayout.widget.ConstraintLayout>
 ​
 </androidx.constraintlayout.widget.ConstraintLayout>