安卓自定义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>

android 侧量控件宽度 android自定义view测量模式_canvas

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.运行效果:

android 侧量控件宽度 android自定义view测量模式_java_02

七、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.运行效果

android 侧量控件宽度 android自定义view测量模式_安卓_03

实现效果:小球跟随输入的坐标位置移动

5.MVC思想:
  • 主活动的xml文件就是 VIEW 层
  • 主活动java文件 就是 Contral 层
  • TestView 就是 Model