自定义控件——视图的构建过程——视图的测量方法
对于wrap_content形式的宽高,App需要测量它们的实际长度,需要测量的实体主要有3种:
(1)文本尺寸测量
文本尺寸分为文本的宽度和高度,需根据文本大小分别计算。
(2)图形尺寸测量
如果图形是Bitmap格式,就调用getWidth和getHeight方法;如果图形是Drawable格式,就调用getIntrinsicWidth和getIntrinsicHeight方法。
(3)布局尺寸测量
调用measure方法按照测量规格进行测量操
===========================================================================================
文本尺寸的测量
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp" > <RelativeLayout android:layout_width="match_parent" android:layout_height="40dp" > <TextView android:id="@+id/tv_size" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:gravity="center" android:text="字体大小:" android:textColor="@color/black" android:textSize="17sp" /> <Spinner android:id="@+id/sp_size" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@+id/tv_size" android:gravity="left|center" android:spinnerMode="dialog" /> </RelativeLayout> <TextView android:id="@+id/tv_desc" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="left" android:textColor="@color/black" android:textSize="17sp" /> <TextView android:id="@+id/tv_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:gravity="center" android:text="每逢佳节倍思亲" android:textColor="@color/black" /> </LinearLayout>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="40dp" android:singleLine="true" android:gravity="center" android:textSize="17sp" android:textColor="#0000ff" />
代码:
package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.TypedValue; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; import com.example.myapplication.util.MeasureUtil; public class MainActivity extends AppCompatActivity { private TextView tv_desc, tv_text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_desc = findViewById(R.id.tv_desc); tv_text = findViewById(R.id.tv_text); initSizeSpinner(); // 初始化文字大小的下拉框 } // 初始化文字大小的下拉框 private void initSizeSpinner() { ArrayAdapter<String> sizeAdapter = new ArrayAdapter<String>(this, R.layout.item_select, descArray); Spinner sp_size = findViewById(R.id.sp_size); sp_size.setPrompt("请选择文字大小"); sp_size.setAdapter(sizeAdapter); sp_size.setOnItemSelectedListener(new SizeSelectedListener()); sp_size.setSelection(0); } private String[] descArray = {"12sp", "15sp", "17sp", "20sp", "22sp", "25sp", "27sp", "30sp"}; private int[] sizeArray = {12, 15, 17, 20, 22, 25, 27, 30}; class SizeSelectedListener implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { String text = tv_text.getText().toString(); int textSize = sizeArray[arg2]; tv_text.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize); // 计算获取指定文本的宽度(其实就是长度) int width = (int) MeasureUtil.getTextWidth(text, textSize); // 计算获取指定文本的高度 int height = (int) MeasureUtil.getTextHeight(text, textSize); String desc = String.format("下面文字的宽度是%d,高度是%d", width, height); tv_desc.setText(desc); } public void onNothingSelected(AdapterView<?> arg0) {} } }
MeasureUtil
package com.example.myapplication.util; import android.app.Activity; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.text.TextUtils; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; public class MeasureUtil { // 获取指定文本的宽度(其实就是长度) public static float getTextWidth(String text, float textSize) { if (TextUtils.isEmpty(text)) { return 0; } Paint paint = new Paint(); // 创建一个画笔对象 paint.setTextSize(textSize); // 设置画笔的文本大小 return paint.measureText(text); // 利用画笔丈量指定文本的宽度 } // 获取指定文本的高度 public static float getTextHeight(String text, float textSize) { Paint paint = new Paint(); // 创建一个画笔对象 paint.setTextSize(textSize); // 设置画笔的文本大小 FontMetrics fm = paint.getFontMetrics(); // 获取画笔默认字体的度量衡 return fm.descent - fm.ascent; // 返回文本自身的高度 //return fm.bottom - fm.top + fm.leading; // 返回文本所在行的行高 } // 根据资源编号获得线性布局的实际高度(页面来源) public static float getRealHeight(Activity act, int resid) { LinearLayout llayout = act.findViewById(resid); return getRealHeight(llayout); } // 根据资源编号获得线性布局的实际高度(视图来源) public static float getRealHeight(View parent, int resid) { LinearLayout llayout = parent.findViewById(resid); return getRealHeight(llayout); } // 计算指定线性布局的实际高度 public static float getRealHeight(View child) { LinearLayout llayout = (LinearLayout) child; // 获得线性布局的布局参数 LayoutParams params = llayout.getLayoutParams(); if (params == null) { params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } // 获得布局参数里面的宽度规格 int wdSpec = ViewGroup.getChildMeasureSpec(0, 0, params.width); int htSpec; if (params.height > 0) { // 高度大于0,说明这是明确的dp数值 // 按照精确数值的情况计算高度规格 htSpec = MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY); } else { // MATCH_PARENT=-1,WRAP_CONTENT=-2,所以二者都进入该分支 // 按照不确定的情况计算高度规则 htSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } llayout.measure(wdSpec, htSpec); // 重新丈量线性布局的宽高 // 获得并返回线性布局丈量之后的高度。调用getMeasuredWidth方法可获得宽度 return llayout.getMeasuredHeight(); } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <include layout="@layout/drag_drop_header" /> <TextView android:id="@+id/tv_desc" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" android:gravity="left" android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#eeeeee" android:gravity="center_horizontal" android:orientation="vertical" android:paddingBottom="15dp" android:paddingTop="15dp" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_header" /> <LinearLayout android:id="@+id/ll_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" > <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_arrow" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:gravity="center_horizontal" android:orientation="vertical" > <TextView android:id="@+id/tv_tips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="轻轻下拉,刷新精彩..." android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout> </LinearLayout> </LinearLayout>
代码:
package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.LinearLayout; import android.widget.TextView; import com.example.myapplication.util.MeasureUtil; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout ll_header = findViewById(R.id.ll_header); TextView tv_desc = findViewById(R.id.tv_desc); // 计算获取线性布局的实际高度 float height = MeasureUtil.getRealHeight(ll_header); String desc = String.format("上面下拉刷新头部的高度是%f", height); tv_desc.setText(desc); } }
MeasureUtil
package com.example.myapplication.util; import android.app.Activity; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.text.TextUtils; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; public class MeasureUtil { // 获取指定文本的宽度(其实就是长度) public static float getTextWidth(String text, float textSize) { if (TextUtils.isEmpty(text)) { return 0; } Paint paint = new Paint(); // 创建一个画笔对象 paint.setTextSize(textSize); // 设置画笔的文本大小 return paint.measureText(text); // 利用画笔丈量指定文本的宽度 } // 获取指定文本的高度 public static float getTextHeight(String text, float textSize) { Paint paint = new Paint(); // 创建一个画笔对象 paint.setTextSize(textSize); // 设置画笔的文本大小 FontMetrics fm = paint.getFontMetrics(); // 获取画笔默认字体的度量衡 return fm.descent - fm.ascent; // 返回文本自身的高度 //return fm.bottom - fm.top + fm.leading; // 返回文本所在行的行高 } // 根据资源编号获得线性布局的实际高度(页面来源) public static float getRealHeight(Activity act, int resid) { LinearLayout llayout = act.findViewById(resid); return getRealHeight(llayout); } // 根据资源编号获得线性布局的实际高度(视图来源) public static float getRealHeight(View parent, int resid) { LinearLayout llayout = parent.findViewById(resid); return getRealHeight(llayout); } // 计算指定线性布局的实际高度 public static float getRealHeight(View child) { LinearLayout llayout = (LinearLayout) child; // 获得线性布局的布局参数 LayoutParams params = llayout.getLayoutParams(); if (params == null) { params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } // 获得布局参数里面的宽度规格 int wdSpec = ViewGroup.getChildMeasureSpec(0, 0, params.width); int htSpec; if (params.height > 0) { // 高度大于0,说明这是明确的dp数值 // 按照精确数值的情况计算高度规格 htSpec = MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY); } else { // MATCH_PARENT=-1,WRAP_CONTENT=-2,所以二者都进入该分支 // 按照不确定的情况计算高度规则 htSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } llayout.measure(wdSpec, htSpec); // 重新丈量线性布局的宽高 // 获得并返回线性布局丈量之后的高度。调用getMeasuredWidth方法可获得宽度 return llayout.getMeasuredHeight(); } }