安卓开发需要注意/知道的小问题

  1. android:descendantFocusability属性,其中翻译成中文意思是子孙焦点,也就是当前 viewgroup 和其子控件谁获取当前焦点的意思。常用在 listview 和 recyclerView 中,当 listView 每个条目需要监听点击事件,同时每个条目里的子控件例如:button、checkbox 等也需要点击焦点的时候。

此属性对应的值有三个,分别是

beforeDescendants:viewgroup会优先其子类控件而获取到焦点

afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点 

blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

官方文档对此说明是:

android:descendantFocusabilityDefines the relationship between the ViewGroup and its descendants when looking for a View to take focus.Must be one of the following constant values.


对应的值.jpg
  1. Android 里 canvas.draw() 方法需要注意的:
    当画矩形 canvas.drawRect() 时如果是空心的矩形,即设置了 paint.setStyle(Paint.Style.STROKE) 和 paint.setStrokeWidth(20) 属性,则要注意矩形起始点和右下角坐标位置应该分别减去线条宽度的一半,即
canvas.drawRect(strokeWidth/2,strokeWidth/2,width-strokeWidth/2,height-strokeWidth/2,paint);  //左上右下,画笔

举个例子 参考来源

安卓开发需要注意/知道的小问题_第1张图片
矩形画布.png

如果在上图位置画一个 50*100dp 的矩形(见下图),其中线条宽度是20dp,起始左上角坐标和结束右下角坐标不是(0,0)和 (100,50),此时应该注意线条宽度也占据了位置,起始位置应该加上线条宽度的一半,终点位置应该减去线条宽度的一半,也就是取线条的中间。这里就是

canvas.drawRect(10,10,90, 40, paint);  //这里暂不考虑 dp 转 px 的分辨率问题,都当成 px 处理。
安卓开发需要注意/知道的小问题_第2张图片
需要实现的矩形

延伸

①.如果画矩形不设置 stroke 属性,直接设置 paint.setStyle(Style.FILL),则直接从红色矩形左上角坐标点开始即可;

②.如果画圆,也设置了描边,如下图,那么画圆的时候半径就是布局的一半减去线条宽度的一半。


安卓开发需要注意/知道的小问题_第3张图片
圆.png

③.如果画线,即 canvas.drawLine(),如下图所示,如果设置 paint.setStrokeWidth(20),
则表示画线的线条粗细为 20px ,那么画的时候坐标点就是图中红点,即左边界线条的中间点,即(0,10)这个点;


安卓开发需要注意/知道的小问题_第4张图片
线条
  1. 全面屏手机显示 view 的时候,部分手机底部有留白或者黑边,也就是没有铺满屏幕显示。
    其实只要设置项目的 targetSdkVersion > 24,即大于 7.0,则默认支持全面屏,也就不会有黑边显示。如果项目是比较老的版本,并且 targetSdkVersion < 24 且不好随便更改,也可以在 manifest 文件里配置以下内容:

这样也可以做到全屏展示。为什么这里 value 是 2.4,其实这是计算出来的一个预估值,因为传统屏幕:ratio_float = 16/9 = 1.778 ;像小米的 MIX2 是 18 / 9 =2 的,mix3 是 19.5 / 9 = 2.16,越来越长可以写稍微大点留一些余地,所以直接写成 2.4 更保险一些。以后开发估计都会支持全面屏,并且 Google 支持把 targetSdkVersion 写成最新版本,所以基本上以后的项目不会涉及到全面屏显示不全的问题,因为 targetSdkVersion 大于 24 已经默认支持了全面屏显示。

  1. 安卓中三个获取定义的 dimens 值的方法:
  getDimension、getDimensionPixelOffset 和 getDimensionPixelSize

在开发中,在代码里动态设置布局的间距、尺寸等数值时,最好使用 /res/values/dimens.xml 里的自定义尺寸,这样可以做到不同分辨率的屏幕适配,因为可以定义不同的 dimens 文件对应不同的分辨率。现在就看下上边三种方法对应获取的值的区别。

