Android(安卓)button 性能探讨
button是Android 中常用的控件, 父类是 Textview, 在之前的文章中对 Textview的一些 基本信息都介绍过, 还包括Textview的 之 setText方法 带来的一些 性能问题。 这篇文章就讲到了 button的一些使用要注意的地方。
这些需要注意的 还包括了 Textview 已经一些 Textview的 子类 如 CheckBox,EditText 等等 一些控件。 问题的产生
button 我们一般都会给它 设置一个background,许多人会做成selector 或者 shape ,layer-list等等一些 资源。
有的做的漂亮的项目 会给 按钮 两张图片 ,一张 是选中 或者点击状态,另外一张 则是 正常状态。
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- checked --> <item android:drawable="@drawable/language_press" android:state_checked="true"/> <!-- default --> <item android:drawable="@drawable/language_no_press" android:state_checked="false"/></selector></span>
那么这样使用 就使得 xml加载的时候 这两张图片也加载了, 也就是一个按钮 就占用了 两张 图片的内存,这是不合理的。 那有人说 我都这么用 没一点事, 确实 随着现在 机器硬件的提升,以及Android本身系统的优化 等 大幅提高了效率, 使得这种 问题 变的不会很明显, 你一个xml 可能 使用带有图片的 selector 可能也就那么几个 不会很多。 那么我们讲的是为什么 会出现这个问题 ,怎么避免。 至于一些 临界点(使用多少会出现性能问题 造成卡顿)暂不关心。
那么 为什么会加载两张,因为我们在 xml中配置,Drawable.java的createFromXmlInner方法中对图片进行解析,最终调用Drawable的inflate方法),相当于一个按钮占用了两张相同大小图片所使用的内存。 我们来看看 源码
<span style="font-size:18px;"> public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { Drawable drawable; final String name = parser.getName(); if (name.equals("selector")) { drawable = new StateListDrawable(); } else if (name.equals("level-list")) { drawable = new LevelListDrawable(); /* Probably not doing this. } else if (name.equals("mipmap")) { drawable = new MipmapDrawable(); */ } else if (name.equals("layer-list")) { drawable = new LayerDrawable(); } else if (name.equals("transition")) { drawable = new TransitionDrawable(); } else if (name.equals("color")) { drawable = new ColorDrawable(); } else if (name.equals("shape")) { drawable = new GradientDrawable(); } else if (name.equals("scale")) { drawable = new ScaleDrawable(); } else if (name.equals("clip")) { drawable = new ClipDrawable(); } else if (name.equals("rotate")) { drawable = new RotateDrawable(); } else if (name.equals("animated-rotate")) { drawable = new AnimatedRotateDrawable(); } else if (name.equals("animation-list")) { drawable = new AnimationDrawable(); } else if (name.equals("inset")) { drawable = new InsetDrawable(); } else if (name.equals("bitmap")) { drawable = new BitmapDrawable(r); if (r != null) { ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); } } else if (name.equals("nine-patch")) { drawable = new NinePatchDrawable(); if (r != null) { ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); } } else { throw new XmlPullParserException(parser.getPositionDescription() + ": invalid drawable tag " + name); } drawable.inflate(r, parser, attrs); return drawable; }</span>
可以看到 源码中 做了 很多判断,根据xml中的配置 加载了 不同的资源(我的源码可能是老版本的,新版本的 我看了 做了优化 匹配都变为了 switch/case模式)。可以看到 使用图片的话 会用BitmapDrawable , 使用BitmapDrawable 这个类里面的inflate的方法。 会将图片加载到内存中。并绘制出来
如果在内存吃紧的情况下 做一些 小优化还是非常有必要的。
比如 在 图片是 纯颜色的 时候 建议selector 里面使用 颜色 而不使用 图片。具体原因 还是源码
下面是BitmapDrawable的inflate方法
<span style="font-size:18px;"> @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { super.inflate(r, parser, attrs); TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.BitmapDrawable); final int id = a.getResourceId(com.android.internal.R.styleable.BitmapDrawable_src, 0); if (id == 0) { throw new XmlPullParserException(parser.getPositionDescription() + ": <bitmap> requires a valid src attribute"); } final Bitmap bitmap = BitmapFactory.decodeResource(r, id); if (bitmap == null) { throw new XmlPullParserException(parser.getPositionDescription() + ": <bitmap> requires a valid src attribute"); } mBitmapState.mBitmap = bitmap; setBitmap(bitmap); setTargetDensity(r.getDisplayMetrics()); final Paint paint = mBitmapState.mPaint; paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias, paint.isAntiAlias())); paint.setFilterBitmap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_filter, paint.isFilterBitmap())); paint.setDither(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_dither, paint.isDither())); setGravity(a.getInt(com.android.internal.R.styleable.BitmapDrawable_gravity, Gravity.FILL)); int tileMode = a.getInt(com.android.internal.R.styleable.BitmapDrawable_tileMode, -1); if (tileMode != -1) { switch (tileMode) { case 0: setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); break; case 1: setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); break; case 2: setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); break; } } a.recycle(); }</span>
下面是ColorDrawable的inflate的源码
<span style="font-size:18px;"> @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { super.inflate(r, parser, attrs); TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ColorDrawable); int color = mState.mBaseColor; color = a.getColor(com.android.internal.R.styleable.ColorDrawable_color, color); mState.mBaseColor = mState.mUseColor = color; a.recycle(); }</span>
具体一对比,不说细节,光说代码量就能说明一点问题了吧。 如果非要使用 图片,就用代码 进行动态设置。
给要使用的view加 onTouch事件,在 down,和move的时候为 点击状态, up为正常状态。觉得每一个设置太麻烦?
那就封装一个方法
<span style="font-size:18px;"> public void changeImage(View view, final int normalResId, final int pressResId){ view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: v.setBackgroundResource(pressResId); break; case MotionEvent.ACTION_MOVE: v.setBackgroundResource(pressResId); break; case MotionEvent.ACTION_UP: v.setBackgroundResource(normalResId); break; } // 为了不影响其他事件 return false; } }); }</span>
这样不仅 节省了内存, 而且 能减小apk包的大小(不需要每个都写 selector.xml了)
有些 边框,线条,圆角等等 能自己写 stroke,layer-list,shape 就尽量使用。 对内存和性能 比图片更好一点
更多相关文章
- 安卓(Android)实现选择时间功能
- Android(安卓)使用Glide加载图片
- Android中对MIME类型的理解
- Android高手进阶教程(十一)之----Android(安卓)通用获取Ip的方法
- Android(安卓)上层应用读写设备节点
- Android(安卓)Studio导入SlidingMenu类库的方法(其他类库应该也适
- 总结系列-硬件加速
- Android(安卓)Studio的基本控件 图片框与进度条
- Android中Window的管理深入讲解