Android与屏幕有关的知识点(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gaolh89/article/details/81161584

在Android与屏幕有关的知识点(一)中,我们对屏幕有关的一些常用概念、基本用法、单位之间转化、截屏等知识点进行了学习.本文将进一步对屏幕有关知识进行学习.

在学习本文前,建议先阅读 Android与屏幕有关的知识点(一)


说明:
(1)本文中涉及测试机的地方,若无特别说明:测试机A指的是分辨率:10802160手机、测试机B指的是分辨率:7201280.

(2)由于笔者所在公司要求不同分辨率手机上,TextView的字体大小、控件宽高相同,并非不同的分辨率手机采用不同的dp、sp.所以在代码设置、自定义属性默认值过程中,都是采用固定数值.如果大家项目中要求适配,请注意修改相应部分.

一.xml与代码中数据的关系

我们先在xml中写一个原生的TextView.设置字体大小为40sp.代码如下:

<?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"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
    >

    <!--直接xml设置-->
    <TextView
        android:id="@+id/tv_xml"
        android:layout_width="300dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:background="@color/colorPrimary"
        android:text="测试内容"
        android:textSize="40sp"
        />
</LinearLayout>

然后在代码中获取字体大小,代码如下:

@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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
    >

    <!--直接xml设置-->
    <TextView
        android:id="@+id/tv_xml"
        android:layout_width="350dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:background="@color/colorPrimary"
        android:text="测试内容"
        android:textSize="40sp"
        />

    <!--通过代码 set的形式-->
    <TextView
        android:id="@+id/tv_code"
        android:layout_width="350dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:background="@color/colorAccent"
        android:text="测试内容"
        />

    <!--通过自定义控件属性的形式-->
    <com.mfc.countdowntimerutils.TestTextView
        android:id="@+id/tv_custom"
        android:layout_width="350dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:background="@color/colorPrimary"
        android:text="测试内容"
        app:testTextMarginTop="20dp"
        app:testTextSize="40sp"
        />

</LinearLayout>

为了测试的准确性,我们只改变textView字体的大小、textview距离layout_marginTop.
其中,com.mfc.countdowntimerutils.TestTextView 属于继承系统TextView的控件.自定义控件不属于本文谈论范围,对自定义控件不熟或者感兴趣的同学,可以自行谷歌.

com.mfc.countdowntimerutils.TestTextView的属性代码如下(valuse/arrts下):

  <!--自定义字体大小 margintop的TextView-->
    <declare-styleable name="TestTextView">
        <attr name="testTextSize" format="dimension"/>
        <attr name="testTextMarginTop" format="dimension"/>
    </declare-styleable>

自定义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.
     *
     * <p>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.
     *
     * <p>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;
//数据是px
lp.topMargin = marginTop;
lp.bottomMargin = 0;
setLayoutParams(lp);

同样的, typedArray.getDimensionPixelOffset获取到的值 的单位是px.而setMargin 需要的值的单位也是px.所以直接使用即可.不需要(也不能转换)

这是手机A断点测试的数据
这里写图片描述

这是手机B断点测试的数据
这里写图片描述

尤其是7201280的手机B数据就一目了然( 7201280分辨率手机: 1dp=2px)

功能运行后,

在手机A上的效果如下

这里写图片描述

在手机B上的效果如下
这里写图片描述

可以发现,三种结果无论是那一张,字体大小, 控件与上边的间距是一样的.从而验证了上述方法的可行性.

猜你喜欢

转载自blog.csdn.net/gaolh89/article/details/81161584