安卓自定义view
一、view的测量
1.测量的模式:
- EXACTLY :精确值模式,当把空间的layout_width属性或者layout_height的属性设置为具体的数值时,或者指定为match_parent时候,系统就是使用的EXACTLY 模式;
- AT_MOST: 最大值模式,当空间的layout_width属性或者layout_height属性指定为wrap_content时候,控件大小跟着控件的内容变化而变化,此时控件的尺寸只要不超过父控件允许的最大尺寸即可。
- UNSPECIFIED: 这个属性比较奇怪,不指定其测量大小模式,view想多大就多大,通常情况下只有在绘制自定义view的时候才会使用
2.简单演示如何进行view的测量
1.自定义一个类继承自View
public class one extends View {
public one(Context context) {
super(context);
}
public one(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public one(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//重写onMeasure方法来进行测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
//测量高度
private int measureHeight(int heightMeasureSpec) {
//第一步,从MeasureSpec对象中提取出具体的测量模式和大小
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
int result = 0;
//检测是否是精确值模式
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}else{
//如果不是就自己设定
result = 200;
if (specMode== MeasureSpec.AT_MOST) {
result = Math.min(result,specSize);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
int result = 0;
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}else{
result = 200;
if (specMode== MeasureSpec.AT_MOST) {
result = Math.min(result,specSize);
}
}
return result;
}
}
2.新建一个xml文件来作为自定义view的载体
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="400px"
android:layout_height="400px"
android:background="#00ff0f"/>
</LinearLayout>
3.在main活动的xml文件下添加自定义的控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<com.example.customize_learning.learning.one
android:layout_width="400px"
android:layout_height="400px"/>
</LinearLayout>
主要是进行测量,测量的主要步骤就是重写onMeasure方法,在代码中有很详细的注释
二、view的绘制
简单来说就是继承view之后再重写onDraw方法,并在Canvas对象上来绘制需要的图形
以绘制一个闪光的文字作为例子
1.新建一个类及其xml文件
public class MyTextView extends TextView {
private int mViewWidth;
private Paint mPaint;
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private int mTranslate;
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mPaint = new Paint();
if (mViewWidth == 0){
mViewWidth = getMeasuredWidth();
if (mViewWidth>0) {
mPaint = getPaint();
/**
* LinearGradient构造方法中的参数int[] color:
* 第一个元素:发光字体闪过后所显示的字体颜色,这里给定与第三个元素一样
* 第二个元素:字体发光的颜色
* 第三个元素:原字体显示的颜色
*
* mViewWidth:设置发光的宽度
* */
mLinearGradient = new LinearGradient(0,
0,
mViewWidth,
0,
new int[]{0x22ffffff, 0xffffffff, 0x22ffffff},
null,
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
//创建矩形
mGradientMatrix = new Matrix();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
mTranslate += mViewWidth / 5;
if (mTranslate > mViewWidth * 2) {
mTranslate = -mViewWidth;
}
mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
//控制闪过的时间
postInvalidateDelayed(80);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是一个会闪烁的TextView"/>
</FrameLayout>
2.将自定义控件添加到主活动的xml文件下面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<com.example.customize_learning.learning.MyTextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="30dp"/>
</LinearLayout>
三、组合自定义控件
1.先建一个类,因为是组合控件,使其继承自RelativeLayout
2建一个xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="35dp">
<Button
android:id="@+id/plus_btn"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:textSize="18sp"
android:background="@drawable/selector_left"
android:text="+">
</Button>
<EditText
android:id="@+id/values_edt"
android:gravity="center"
android:layout_gravity="center"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="0"/>
<Button
android:id="@+id/minus_btn"
android:background="@drawable/selector_right"
android:layout_width="100dp"
android:text="-"
android:textSize="18sp"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
</LinearLayout>
3.设置按钮按下和松开时候不同的效果
(1)设置左右两边按钮的自定义背景(按下时)
左边
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--纯色填充-->
<solid android:color="#DFE8DF"/>
<!--设置圆角-->
<corners android:bottomLeftRadius="10dp"
android:topLeftRadius="10dp"/>
<!--渐变色-->
<gradient android:startColor="#EC1426"
android:angle="270"
android:centerColor="#C64B7C"
android:endColor="#71363D"
android:type="linear"/>
</shape>
右边
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#DFE8DF"/>
<corners android:bottomRightRadius="10dp"
android:topRightRadius="10dp"/>
<!--渐变色-->
<gradient android:startColor="#EC1426"
android:angle="270"
android:centerColor="#C64B7C"
android:endColor="#71363D"
android:type="linear"/>
</shape>
(2)设置不触碰时左右两边按钮的自定义背景
左边
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--纯色填充-->
<solid android:color="#DFE8DF"/>
<!--设置圆角-->
<corners android:bottomLeftRadius="10dp"
android:topLeftRadius="10dp"/>
<!--渐变色-->
<gradient android:startColor="#14EC14"
android:angle="270"
android:centerColor="#4BC64B"
android:endColor="#367143"
android:type="linear"/>
</shape>>
右边
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#DFE8DF"/>
<corners android:bottomRightRadius="10dp"
android:topRightRadius="10dp"/>
<!--渐变色-->
<gradient android:startColor="#14EC14"
android:angle="270"
android:centerColor="#4BC64B"
android:endColor="#367143"
android:type="linear"/>
</shape>
(3)设置一个选择器,实现按下和松开展示不同的背景
左边
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/shape_number_btn_bg_left_presses"/>
<item android:drawable="@drawable/shape_number_btn_bg_left"/>
</selector>
右边
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/shape_number_btn_bg_right_pressed"/>
<item android:drawable="@drawable/shape_number_btn_bg_right"/>
</selector>
4.把自定义控件添加到主活动里边去
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<com.example.customize_learning.customview.InputNumberView
android:id="@+id/input_number"
android:layout_height="100dp"
android:layout_width="match_parent" />
</LinearLayout>
5.在建的那个类中填写代码
package com.example.customize_learning.customview;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import com.example.customize_learning.R;
public class InputNumberView extends RelativeLayout {
private int mCurrentNumber = 0;
private View addBtn;
private EditText valueEdt;
private View minBtn;
private OnNumberChange mOnNumberChange = null;
public InputNumberView(Context context) {
this(context,null);
}
public InputNumberView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public InputNumberView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//绑定布局
initView(context);
//处理事件
setEvent();
}
private void setEvent() {
//实现值的增加
addBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCurrentNumber++;
updateValue();
}
});
//实现值的减少
minBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCurrentNumber--;
updateValue();
}
});
}
//数据的更新
private void updateValue() {
valueEdt.setText(String.valueOf(mCurrentNumber));
}
private void initView(Context context) {
//添加布局
View view = LayoutInflater.from(context).inflate(R.layout.input_value_layout,this,false);
addView(view);
addBtn = this.findViewById(R.id.plus_btn);
valueEdt = this.findViewById(R.id.values_edt);
minBtn = this.findViewById(R.id.minus_btn);
}
public int getNumber() {
return mCurrentNumber;
}
public void setNumber(int mCurrentNumber) {
this.mCurrentNumber = mCurrentNumber;
this.updateValue();
}
//暴露接口
public void SetOnNumberChange(OnNumberChange onNumberChange){
this.mOnNumberChange = onNumberChange;
}
public interface OnNumberChange{
void onNumberChange(int value);
}
}
里边代码很详尽,包含了暴露接口、处理事件的过程
6.在主活动java文件下处理事件
public class MainActivity extends AppCompatActivity implements InputNumberView.OnNumberChange {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InputNumberView inputNumberView = this.findViewById(R.id.input_number);
inputNumberView.SetOnNumberChange(this);
}
//调用接口处理事件
@Override
public void onNumberChange(int value) {
Log.d(TAG, "current value is == > "+value);
}
}
四、获取自定义view的属性
1.在values文件下面新建一个attrs的文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="InputNumberView">
<attr name="max" format="integer"/>
<attr name="min" format="integer"/>
<attr name="step" format="integer"/>
<attr name="disable" format="boolean"/>
<attr name="btnColor" format="reference"/>
<attr name="valueSize" format="integer"/>
</declare-styleable>
</resources>
- Integer,比如说行数,TextView的maxLine,就是Integer类型
- enum,枚举类型,比如说gravity,left,top,bottom,center,right这些是枚举类型
- boolean,布尔类型,比如说layout_alignParentRight
- dimension,尺寸比如说size,margin_left这些,单位为px,dp,sp这些
- color,这个大家都清楚了,颜色嘛,比如说background,比如说textColor
- flags,标记,比如说我们学习activity声明周期时的configChanges
- float,浮点数,也就是小数,比如说,透明度alpha
- fraction,百分数,比如说动画的开始位置,fromDx
- refrence,引用,比如说background,src,有同学可能有疑问了,background可以是color又可以是refrence,怎么整呢? 其实是可以多个的哈,比如说:name=“switch_time” format=“integer|float”,可以是Integer类型,或者float类型
- string,这个最简单了,比如说text
2.获取属性
public InputNumberView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取相关属性
initAttrs(context, attrs);
//绑定布局
initView(context);
//处理事件
setEvent();
}
//获取相关属性
private void initAttrs(Context context, AttributeSet attrs) {
typedArray = context.obtainStyledAttributes(attrs, R.styleable.InputNumberView);
mMax = typedArray.getInt(R.styleable.InputNumberView_max, 0);
mMin = typedArray.getInt(R.styleable.InputNumberView_min, 0);
mStep = typedArray.getInt(R.styleable.InputNumberView_step, 0);
mValue = typedArray.getInt(R.styleable.InputNumberView_valueSize, 0);
mDisable = typedArray.getBoolean(R.styleable.InputNumberView_disable, false);
typedArray.getResourceId(R.styleable.InputNumberView_btnColor, -1);
Log.d(TAG, "mMax === >: " + mMax);
Log.d(TAG, "mMin === >: " + mMin);
Log.d(TAG, "mStep === >: " + mStep);
Log.d(TAG, "mValue === >: " + mValue);
Log.d(TAG, "mDisable === >: " + mDisable);
typedArray.recycle();
}
3.在引入该自定义控件的xml文件下使用属性
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
xmlns:hsk="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:orientation="vertical">
<com.example.customize_learning.customview.InputNumberView
android:id="@+id/input_number"
android:layout_height="match_parent"
android:layout_width="match_parent"
hsk:max ="30"
hsk:min="-30"
hsk:step="5"
hsk:disable="false"
hsk:valueSize="15"
hsk:btnColor="@drawable/selector_left"/>
</LinearLayout>
五、自定义viewgroup
1.首先创建一个类继承自viewgroup,实现方法
public class FlowLayout extends ViewGroup {
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
2.获取相关属性
- 在values文件夹下面新建一个attrs文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Flowlayout">
<attr name="InputNumberView_max" format="integer"/>
<attr name="itemHorizontalMargin" format="dimension"/>
<attr name="itemVerticalMargin" format="dimension"/>
<attr name="textMaxLine" format="integer"/>
<attr name="textColor" format="color"/>
<attr name="borderColor" format="color"/>
<attr name="borderRadius" format="dimension"/>
</declare-styleable>
</resources>
- 在该类下面获取相关属性
public class FlowLayout extends ViewGroup {
public static final int DEFAULT_LINE = 3;
public static final int DEFAULT_HORZONTAL_MARGIN = 5;
public static final int DEFAULT_VERTIAL_MARGIN = 5;
public static final int DEFAULT_TEXT_MAX_LENGTH = 20;
public static final int DEFAULT_BORDER_RADIUS = 20;
private TypedArray typedArray;
private int anInt;
private float horizontalMargin;
private float verticalMargin;
private int mMaxLine;
private int textcolor;
private int borderradius;
private List<String> mList = null;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取属性
typedArray = context.obtainStyledAttributes(attrs, R.styleable.Flowlayout);
//maxLine
anInt = typedArray.getInt(R.styleable.Flowlayout_InputNumberView_max, DEFAULT_LINE);
//itemHorizontalMargin
horizontalMargin = typedArray.getDimension(R.styleable.FloeLayout_itemHorizontalMargin, DEFAULT_HORZONTAL_MARGIN);
//itemVerticalMargin
verticalMargin = typedArray.getDimension(R.styleable.FloeLayout_itemHorizontalMargin, DEFAULT_VERTIAL_MARGIN);
//textMaxLine
mMaxLine = typedArray.getInt(R.styleable.FloeLayout_maxLine, DEFAULT_TEXT_MAX_LENGTH);
//textColor
textcolor = typedArray.getColor(R.styleable.Flowlayout_textColor, getResources().getColor(R.color.black));
//borderColor
typedArray.getColor(R.styleable.Flowlayout_borderColor, getResources().getColor(R.color.black));
//borderRadius
borderradius = typedArray.getInt(R.styleable.Flowlayout_borderRadius, DEFAULT_BORDER_RADIUS);
typedArray.recycle();
}
public TypedArray getTypedArray() {
return typedArray;
}
public void setTypedArray(TypedArray typedArray) {
this.typedArray = typedArray;
}
public int getAnInt() {
return anInt;
}
public void setAnInt(int anInt) {
this.anInt = anInt;
}
public float getHorizontalMargin() {
return horizontalMargin;
}
public void setHorizontalMargin(float horizontalMargin) {
this.horizontalMargin = horizontalMargin;
}
public float getVerticalMargin() {
return verticalMargin;
}
public void setVerticalMargin(float verticalMargin) {
this.verticalMargin = verticalMargin;
}
public int getmMaxLine() {
return mMaxLine;
}
public void setmMaxLine(int mMaxLine) {
this.mMaxLine = mMaxLine;
}
public int getTextcolor() {
return textcolor;
}
public void setTextcolor(int textcolor) {
this.textcolor = textcolor;
}
public int getBorderradius() {
return borderradius;
}
public void setBorderradius(int borderradius) {
this.borderradius = borderradius;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
- 把子view添加进来
有两种方法,第一种是直接在xml布局中进行添加,第二种就是想listview一样使用适配器来进行添加
- 我们使用第二种
public void settextList(List<String> data){
this.mList.clear();
this.mList.addAll(data);
setUpText();
}
private void setUpText() {
//先清空原来的内容
removeAllViews();
//添加子view进来
for (String s : mList) {
TextView textView = new TextView(getContext());
textView.setText(s);
//设置相关属性
addView(textView);
}
}
在主活动中进行添加
public class TrueActiivty extends AppCompatActivity {
private List<String> data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_true_actiivty);
FlowLayout flowLayout = findViewById(R.id.flow_layout);
data = new ArrayList<>();
data.add("键盘");
data.add("显示器");
data.add("鼠标");
data.add("iPad");
data.add("air pot");
data.add("女装");
data.add("男鞋");
data.add("男装");
flowLayout.settextList(data);
}
}
六、自定义view绘制几何图形
1.直接在java文件中进行操作
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TestView(this));
}
private class TestView extends View{
public TestView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置一个背景图片
canvas.drawColor(Color.YELLOW);
//设置画笔
Paint paint = new Paint();
paint.setStrokeWidth(30);
paint.setStyle(Paint.Style.STROKE);
//去掉锯齿
paint.setAntiAlias(true);
//绘制一个矩形
canvas.drawRect(100,100,500,500,paint);
//再绘制一个矩形
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(600,100,1000,500,paint);
//更改颜色
paint.setColor(Color.BLUE);
//画一个圆
canvas.drawCircle(550,700,300,paint);
//再画一个圆
paint.setColor(Color.WHITE);
canvas.drawCircle(510,720,100,paint);
//画一个三角形
paint.setColor(Color.RED);
//定义一个路径
Path path = new Path();
path.moveTo(200,1000);
path.lineTo(900,1000);
path.lineTo(5500,1100);
path.close();
canvas.drawPath(path,paint);
//绘制文字
paint.setTextSize(100);
paint.setColor(Color.BLUE);
canvas.drawText("hello",300,1200,paint);
}
}
}
定义一个内部类,继承自view,覆写onDraw()方法,利用画笔来绘制图形
2.运行效果:
七、MVC小实例及自定义view小实例
1.布局文件下添加控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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=".MainActivity2"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="输入位置:"/>
<EditText
android:id="@+id/editText1"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:hint="x坐标值:"/>
<EditText
android:id="@+id/editText2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:hint="y坐标值:"/>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
</LinearLayout>
<com.example.customviewactivity.TestView
android:id="@+id/testView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.customviewactivity.TestView>
</LinearLayout>
2.设置一个类继承自view
package com.example.customviewactivity;
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;
public class TestView extends View {
int x,y;
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
protected void setXY(int _x,int _y){
x = _x;
y = _y;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.CYAN);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
canvas.drawCircle(x,y,40,paint);
paint.setColor(Color.WHITE);
canvas.drawCircle(x-8,y-8,8,paint);
}
}
重写onDraw()方法,进行绘制
3.主活动java文件下编辑代码
package com.example.customviewactivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
EditText edit_x, edit_y;
Button btn;
TestView testView;
int x1 = 150, y1 = 50;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
edit_x = findViewById(R.id.editText1);
edit_y = findViewById(R.id.editText2);
btn = findViewById(R.id.button1);
testView = findViewById(R.id.testView);
testView.setXY(x1, y1);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
x1 = Integer.parseInt(edit_x.getText().toString());
y1 = Integer.parseInt(edit_y.getText().toString());
testView.setXY(x1,y1);
//每次给到一个新的xy值,必须进行刷新
testView.invalidate();
}
}
4.运行效果
实现效果:小球跟随输入的坐标位置移动
5.MVC思想:
- 主活动的xml文件就是 VIEW 层
- 主活动java文件 就是 Contral 层
- TestView 就是 Model