Android与屏幕有关的知识点(二)
在Android与屏幕有关的知识点(一)中,我们对屏幕有关的一些常用概念、基本用法、单位之间转化、截屏等知识点进行了学习.本文将进一步对屏幕有关知识进行学习.
在学习本文前,建议先阅读 Android与屏幕有关的知识点(一)
说明:
(1)本文中涉及测试机的地方,若无特别说明:测试机A指的是分辨率:10802160手机、测试机B指的是分辨率:7201280.
(2)由于笔者所在公司要求不同分辨率手机上,TextView的字体大小、控件宽高相同,并非不同的分辨率手机采用不同的dp、sp.所以在代码设置、自定义属性默认值过程中,都是采用固定数值.如果大家项目中要求适配,请注意修改相应部分.
一.xml与代码中数据的关系
我们先在xml中写一个原生的TextView.设置字体大小为40sp.代码如下:
<?xml version="1.0" encoding="utf-8"?>
然后在代码中获取字体大小,代码如下:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dptext); mTvXml = (TextView) findViewById(R.id.tv_xml); float textSize = mTvXml.getTextSize(); }
进行断点测试,在测试机A上,mTvXmlTextSize的值为110.0 ;测试机B上,mTvXmlTextSize的值为80.0.
另外,需要补充说明的事:
(1)xml中使用40sp,修改手机系统设置字体,在测试机上的结果不再是110.0、80.0.而是随着系统设置字体的改变而改变.
(2)如果将xml中的40sp换成40dp,在测试机上的结果同样为110.0、80.0.(并且修改手机系统设置 字体大小,都是110.0、80.0)
二. 关于xml、代码中、自定义属性中使用屏幕相关知识
我们在刚才的基础上,再添加2个textView控件. id为tv_code的TextView(第23行)采用原生TextView.我们在代码中设置字体大小、边距.id为tv_custom的TextView(第33行)是继承原生TextView的自定义控件.
xml代码如下:
<?xml version="1.0" encoding="utf-8"?>
为了测试的准确性,我们只改变textView字体的大小、textview距离layout_marginTop.
其中,com.mfc.countdowntimerutils.TestTextView 属于继承系统TextView的控件.自定义控件不属于本文谈论范围,对自定义控件不熟或者感兴趣的同学,可以自行谷歌.
com.mfc.countdowntimerutils.TestTextView的属性代码如下(valuse/arrts下):
自定义TestTextView的代码如下:
@SuppressLint("AppCompatCustomView")public class TestTextView extends TextView implements ViewTreeObserver.OnGlobalLayoutListener { private Context mContext; int marginTop; public TestTextView(Context context) { super(context); } public TestTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.mContext = context; init(context, attrs); } public TestTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init(Context context, AttributeSet attrs) { mContext = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable .TestTextView); if (typedArray != null) { //①如果xml中使用了TestTextView_testTextSize属性,typedArray.getDimensionPixelOffset //会根据xml中使用的单位进行内部转化(比如xml中都是使用40sp,不同分辨率手机得到的initTextSize值不同). //②如果xml中没有使用TestTextView_testTextSize属性,typedArray.getDimensionPixelOffset会直接 //使用默认的值(它不知道单位的,默认值也没有单位) int initTextSize = typedArray.getDimensionPixelOffset(R.styleable .TestTextView_testTextSize, DisplayUtils.sp2px(mContext, 40)); setTextSize(TypedValue.COMPLEX_UNIT_PX,initTextSize); //TestTextView_testTextMarginTop的道理同上述的TestTextView_testTextSize marginTop = typedArray.getDimensionPixelOffset(R.styleable .TestTextView_testTextMarginTop, DisplayUtils.dip2px(mContext,20)); typedArray.recycle(); post(() -> { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); Log.e("测试元素", "init: " + this); lp.leftMargin = 0; lp.rightMargin = 0; //数据是px lp.topMargin = marginTop; lp.bottomMargin = 0; setLayoutParams(lp); }); } } @Override public void onGlobalLayout() { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); Log.e("测试元素", "init: " + this); lp.leftMargin = 0; lp.rightMargin = 0; //数据是px lp.topMargin = marginTop; lp.bottomMargin = 0; setLayoutParams(lp); }// @Override// protected void onFinishInflate() {// super.onFinishInflate();// getViewTreeObserver().addOnGlobalLayoutListener(this);// }}
上述代码中,关于view的绘制使用了 重写onFinishInflate()方法,或者post()方法.如果直接使用的话,就会报空指针.这部分不属于本文谈论的范畴,不再赘述.
另外需要说明2点:
1.查看setTextSize() 方法方法的源码我们可知,该方法有接收一个参数的,即setTextSize(float size)
相关部分源码如下:
/** * Set the default text size to the given value, interpreted as "scaled * pixel" units. This size is adjusted based on the current density and * user font size preference. * * Note: if this TextView has the auto-size feature enabled than this function is no-op. * * @param size The scaled pixel size. * * @attr ref android.R.styleable#TextView_textSize */ @android.view.RemotableViewMethod public void setTextSize(float size) { setTextSize(TypedValue.COMPLEX_UNIT_SP, size); }
从第14行源码可知,如果只传了一个参数,系统会自动加TypedValue.COMPLEX_UNIT_SP 变成2个参数的.
第一个参数是干嘛用的呢,我们继续点这个方法继续看源码.源码如下:
/** * Set the default text size to a given unit and value. See {@link * TypedValue} for the possible dimension units. * * Note: if this TextView has the auto-size feature enabled than this function is no-op. * * @param unit The desired dimension unit. * @param size The desired size in the given units. * * @attr ref android.R.styleable#TextView_textSize */ public void setTextSize(int unit, float size) { if (!isAutoSizeEnabled()) { setTextSizeInternal(unit, size, true /* shouldRequestLayout */); } }
从第7行对unit参数的描述可知:unit是所需的尺寸单位. 往上看TextView对TypedValue.COMPLEX_UNIT_SP常量的描述,这个常量指的就是sp.也就是说.我们setTextSize(14) 就是14sp.很容易误以为是px.
同样的,我们可以设置第一个参数为dp或sp.当然,后面的值也需要对应更换.还是以实现40sp效果为例.如果使用px,代码如下:
mTvCode.setTextSize(TypedValue.COMPLEX_UNIT_PX,DisplayUtils.sp2px(this,40));
2.关于set的padding或者margin方法,或者计算view的宽高等,单位 则是 px.如果你在xml中使用了TestTextView_testTextMarginTop属性,系统在调用typedArray.getDimensionPixelOffset(R.styleable.TestTextView_testTextMarginTop,value)值时,就根据屏幕的分辨率、xml中自定义属性值,自动转化/
3.在使用最原始的textView时,我们在xml中,分别设置
android:textSize="40sp"android:layout_marginTop="20dp"
为了实现同样的效果,我们使用setTextSize和setMargin的方式也是如何实现的呢:
(1)先看原生的textView但使用代码设置的 方式:
public class TestActivity extends AppCompatActivity { private TextView mTvXml, mTvCode, mTvCutom; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); mTvXml = (TextView) findViewById(R.id.tv_xml); mTvCode = (TextView) findViewById(R.id.tv_code); mTvCutom = (TextView) findViewById(R.id.tv_custom); //这里的40等同于40sp mTvCode.setTextSize(40); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mTvCode.getLayoutParams(); lp.leftMargin = 0; lp.rightMargin = 0; //数据是px int temptopMargin = DisplayUtils.dip2px(this, 20); lp.topMargin = temptopMargin; lp.bottomMargin = 0; mTvCode.setLayoutParams(lp); }}
刚刚已经说过了,setTextSize() 中的 如果是一个参数,数值的单位就是sp.所以直接写 setTextSize(40)即可.
如果想使用2个参数的,写成:
mTvCode.setTextSize(TypedValue.COMPLEX_UNIT_PX,DisplayUtils.sp2px(this,40));
而setMargin的方式,因为赋值的单位是px,而我们一开始的 20dp.所以需要 dp转px后再赋值,即:
int temptopMargin = DisplayUtils.dip2px(this, 20); lp.topMargin = temptopMargin;
这是手机A断点测试的数据.(分辨率: 1080*2160)
这是手机B断点测试的数据.(分辨率: 720*1280)
(2)使用自定义控件的 方式(上述TestTextView.class 代码片段):
①设置字体大小
int initTextSize = typedArray.getDimensionPixelOffset(R.styleable.TestTextView_testTextSize, DisplayUtils.sp2px(mContext, 40)); setTextSize(TypedValue.COMPLEX_UNIT_PX,initTextSize);
②设置间距
marginTop = typedArray.getDimensionPixelOffset(R.styleable.TestTextView_testTextMarginTop, DisplayUtils.dip2px(mContext,20));
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();Log.e("测试元素", "init: " + this);lp.leftMargin = 0;lp.rightMargin = 0;//数据是pxlp.topMargin = marginTop;lp.bottomMargin = 0;setLayoutParams(lp);
同样的, typedArray.getDimensionPixelOffset获取到的值 的单位是px.而setMargin 需要的值的单位也是px.所以直接使用即可.不需要(也不能转换)
这是手机A断点测试的数据
这是手机B断点测试的数据
尤其是7201280的手机B数据就一目了然( 7201280分辨率手机: 1dp=2px)
功能运行后,
在手机A上的效果如下
在手机B上的效果如下
可以发现,三种结果无论是那一张,字体大小, 控件与上边的间距是一样的.从而验证了上述方法的可行性.
更多相关文章
- android代码示例讲解--专题视频课程
- 使用Android Studio手把手教你将应用打包+代码混淆
- Flutter与Android原生代码交互
- Android 单元测试实战(1)—— 调研与选型
- [置顶] android自动化测试之Benckmark
- 安卓系统的开发与测试
- Android开发之核心特性SearchView的开发使用(源代码分享)
- Android studio怎么使用代码检查工具Analyze?