Android自定义控件需要一些使用到自定义的属性,我们需要在app\src\main\res\values下新建一个xml文件名字为attrs也就是属性文件,在文件里面我们要使用declare-styleable来声明一个我们自己的格式的一些属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomLayoutHyy"> <attr name="hyy" format="string"></attr> <attr name="ratio" format="float"></attr> </declare-styleable> </resources>
其中属性名根据自定义的含义来命名,也可以根据自己的喜好来命名,总言而之就是根据心情来命名,但是declare-styleable name=”CustomLayoutHyy”里面的name值最好跟我们自定义控件名相同,format就是数据的格式,它有string 、float、boolean、color、dimension、enum、flag、fraction、integer、reference这10种格式,本次只用到float这种格式。该值用来设置图片等比例缩放值,<attr name="hyy" format="string"></attr>只是用来说明属性名是随意起的名字,代码中没有用到。
public class CustomLayoutHyy extends FrameLayout { private float ratio; public CustomLayout(@NonNull Context context) { super(context); } public CustomLayoutHyy(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); //拿到自定义的属性值 其中R.styleable.CustomLayout_ratio就是自定义属性radio对应的id, 同理 //R.styleable.CustomLayout_hyy 就是自定义属性hyy对应的id TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CustomLayoutHyy); ratio = ta.getFloat(R.styleable.CustomLayout_ratio, -1); ta.recycle(); } public CustomLayoutHyy(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public CustomLayoutHyy(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec);// 获取宽度值 int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 获取宽度模式 int height;// 获取高度值 int heightMode = MeasureSpec.getMode(heightMeasureSpec);// 获取高度模式 if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY && ratio > 0) { int img_width = width - getPaddingLeft() - getPaddingRight(); int img_height = (int) (img_width / ratio + 0.5f); height = img_height + getPaddingTop() + getPaddingBottom(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); } // 按照最新的高度测量控件 super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
<?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:hyy="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.administrator.viewtest.CustomLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#7f000000" android:padding="40dp" hyy:ratio="1.7"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/ic_launcher" /> </com.example.administrator.viewtest.CustomLayout> </LinearLayout>
要实现自定义控件,必须重写onMeasure方法,该方法用来计算控件的宽高,本例中想要使控件的宽度或高度根据所给的比例设置,就需要在此方法中重新进行测量计算。
onMeasure(int widthMeasureSpec, int heightMeasureSpec)可以看到,该方法有两个参数,widthMeasureSpec其实就是width的宽度值与宽度的模式,heightMeasureSpec就是height的高度值与高度的模式,重写该方法时需要对计算控件的实际大小,然后调用setMeasuredDimension(int, int)设置控件实际大小,在Android中,有三种模式,分别是:MeasureSpec.AT_MOST、MeasureSpec.EXACTLY、MeasureSpec.UNSPECIFIED。我们经常使用的wrap_conten对应的模式为MeasureSpec.AT_MOST,该模式意味着控件大小一般随着控件的子控件或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可,使用的match_parent对应的模式为MeasureSpec.EXACTLY,该模式说明控件的大小为精确值,我们已知的控件大小的时候可以设置此模式。当控件的大小不确定,控件的大小是可以随便变化的时候对应的模式为MeasureSpec.UNSPECIFIED,我们通常使用的父控件为Adapter的就是使用的就是这种模式。