美文网首页
Cool Android Apis 整理(三)

Cool Android Apis 整理(三)

作者: 橡樹先生 | 来源:发表于2017-11-01 14:35 被阅读13次

    Foreword

    本文主要整理 Cool Android Apis
    这是这个系列的第三篇,第一篇 Cool Android Apis 整理(一),第二篇 Cool Android Apis 整理(二)
    非常抱歉,由于工作的原因,第三篇隔了很久才发 (写博客果然不是一件容易的事)。

    整理来源

    Content

    这三个布局标签,都是起到布局优化的作用。

    < include /> 重用布局文件
    这个大家应该使用还是比较多的。
    这里还有几个小tip:
    1.< include />标签可以只使用layout属性,这个也是必须使用的。
    2.< include />标签若指定了ID属性,而你的layout也定义了ID,则你的layout的ID会被覆盖,同样的在include标签中所有的android:layout_* 属性都是可以覆盖使用的。

    <include android:layout_width="fill_parent" layout="@layout/image_holder" />
    <include android:layout_width="256dip" layout="@layout/image_holder" />
    

    < merge /> 减少视图层级

    < merge/>标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级。<merge/>多用于替换FrameLayout或者当一个布局包含另一个时,< merge/>标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了。这时我们就可以使用< merge/>标签优化。

    单独的这么说可能比较抽象,大家可以看下这篇blog Android Layout Tricks #3: Optimize by merging 加深理解。

    < ViewStub /> 需要的时候加载

    A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.

    < ViewStub />标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局比如进度条、显示占位信息等可以使用< ViewStub />标签,以减少内存使用量,加快渲染速度。< ViewStub />是一个不可见的,大小为0的View。

    For instance:

    <ViewStub 
        android:id="@+id/stub"
        android:inflatedId="@+id/subTree"
        android:layout="@layout/mySubTree"
        android:layout_width="120dip"
        android:layout_height="40dip" />
    

    当调用 setVisibility(int) or inflate() 时,布局就会加载。

    更多信息:Android Layout Tricks #3: Optimize with stubs

    我们先来看下继承关系:


    api05.jpg

    可以看到ViewAnimator 继承自FrameLayout ,那么也就是会说ViewAnimator 具有FrameLayout的特性,可以将多个View组件叠在一起,然后还能够处理views切换时的动画效果。
    但是这里不对ViewAnimator做更多的讲解,我们来看他的子类来了解更多,来理解这个类以及子类到底能够起到一个什么样的作用,而且我感觉在后面的Android的版本更新中这几个类就可能会过时。

    通过add(View view)向内部添加多个组件,再使用动画控制多个组件之间的切换效果,而且能够自动执行切换。
    与ViewPager有相似之处,ViewPager长于使用手势滑动进行页面切换。ViewFlipper长于实现子界面的自动切换。

    向ViewFlipper 加入View:
    静态导入:在layout布局中直接导入(不推荐,不灵活):

    <ViewFlipper
        ...>
        < ImageView
              ... />
        <ImageView
              ... />
        <ImageView
              ... />
    </ViewFlipper>
    

    动态导入: addView()方法。

    这里给大家列出几个常用方法和使用demo。

    方法 说明
    setInAnimation 设置View进入屏幕时使用的动画
    setOutAnimation 设置View退出屏幕时使用的动画
    showNext 调用函数来显示ViewFlipper里的下一张View
    showPrevious 调用函数来显示ViewFlipper里的上一张View
    setFilpInterval 设置View之间切换的时间间隔
    startFlipping 开始View自动播放
    stopFlipping 停止View播放
        ...
        flipper = (ViewFlipper) findViewById(R.id.flipper);
        //动态导入的方式为ViewFlipper加入View
        for (int i = 0 ; i< res.length; i++){
            flipper.addView(getImageView(res[i]));
        }
        flipper.setInAnimation(this, R.anim.push_right_in);
        flipper.setOutAnimation(this, R.anim.push_right_out);
        flipper.setFlipInterval(3000);
        flipper.setBackgroundColor(Color.BLACK);
        flipper.startFlipping();
        
        ...
        private ImageView getImageView(int id) {
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(id);
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            return imageView;
        }
    

    A ViewSwitcher can only have two child views, of which only one is shown at a time.

    当我在官方文档看到这个的时候基本感觉无爱了,因为这个限制,使用场景就很有限了,但是还是不能排除有合适的应用场景。
    用法上呢, 其实和 View Flipper 有点相似,但是有个了一个 setFactory 的,当然本质还是addView() 但是得多了对 LayoutParams 的默认处理。
    在实际中可能 ImageSwitcher , TextSwitcher 使用要比 ViewSwitcher 更多一些。
    上一个TextSwitcher的demo。
    xml:

     <TextSwitcher
            android:id="@+id/profileSwitcher"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:background="@color/border_gray"
            android:inAnimation="@anim/push_up_in"
            android:minHeight="30dp"
            android:outAnimation="@anim/push_up_out"></TextSwitcher>
    

    java:

        ...
        textSwitcher.setFactory(this);
        textSwitcher = (TextSwitcher) findViewById(R.id.profileSwitcher);
        textSwitcher.setFactory(this);
        Looper looper = Looper.myLooper();
        bitHandler = new bitHandler(looper);
        new myThread().start();
        ...
        
         @Override
        public View makeView() {
            TextView textView = new TextView(this);
            textView.setSingleLine();
            textView.setTextSize(15);
            textView.setEllipsize(TextUtils.TruncateAt.END);
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
            );
            lp.gravity = Gravity.CENTER;
            textView.setLayoutParams(lp);
            return textView;
        }
    
        //---这么写并不好,只是个demo,大家凑合看吧---//
        class bitHandler extends Handler {
    
            public bitHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                textSwitcher.setText(strings[index]);
                index++;
                if (index == strings.length) {
                    index = 0;
                }
            }
        }
    
        private class myThread extends Thread {
            @Override
            public void run() {
                super.run();
                while (index < strings.length) {
                    try {
                        synchronized (this) {
                            bitHandler.sendEmptyMessage(0);
                            this.sleep(4000);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
            
    

    效果图:


    api06.gif

    view属性部分

    • android:weightSum (LinearLayout)

    如果想使用 layout weights,但是却不想填充整个 LinearLayout 的话,就可以用 weightSum 来定义总的 weight 大小。

    • android:descendantFocusability(ViewGroup)

    在使用ListView时,当item中有Button、CheckBox、RadioButton、TextView等组件时,我们通常会遇到listview的每一项无法响应点击的问题,因为消息还没传回每一项的viewgroup就被其子view消费了。
    我们可以通过为该ViewGroup设置“android:descendantFocusability”属性来强制获取焦点,以便能够消费android系统传递过来的消息。“android:descendantFocusability”的详细解释如下图所示:

    api07.jpg

    属性的值有三种:

    方法 说明
    beforeDescendants viewgroup会优先其子类控件而获取到焦点
    afterDescendants viewgroup只有当其子类控件不需要获取焦点时才获取焦点
    blocksDescendants viewgroup会覆盖子类控件而直接获得焦点
    • android:duplicateParentState(ViewGroup)

    如果设置此属性,将直接从父容器中获取绘图状态(光标,按下,选中等)。比方说是你点一下LinearLayout时Button有被点击的效果,但是button不是相应点击事件,但是状态却随着LinearLayout变化了。这个简直是神技。。。

    • android:fillViewport (ScrollView)

    解决在 ScrollView 中当内容不足的时候填不满屏幕的问题。
    想了解更多,点击 ScrollView’s handy trick

    • android:scaleType (ImageView)

    定义在 ImageView 中怎么缩放/剪裁图片,ImageView.ScaleType共八种:

    方法 说明
    ImageView.ScaleType.CENTER 图片位于视图中间,但不执行缩放
    ImageView.ScaleType.CENTER_CROP 按统一比例缩放图片(保持图片的尺寸比例)便于图片的两维(宽度和高度)等于或者大于相应的视图的维度
    ImageView.ScaleType.CENTER_INSIDE 按统一比例缩放图片(保持图片的尺寸比例)便于图片的两维(宽度和高度)等于或者小于相应的视图的维度
    ImageView.ScaleType.FIT_CENTER 缩放图片使用center
    ImageView.ScaleType.FIT_END 缩放图片使用END
    ImageView.ScaleType.FIT_START 缩放图片使用START
    ImageView.ScaleType.FIT_XY 缩放图片使用XY
    ImageView.ScaleType.MATRIX 当绘制时使用图片矩阵缩放
    • android:imeOptions(EditText)

    EditText中使用对键盘的action键(一般是右下角)定制,并且通过监听键盘执行相应的操作,但是imeOptions并 不仅仅局限于此,还有很对对键盘控制的属性,比方在横屏的时候,控制键盘不全屏显示等。感兴趣的可以仔细的阅读下官方文档。
    部分属性:

    方法 说明
    android:imeOptions="actionNone" 输入框右侧不带任何提示
    android:imeOptions="actionGo" 右下角按键内容为'开始'
    android:imeOptions="actionSearch" 右下角按键为搜索
    android:imeOptions="actionSend" 右下角按键内容为'发送'
    android:imeOptions="actionPrevious" 右下角按键内容为'上一步'
    android:imeOptions="actionNext" 右下角按键内容为'下一步'
    android:imeOptions="actionDone" 右下角按键内容为'完成'

    For instance:

    etLayoutSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
                @Override
                public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                    if (actionId == EditorInfo.IME_ACTION_SEARCH ) {
                       ...
                    }
                    ...
                }
            });
    

    OnTrimMemory()方法并不仅仅是Activity实现的方法,系统提供包括Application, Fragement, Service, ContentProvider 都可以实现这个回调。是的,回调。
    OnTrimMemory是Android在4.0之后加入的一个回调,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法.OnTrimMemory的主要作用就是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉。

        @Override
        public void onTrimMemory(int level) {
            super.onTrimMemory(level);
        }
    

    我们可以看到在实现回调的时候,传入了一个参数,这个参数是内存使用情况的等级。总共有七种情况,分别是
    TRIM_MEMORY_UI_HIDDEN
    TRIM_MEMORY_RUNNING_MODERATETRIM_MEMORY_RUNNING_LOWTRIM_MEMORY_RUNNING_CRITICAL
    TRIM_MEMORY_BACKGROUNDTRIM_MEMORY_MODERATETRIM_MEMORY_COMPLETE
    从命名上大家应该能看出一些端倪,具体的在文档中有比较详细的说明和介绍。这里就不多说了,希望大家能够去看下。

    那么我们需要在什么时候OnTrimMemory回调释放哪些资源呢?根据内存使用的等级情况,一般我们会在内存情况很糟糕TRIM_MEMORY_COMPLETE,或者应用所有UI界面被隐藏了TRIM_MEMORY_UI_HIDDEN、我们需要对应的来释放一些缓存资源,以及我们动态添加的使用较少的View,这样来保证应用不会被杀掉。
    虽然,系统在内存不足的时候杀进程的顺序是按照LRU Cache中从低到高来的,但是它同时也会考虑杀掉那些占用内存较高的应用来让系统更快地获得更多的内存。如果我们应用占用内存较小,就可以增加不被杀掉的几率,从而快速地恢复。

    另外,在api 14之前使用的 OnLowMemory,级别应该和TRIM_MEMORY_COMPLETE是差不多的。从这点也可以看出Google对Android应用内存使用情况越来越多的重视(虽然有些自家应用很Low……)。
    所以,一个好的应用不仅仅是好看,流畅和快速的体验更加重要。

    Summary

    这样,这个系列基本算是结束了,后面如果自己工作中有发现更多好的API,我还是会不定时的过来更新。另外,如果大家在阅读过程中又发现问题或者有任何的意见或建议,可以在评论区留言。

    声明

    1. 由于互联网数据的分享性,如果我发表的文章,来源于您的原创文章,且我没有注明,请微博私信或者邮件macouen@gmail.com说明。
    2. 欢迎转载,但请注明文章原始出处。

    作者:Oak_Zmm
    出处:http://oakzmm.com/

    相关文章

      网友评论

          本文标题:Cool Android Apis 整理(三)

          本文链接:https://www.haomeiwen.com/subject/myfgpxtx.html