首先说明三个方法返回的都是对应的像素值,也就是不管定义的是 dp 还是 sp 或者 px,返回的都是 px 单位的值,可以通过以下的例子来看:


    
    17dp
    17sp
    17px


        //获取屏幕信息
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        float density = displayMetrics.density;
        int densityDpi = displayMetrics.densityDpi;
        System.out.println("屏幕宽:"+ width + "屏幕高:"+height);
        System.out.println("屏幕密度:"+ density + "屏幕密度 DPI:" + densityDpi);


        float a1 = getResources().getDimension(R.dimen.px_value);
        float a2 = getResources().getDimension(R.dimen.dp_value);
        float a3 = getResources().getDimension(R.dimen.sp_value);
        System.out.println("getDimension 中 px值是:" + a1);
        System.out.println("getDimension 中 dp值是:" + a2);
        System.out.println("getDimension 中 sp值是:" + a3);

        int b1 = getResources().getDimensionPixelSize(R.dimen.px_value);
        int b2 = getResources().getDimensionPixelSize(R.dimen.dp_value);
        int b3 = getResources().getDimensionPixelSize(R.dimen.sp_value);
        System.out.println("getDimensionPixelSize 中 px值是:" + b1);
        System.out.println("getDimensionPixelSize 中 dp值是:" + b2);
        System.out.println("getDimensionPixelSize 中 sp值是:" + b3);

        int c1 = getResources().getDimensionPixelOffset(R.dimen.px_value);
        int c2 = getResources().getDimensionPixelOffset(R.dimen.dp_value);
        int c3 = getResources().getDimensionPixelOffset(R.dimen.sp_value);
        System.out.println("getDimensionPixelOffset 中 px值是:" + c1);
        System.out.println("getDimensionPixelOffset 中 dp值是:" + c2);
        System.out.println("getDimensionPixelOffset 中 sp值是:" + c3);

打印数据是:

2019-07-28 17:43:27.469 23912-23912/com.ispring.gameplane I/System.out: 屏幕宽:1080 屏幕高:2030
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: 屏幕密度:2.75屏幕密度 DPI:440
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimension 中 px值是:17.0
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimension 中 dp值是:46.75
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimension 中 sp值是:46.75
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelSize 中 px值是:17
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelSize 中 dp值是:47
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelSize 中 sp值是:47
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelOffset 中 px值是:17
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelOffset 中 dp值是:46
2019-07-28 17:43:27.470 23912-23912/com.ispring.gameplane I/System.out: getDimensionPixelOffset 中 sp值是:46

通过结果可以看出,获取某个 dimen 的值,如果单位是 dp 或者 sp ,需要乘以 density (屏幕密度),如果是 px ,则不需要。

不同点:
getDimension:返回的是 float 类型的值。
getDimensionPixelSize: 返回的是 int 类型的值,并且由浮点型转成整型时是四舍五入的方式。
getDimensionPixelOffset : 返回的是 int 类型的值,并且由浮点型转成整型时,原则是忽略小数点部分。

  1. textView 在代码中设置粗体可以这样设置:
TextView tv = (TextView)findViewById(R.id.TextView01);  
TextPaint tp = tv.getPaint();
tp.setFakeBoldText(true); 

取消加粗效果的话设置
TextPaint tp = tv.getPaint();
tp.setFakeBoldText(false);

  1. android:clipToPadding属性的用法

在使用 ListView 或者 RecycleView、ScrollView 等滑动控件时候有一个强大但隐秘的属性,在android 的布局 XML 文件中,android:clipToPadding=“boolean”,该属性值可设为 true 或者false。表示控件的绘制区域是否在 padding 里面,true 的情况下如果你设置了 padding 那么绘制的区域就往里缩,false 则表示滑动时忽略 padding 的值。系统默认是 true。

属性的作用就是当你对 ListView 等滑动控件设置了 padding 属性后,四周会有缩进的效果,正常会与屏幕四边有空白间距,然后滑动的时候此间距一直显示。如果设置 android:clipToPadding=“false” ,则在滑动的时候间距自动随着滑动布局上滑以致不会显示空白间距,此时也就有了比较好的用户体验。

效果就是下边两张图所示:第一张为默认情况,即 android:clipToPadding =“true”,第二张为 false 时的结果。


安卓开发需要注意/知道的小问题_第5张图片
true结果.png
安卓开发需要注意/知道的小问题_第6张图片
false结果.png
  1. ColorStateList 可以设置文字颜色在不同状态下切换的功能,不需要手动根据不同状态设置不同的颜色值,使用方法如下:

