那些好玩的 android 小事

那些好玩的 android 小事

本文记录的是一些在开发时遇到的好玩的东西,一些容易出错的地方,一些迷惑的地方, 虽然记录的东西很简单,但是又特别的细节。

  1. View 的 setOnclickListener(...) 与 setClickable

    view.setClickable(false);
    view.setOnclickListener(...);

    则 该 view 仍然为可点击状态,因为
    setOnClickListener()里面会把 view 设置为 clickable , 可点击状态:

    // view 的 源码  
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
    

    会首先检查该 view 是否可点击,如果不可点击,则会把它设置为可点击状态。

  2. ImageView.setAlpha(int) 与 ImageView.setAlpha(float) 的区别

    这是一个文档里,很容易踩进去的坑。

    setAlpha(int) 是对 image 进行 alpha 进行变化, 范围是 0 ~ 255;

    setAlpha(float) 是对 view 进行 alpha 进行变化,范围是 0f ~ 1f;

    两个方法 做 透明度变化的对象不同,要千万注意,注意!!!, 如果混用,就会出现问题的。

    例如,如果是想利用 float 进行设置,刚开始设置了 setAlpha(0), 后面都是 setAlpha(float), 则会出现 这个 view 永远展示不出来的情况, 刚开始设置 setAlpha(0f) 则是正确的。

    注意:官方已经抛弃了 setAlpha(float) 这个方法,推荐使用 setImageAlpha(int) (API 16 以上才会生效) 这个方法.

  3. 在 自定义 view 中, paint 的 setColor() 与 setAlpha() 的关系

    paint.setColor(#12ffffff);
    如果设置的颜色里面包含了 透明度, 则 该画笔 的透明度 不一定就是 12;

    /**
    * ... ts alpha can be any value, regardless of the values of r,g,b
    */
    public void setColor(@ColorInt int color) {
        nSetColor(mNativePaint, color);
    }
    

    它的 alpha 可能会变化,因为受 setAlpha 的影响

     /**
     * Helper to setColor(), that only assigns the color's alpha value,
     * leaving its r,g,b values unchanged. Results are undefined if the alpha
     * value is outside of the range [0..255]
     *
     * @param a set the alpha component [0..255] of the paint's color.
     */
    
    public void setAlpha(int a) {
        nSetAlpha(mNativePaint, a);
    }
    
    

    setAlpha() 会覆盖 setColor 中的 透明度, 所以 当你做自定义 view 的变换时,如果同时设置了 setColor 和 setAlpha 则 透明度会有后者决定。

  1. 图片的时间戳问题

    在 媒体库里面 MediaStore.Images 里面, ImageColumns.DATE_TAKEN, 这项属性,描述的是该图片的时间戳。但是当你修改该图片后,该时间 DATE_TAKEN, 会怎么变化呢?假设原图片(A)的时间为 2018.01.02 13:00, 那么修改改图片后,一般会保留原图片,生成一个新的图片(B),那么这个新的图片的时间为多少呢?首先猜一猜,要么和原图一样,要么是现在修改的时间。

    但是!!!事实上,当我去打印这个时间的时候,竟然发现,修改后的 B 的时间戳,竟然小于 原图片 A 的时间!!!,也就是说,这个时间 会早于 2018.01.02 13:00 可能是 2018.01.02 12:59.

    这个不是特别会影响功能的地方,我也是偶然发现的,在这里记录一下,也说不定是错的,我只测试了我手上的几款机型,可能根据不同的机型会有所不同吧。

  1. padding 与 margin 对同一个控件的影响

    例如:父控件为LinearLayout, 子控件为button,下面两种设置的方式效果是一样的:

     1. 当父控件设置了`padding="8dp"`
         padding 是内边框, 使得该父控件里的子view的空间都会减少8dp
     2. 子控件设置了`layout_margin="8dp"`
         margin 是相对button而言的,使得自身距离父控件各个方向有8dp的距离;
    

    上面两种,实际button的点击区域,view的绘制区域都是相同的,但是padding后使得linearlayout里content会缩小8dp的距离,而margin则不会影响content的区域大小,从而造成一些特定情况下的问题。在margin下,button可以显示设置的阴影,而padding则没有足够的content去显示阴影。

    说的有点不清楚,可能需要特定的情况下,才可以发现区别,但是, padding 会导致里面 的 子 view button 的大小不能超过 padding 的限制, 是父 view 对 子view 的限制,是被动的; 而 margin 是一个主动的行为,是子 view 对 自身的一个限制;

    下面看一个

  2. 数据库的操作条数限制 : 1000 条

    Crash : SQLiteException: Expression tree is too large (maximum depth 1000)
    

    原因: 在 sqlite 语句中 的 筛选条件里面 包含了太多的内容项,超过了1000 个,就会 crash; 这个是直接写在代码里的,是 sqlite 直接抛出的异常.

    通常会出现在 删除记录时,例如, 筛选条件 whereCause.append(ID + "=" + list.get(i).id), 然后循环添加 要删除的对象,如果 list 内容太多,就会造成 whereCause 十分的庞大,当超过 1000 深度时,便会 发生 这个 crash .

    解决方案:

    1. 手动对 whereCause.append 进行限制,如果超过 1000 , 则执行 sqlite 删除语句,然后接着 另外一个 whereCause 去 添加条件, 再次去执行 删除语句。

    2. 如果条件可以替换为 IN 处理

      whereCause = "Table.column._ID IN (ids[1], ids[2], ids[3], ...);
          
      whereCause = "Table.column._ID = ids[1] OR Table.column._ID = ids[2] OR Table.column._ID = ids[3] ...";
          
      上面两者是等价的, 但是 第一种不会造成 上述 exception, in 只代表一条语句,但是 or 却有多个 or 出现
      
  3. ConstraintLayout 中的 Group 控件要慎用!

    现在更多的都会使用 ConstraintLayout 布局,因为它太强大了,减少了很多 view 的层级。

    在约束布局中,有时会想要同时控制两个以上的 view 的可见性,一般我们都是通过 Group 这个官方提供的控件去设置。

    代码示例如下:

    <android.support.constraint.Group
        android:id="@+id/preview_layout_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:constraint_referenced_ids="preview_image_view, preview_delete_view" />
    

    当我们设置 preview_layout_group.setVisibility(...), 会同时对两个 view(preview_image_view, preview_delete_view) 同时生效,比较方便。

    但是,当我们这样设置的时候,有可能会出现里面子 view 设置setVisibility() 时失效的现象!!!

    假设,我们设置 preview_layout_group.setVisibility(VISIBLE), 然后想对里面的 deleteView 设置为不可见,那么实际上是不会生效的。 deleteView imageView 都是可见的。

    • 这里跟我们之前设置 view 的可见性不同的是,以前一般都是设置了父 view 可见后,在针对个别 view 设置它的不可见。

    • 如果我们使用了 Group 时, Group 并不是父 view。它是一个单独的控件去控制里面所有关联 view 的可见性,而且,是一种优先级比较大的方式去设置了里面子 view 的可见性。

      所以当你设置了 Group 为可见时,里面的子 view 你设置了不可见,子 view 的设置是不会生效的!!!

    所以要慎重的使用 Group,它其实很实用,会很方便控制一些 view 的共同可见性,但是,当使用的时候,一定要确保里面所有关联的子 view 之间的可见性时完全一样的,要么都可见,要么都不可见!

  1. RecyclerViewscrollbar 和分割线 Item.Decoration 的遮挡问题
    有时候,会出现 RecyclerViewscrollbar 显示不完全,或者 scrollbar 被「分割线」分开的现象,这部分涉及到的内容比较多。详细在下面这篇文章中:
    RecyclerView 的 scrollbar 和 ItemDecoration 的绘制和遮挡问题

  2. TextView 里面的 moveCursorToVisibleOffset() 方法

    该方法在源码中注释很详细:当 TextView 中的 mTextSpannable 的实例且不只一个字符时,当文本滚动时「文本太多,可滑动」,编辑的光标会随着滚动而移动。源码如下:

     /**
     * Move the cursor, if needed, so that it is at an offset that is visible
     * to the user.  This will not move the cursor if it represents more than
     * one character (a selection range).  This will only work if the
     * TextView contains spannable text; otherwise it will do nothing.
     *
     * @return True if the cursor was actually moved, false otherwise.
     */
    public boolean moveCursorToVisibleOffset() {
        if (!(mText instanceof Spannable)) {
            return false;
        }
        int start = getSelectionStart();
        int end = getSelectionEnd();
        if (start != end) {
            return false;
        }
    
        // First: make sure the line is visible on screen:
    
        int line = mLayout.getLineForOffset(start);
    
        final int top = mLayout.getLineTop(line);
        final int bottom = mLayout.getLineTop(line + 1);
        final int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
        int vslack = (bottom - top) / 2;
        if (vslack > vspace / 4) {
            vslack = vspace / 4;
        }
        final int vs = mScrollY;
    
        if (top < (vs + vslack)) {
            line = mLayout.getLineForVertical(vs + vslack + (bottom - top));
        } else if (bottom > (vspace + vs - vslack)) {
            line = mLayout.getLineForVertical(vspace + vs - vslack - (bottom - top));
        }
    
        // Next: make sure the character is visible on screen:
    
        final int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
        final int hs = mScrollX;
        final int leftChar = mLayout.getOffsetForHorizontal(line, hs);
        final int rightChar = mLayout.getOffsetForHorizontal(line, hspace + hs);
    
        // line might contain bidirectional text
        final int lowChar = leftChar < rightChar ? leftChar : rightChar;
        final int highChar = leftChar > rightChar ? leftChar : rightChar;
    
        int newStart = start;
        if (newStart < lowChar) {
            newStart = lowChar;
        } else if (newStart > highChar) {
            newStart = highChar;
        }
    
        if (newStart != start) {
            Selection.setSelection(mSpannable, newStart);
            return true;
        }
    
        return false;
    }
    

    但是呢? 当我们不需要该效果,期望光标不随着文本的滚动而变化位置,那么重写该方法返回 false 即可。

    @Override
    public boolean moveCursorToVisibleOffset() {
        return false;
    }
    
  1. To be continued ...

上面是个人在开发过程中遇到的一些比较好玩的事情,有时候敲代码蛮枯燥,发现的一些小的惊奇的点,会让自己很开心,就像在沙滩上拾贝一样,偶尔发现几个特别奇特的贝壳, 会特别开心,这也算代码的魅力吧。 会持续更新, 发现一些好的点都会补充上去~

大家有什么感觉好的点,也可以指出来,如有哪里不对的地方, 水平有限,也请指出来,谢谢~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容