首先,定义一个 selector 的选择器



    
    

在代码中使用方法如下:

ColorStateList csl=(ColorStateList)getResources().getColorStateList(R.color.button_text);  
Button btn =  new Button(mContext);
btn.setTextColor(csl);

也可以直接在布局文件中设置 android:textColor = "@color/button_text" 来实现。

此外,还有个 StateListDrawable 与 ColorStateList 类似,只不过它是设置 drawable 类型的资源文件,并且设置的资源文件是放在 res/drawable/ 文件夹下,静态使用方法如下:



     
     
     
     

也可以动态代码构造一个 StateListDrawable ,例如:

//初始化一个空对象
StateListDrawable stalistDrawable = new StateListDrawable();
//获取对应的属性值 Android框架自带的属性 attr
int pressed = android.R.attr.state_pressed;
int window_focused = android.R.attr.state_window_focused;
int focused = android.R.attr.state_focused;
int selected = android.R.attr.state_selected;
stalistDrawable.addState(newint []{pressed , window_focused}, getResources().getDrawable(R.drawable.pic1));
//负值表示对应的属性是 false, 下边的属性即按下状态但没有获取焦点的时候
stalistDrawable.addState(newint []{pressed , -focused}, getResources().getDrawable(R.drawable.pic2);
stalistDrawable.addState(newint []{selected }, getResources().getDrawable(R.drawable.pic3);
stalistDrawable.addState(newint []{focused }, getResources().getDrawable(R.drawable.pic4);
//没有任何状态时显示的图片,我们给它设置我空集合
stalistDrawable.addState(newint []{}, getResources().getDrawable(R.drawable.pic5);

动态使用方法:

   StateListDrawable states = new StateListDrawable();
   states.addState(new int[] {-android.R.attr.state_pressed}, INTERSTITIAL_CLOSE_BUTTON_NORMAL.createDrawable( getContext()));
    states.addState(new int[] {android.R.attr.state_pressed}, INTERSTITIAL_CLOSE_BUTTON_PRESSED.createDrawable(
                getContext()));
    mImageView.setImageDrawable(states);
  1. 异常的处理和抛出:throw与throws以及捕捉异常try、catch、finally
    1、throws出现在方法函数头;而throw出现在函数体。
    2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
    3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
    4、throws说明你有那个可能,倾向。throw的话,那就是你把那个倾向变成真实的了。
    5、举例:throws E1,E2,E3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常E1,E2,E3可能是该函数体产生的。
    throw则是明确了这个地方要抛出这个异常。
    举个例子:
void function_throws(int a,int b) throws Exception1,Exception3{
           try{
                 ......
           }catch(Exception1 e){
              throw e;
           }catch(Exception2 e){
              System.out.println("出错了!");
           }
           if(a!=b)
              throw new  Exception3("自定义异常");
}

代码块中可能会产生3个异常,(Exception1,Exception2,Exception3)。
如果产生 Exception1 异常,则捕获之后再抛出,由该方法的调用者去处理。
如果产生 Exception2 异常,则该方法自己处理了(即System.out.println("出错了!");)。所以该方法就不会再向外抛出 Exception2 异常了,即 throws Exception1,Exception3 里面不用写 Exception2 异常。

抛出异常后的语句都不会执行,try catch finally 里 finally 不管怎样都会执行。

  1. 申请权限时,在 Fragment 中申请权限,不要使用这种方式
ActivityCompat.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);

直接使用 Fragment 的 requestPermissions 方法,即

requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);

否则会回调到 Activity 的 onRequestPermissionsResult ,使用第二种方法会直接在 Fragment 里回调。

如果在 Fragment 中嵌套 Fragment,在子 Fragment 中使用 requestPermissions 方法,onRequestPermissionsResult 不会回调回来,建议在子 Fragment 中使用 getParentFragment().requestPermissions 方法,
这个方法会回调到父 Fragment 中的 onRequestPermissionsResult(),加入以下代码可以把回调透传到子 Fragment。

 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        List fragments = getChildFragmentManager().getFragments();
        if (fragments != null) {
            for (Fragment fragment : fragments) {
                if (fragment != null) {
                    fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
                }
            }
        }
    }

你可能感兴趣的:(安卓开发需要注意/知道的小问题)