Android ImageView分析并展开
对于它的使用。除了注意ScaleType的理解和设置外,还须要注意其它一些问题,比方设置一张大的背景图片内存占用和释放等。
还有它的拓展性方面,像圆角图片、圆形图片、图片边框等等。因此,假设想熟练使用这个控件,就须要对事实上现的机制有一个基本的了解。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZ2ppbnl1NTAx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
- public ImageView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initImageView();
- TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.ImageView, defStyle, 0);
- Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
- if (d != null) {
- setImageDrawable(d);
- }
- mBaselineAlignBottom = a.getBoolean(
- com.android.internal.R.styleable.ImageView_baselineAlignBottom, false);
- mBaseline = a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_baseline, -1);
- setAdjustViewBounds(
- a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds,
- false));
- setMaxWidth(a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
- setMaxHeight(a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
- int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
- if (index >= 0) {
- setScaleType(sScaleTypeArray[index]);
- }
- int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
- if (tint != 0) {
- setColorFilter(tint);
- }
- int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
- if (alpha != 255) {
- setAlpha(alpha);
- }
- mCropToPadding = a.getBoolean(
- com.android.internal.R.styleable.ImageView_cropToPadding, false);
- a.recycle();
- //need inflate syntax/reader for matrix
- }
- private void initImageView() {
- mMatrix = new Matrix();
- mScaleType = ScaleType.FIT_CENTER;
- mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
- }
- /**
- * Options for scaling the bounds of an image to the bounds of this view.
- */
- public enum ScaleType {
- /**
- * Scale using the image matrix when drawing. The image matrix can be set using
- * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax:
- * <code>android:scaleType="matrix"</code>.
- */
- MATRIX (0),
- /**
- * Scale the image using {@link Matrix.ScaleToFit#FILL}.
- * From XML, use this syntax: <code>android:scaleType="fitXY"</code>.
- */
- FIT_XY (1),
- /**
- * Scale the image using {@link Matrix.ScaleToFit#START}.
- * From XML, use this syntax: <code>android:scaleType="fitStart"</code>.
- */
- FIT_START (2),
- /**
- * Scale the image using {@link Matrix.ScaleToFit#CENTER}.
- * From XML, use this syntax:
- * <code>android:scaleType="fitCenter"</code>.
- */
- FIT_CENTER (3),
- /**
- * Scale the image using {@link Matrix.ScaleToFit#END}.
- * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>.
- */
- FIT_END (4),
- /**
- * Center the image in the view, but perform no scaling.
- * From XML, use this syntax: <code>android:scaleType="center"</code>.
- */
- CENTER (5),
- /**
- * Scale the image uniformly (maintain the image's aspect ratio) so
- * that both dimensions (width and height) of the image will be equal
- * to or larger than the corresponding dimension of the view
- * (minus padding). The image is then centered in the view.
- * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>.
- */
- CENTER_CROP (6),
- /**
- * Scale the image uniformly (maintain the image's aspect ratio) so
- * that both dimensions (width and height) of the image will be equal
- * to or less than the corresponding dimension of the view
- * (minus padding). The image is then centered in the view.
- * From XML, use this syntax: <code>android:scaleType="centerInside"</code>.
- */
- CENTER_INSIDE (7);
- ScaleType(int ni) {
- nativeInt = ni;
- }
- final int nativeInt;
- }
接着就是onMeasure()方法了,它用于设置ImageView的大小。我们在xml文件里设置ImageView的时候,假设指定了固定的宽高,那么onMeasur()方法中測量的大小就是固定的宽高大小;假设是包裹内容,那么就须要进一步的计算。
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- resolveUri();//获取图片Drawable
- int w;
- int h;
- // Desired aspect ratio of the view's contents (not including padding)
- float desiredAspect = 0.0f;
- // We are allowed to change the view's width
- boolean resizeWidth = false;
- // We are allowed to change the view's height
- boolean resizeHeight = false;
- final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
- if (mDrawable == null) {
- // If no drawable, its intrinsic size is 0.
- mDrawableWidth = -1;
- mDrawableHeight = -1;
- w = h = 0;
- } else {
- w = mDrawableWidth;在updateDrawable(Drawable d)方法赋值的。
- h = mDrawableHeight;
- if (w <= 0) w = 1;
- if (h <= 0) h = 1;
- // We are supposed to adjust view bounds to match the aspect
- // ratio of our drawable. See if that is possible.
- if (mAdjustViewBounds) {
- resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
- resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
- desiredAspect = (float) w / (float) h;
- }
- }
- int pleft = mPaddingLeft;
- int pright = mPaddingRight;
- int ptop = mPaddingTop;
- int pbottom = mPaddingBottom;
- int widthSize;
- int heightSize;
- if (resizeWidth || resizeHeight) {
- /* If we get here, it means we want to resize to match the
- drawables aspect ratio, and we have the freedom to change at
- least one dimension.
- */
- // Get the max possible width given our constraints
- widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);
- // Get the max possible height given our constraints
- heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
- if (desiredAspect != 0.0f) {
- // See what our actual aspect ratio is
- float actualAspect = (float)(widthSize - pleft - pright) /
- (heightSize - ptop - pbottom);
- if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
- boolean done = false;
- // Try adjusting width to be proportional to height
- if (resizeWidth) {
- int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
- pleft + pright;
- // Allow the width to outgrow its original estimate if height is fixed.
- if (!resizeHeight && !mAdjustViewBoundsCompat) {
- widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
- }
- if (newWidth <= widthSize) {
- widthSize = newWidth;
- done = true;
- }
- }
- // Try adjusting height to be proportional to width
- if (!done && resizeHeight) {
- int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
- ptop + pbottom;
- // Allow the height to outgrow its original estimate if width is fixed.
- if (!resizeWidth && !mAdjustViewBoundsCompat) {
- heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
- heightMeasureSpec);
- }
- if (newHeight <= heightSize) {
- heightSize = newHeight;
- }
- }
- }
- }
- } else {
- /* We are either don't want to preserve the drawables aspect ratio,
- or we are not allowed to change view dimensions. Just measure in
- the normal way.
- */
- w += pleft + pright;
- h += ptop + pbottom;
- w = Math.max(w, getSuggestedMinimumWidth());
- h = Math.max(h, getSuggestedMinimumHeight());
- widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
- heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);
- }
- setMeasuredDimension(widthSize, heightSize);
- }
在onMeasure方法中,首先调用了resolveUri()这种方法。目的就是为了确定Drawable。
假设设置了drawableResource。那么Drawable就是其值;假设没有。那么就从ContentResolver获取一个Drawable。
- private void resolveUri() {
- if (mDrawable != null) {
- return;
- }
- Resources rsrc = getResources();
- if (rsrc == null) {
- return;
- }
- Drawable d = null;
- if (mResource != 0) {
- try {
- d = rsrc.getDrawable(mResource);
- } catch (Exception e) {
- Log.w("ImageView", "Unable to find resource: " + mResource, e);
- // Don't try again.
- mUri = null;
- }
- } else if (mUri != null) {
- String scheme = mUri.getScheme();
- if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
- try {
- // Load drawable through Resources, to get the source density information
- ContentResolver.OpenResourceIdResult r =
- mContext.getContentResolver().getResourceId(mUri);
- d = r.r.getDrawable(r.id);
- } catch (Exception e) {
- Log.w("ImageView", "Unable to open content: " + mUri, e);
- }
- } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
- || ContentResolver.SCHEME_FILE.equals(scheme)) {
- InputStream stream = null;
- try {
- stream = mContext.getContentResolver().openInputStream(mUri);
- d = Drawable.createFromStream(stream, null);
- } catch (Exception e) {
- Log.w("ImageView", "Unable to open content: " + mUri, e);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Log.w("ImageView", "Unable to close content: " + mUri, e);
- }
- }
- }
- } else {
- d = Drawable.createFromPath(mUri.toString());
- }
- if (d == null) {
- System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
- // Don't try again.
- mUri = null;
- }
- } else {
- return;
- }
- updateDrawable(d);
- }
- private void updateDrawable(Drawable d) {
- if (mDrawable != null) {
- mDrawable.setCallback(null);
- unscheduleDrawable(mDrawable);
- }
- mDrawable = d;
- if (d != null) {
- d.setCallback(this);
- if (d.isStateful()) {
- d.setState(getDrawableState());
- }
- d.setLevel(mLevel);
- d.setLayoutDirection(getLayoutDirection());
- d.setVisible(getVisibility() == VISIBLE, true);
- mDrawableWidth = d.getIntrinsicWidth();
- mDrawableHeight = d.getIntrinsicHeight();
- applyColorMod();
- configureBounds();
- } else {
- mDrawableWidth = mDrawableHeight = -1;
- }
- }
那这种方法是做什么用的呢?设置View的最大高度,单独使用无效,须要与setAdjustViewBounds一起使用。假设想设置图片固定大小,又想保持图片宽高比,须要例如以下设置:
1) 设置setAdjustViewBounds为true;
2) 设置maxWidth、MaxHeight;
3) 设置设置layout_width和layout_height为wrap_content。
- if (mAdjustViewBounds) {
- resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
- resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
- desiredAspect = (float) w / (float) h;
- }
然后接下来的推断也是基于 resizeWidth和resizeHeight 的值,假设不为true的情况下,会运行例如以下代码:
- w += pleft + pright;
- h += ptop + pbottom;
- w = Math.max(w, getSuggestedMinimumWidth());
- h = Math.max(h, getSuggestedMinimumHeight());
- widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
- heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);
- }
- setMeasuredDimension(widthSize, heightSize);
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (mDrawable == null) {
- return; // couldn't resolve the URI
- }
- if (mDrawableWidth == 0 || mDrawableHeight == 0) {
- return; // nothing to draw (empty bounds)
- }
- if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
- mDrawable.draw(canvas);
- } else {
- int saveCount = canvas.getSaveCount();
- canvas.save();
- if (mCropToPadding) {
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
- scrollX + mRight - mLeft - mPaddingRight,
- scrollY + mBottom - mTop - mPaddingBottom);
- }
- canvas.translate(mPaddingLeft, mPaddingTop);
- if (mDrawMatrix != null) {
- canvas.concat(mDrawMatrix);
- }
- mDrawable.draw(canvas);
- canvas.restoreToCount(saveCount);
- }
- }
这个是在configureBounds()方法中设置的,
- private void configureBounds() {
- if (mDrawable == null || !mHaveFrame) {
- return;
- }
- int dwidth = mDrawableWidth;
- int dheight = mDrawableHeight;
- int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
- int vheight = getHeight() - mPaddingTop - mPaddingBottom;
- boolean fits = (dwidth < 0 || vwidth == dwidth) &&
- (dheight < 0 || vheight == dheight);
- if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
- /* If the drawable has no intrinsic size, or we're told to
- scaletofit, then we just fill our entire view.
- */
- mDrawable.setBounds(0, 0, vwidth, vheight);
- mDrawMatrix = null;
- } else {
- // We need to do the scaling ourself, so have the drawable
- // use its native size.
- mDrawable.setBounds(0, 0, dwidth, dheight);
- if (ScaleType.MATRIX == mScaleType) {
- // Use the specified matrix as-is.
- if (mMatrix.isIdentity()) {
- mDrawMatrix = null;
- } else {
- mDrawMatrix = mMatrix;
- }
- } else if (fits) {
- // The bitmap fits exactly, no transform needed.
- mDrawMatrix = null;
- } else if (ScaleType.CENTER == mScaleType) {
- // Center bitmap in view, no scaling.
- mDrawMatrix = mMatrix;
- mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),
- (int) ((vheight - dheight) * 0.5f + 0.5f));
- } else if (ScaleType.CENTER_CROP == mScaleType) {
- mDrawMatrix = mMatrix;
- float scale;
- float dx = 0, dy = 0;
- if (dwidth * vheight > vwidth * dheight) {
- scale = (float) vheight / (float) dheight;
- dx = (vwidth - dwidth * scale) * 0.5f;
- } else {
- scale = (float) vwidth / (float) dwidth;
- dy = (vheight - dheight * scale) * 0.5f;
- }
- mDrawMatrix.setScale(scale, scale);
- mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
- } else if (ScaleType.CENTER_INSIDE == mScaleType) {
- mDrawMatrix = mMatrix;
- float scale;
- float dx;
- float dy;
- if (dwidth <= vwidth && dheight <= vheight) {
- scale = 1.0f;
- } else {
- scale = Math.min((float) vwidth / (float) dwidth,
- (float) vheight / (float) dheight);
- }
- dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
- dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
- mDrawMatrix.setScale(scale, scale);
- mDrawMatrix.postTranslate(dx, dy);
- } else {
- // Generate the required transform.
- mTempSrc.set(0, 0, dwidth, dheight);
- mTempDst.set(0, 0, vwidth, vheight);
- mDrawMatrix = mMatrix;
- mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
- }
- }
- }
可是这样直接使用会有一个隐形的弊端,假设显示的图片过多或者单张显示的图片像素过大,就easy出现OOM问题。因此就应该依据需求对图片进行预处理,经常用法有下面几种:
关于图片压缩有非常多方法,这里仅仅是列举一个简单的样例,实际使用价值不大。如有需求能够自行參考其它资料。
- InputStream is = this.getResources().openRawResource(R.drawable.xx);
- BitmapFactory.Options options=new BitmapFactory.Options();
- options.inJustDecodeBounds = false;
- options.inSampleSize = 10; //width。hight设为原来的十分一
- Bitmap btp =BitmapFactory.decodeStream(is,null,options);
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap。再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完毕decode。无需再使用java层的createBitmap,从而节省了java层的空间。
假设在读取时加上图片的Config參数,能够跟有效降低载入的内存。从而跟有效阻止抛out of Memory异常。
另外,须要特别注意:decodeStream是直接读取图片资料的字节码了, 不会依据机器的各种分辨率来自己主动适应。使用了decodeStream之后。须要在hdpi和mdpi,ldpi中配置对应的图片资源,否则在不同分辨率机器上都是相同大小(像素点数量)。显示出来的大小就不正确了。
- public static Bitmap readBitMap(Context context, int resId){
- BitmapFactory.Options opt = new BitmapFactory.Options();
- opt.inPreferredConfig = Bitmap.Config.RGB_565;
- opt.inPurgeable = true;
- opt.inInputShareable = true;
- InputStream is = context.getResources().openRawResource(resId);
- return BitmapFactory.decodeStream(is,null,opt);
- }
- if(!bmp.isRecycle() ){
- bmp.recycle() //回收图片所占的内存
- system.gc() //提醒系统及时回收
- }
- private final static float TARGET_HEAP_UTILIZATION = 0.75f;
在程序onCreate时就能够调用
- VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
Android为每一个程序分配的内存能够通过Runtime类的 totalMemory() 、freeMemory() 两个方法获取VM的一些内存信息。
- private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
- VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小heap内存为6MB大小。
最后在按下的时候启动一个属性动画,将圆环放大显示,关于具体的分析能够看android-circlebutton介绍 这篇文章。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
Android ImageView分析并展开的更多相关文章
- Android多线程分析之五:使用AsyncTask异步下载图像
Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<An ...
- Android多线程分析之三:Handler,Looper的实现
Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...
- Android多线程分析之一:使用Thread异步下载图像
Android多线程分析之一:使用Thread异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处 打算整理一下对 Android F ...
- Android群英传笔记——第五章:Android Scroll分析
Android群英传笔记--第五章:Android Scroll分析 滑动事件算是Android比较常用的效果了,而且滑动事件他本身也是有许多的知识点,今天,我们就一起来耍耍Scroll吧 一.滑动效 ...
- Android Launcher分析和修改11——自定义分页指示器(paged_view_indicator)
Android4.0的Launcher自带了一个简单的分页指示器,就是Hotseat上面那个线段,这个本质上是一个ImageView利用.9.png图片做,效果实在是不太美观,用测试人员的话,太丑了. ...
- Android Launcher分析和修改12——Widget列表信息收集
很久没写Launcher分析的文章,最近实在太忙.今天七夕本来是想陪女朋友逛街 ,碰巧打台风呆在家里,就继续写一篇文章.今天主要是讲一下Launcher里面的Widget列表,这方面信息比较多,今天重 ...
- [转] Android 性能分析案例
Android 系统的一个工程师(Romain Guy)针对Falcon Pro 应用,撰写了一个Android性能分析的文章.该文章介绍了如何分析一个应用哪里出现了性能瓶颈,导致该应用使用起来不流 ...
- Android多线程分析之中的一个:使用Thread异步下载图像
Android多线程分析之中的一个:使用Thread异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可.转载请注明出处 打算整理一下对 Android Fr ...
- [转]Android ImageView的scaleType属性与adjustViewBounds属性
Android ImageView的scaleType属性与adjustViewBounds属性 ImageView的scaleType的属性有好几种,分别是matrix(默认).center.c ...
随机推荐
- iOS得知1_初体验
UIView:父类的所有控件,所有的UIView它是一个容器.可容纳其他UIView UIController:用于控制UIView,责创建/销毁自己的UIView,显示/隐藏UIView.处理UIV ...
- dell服务器从硬盘导入阵列信息
前几天去南京客户那里更新新的业务系统,客户要求将服务器上的旧的硬盘拆下来,换上新的硬盘,重新做raid,客户自己要插入旧的硬盘读取旧数据,昨天做了几个实验,两台Dell R710服务器各4块硬盘,一台 ...
- java解析String类型t复杂xml,多级节点,最好的例子
需要用jar包 dom4j-1.6.1.jar 字符串xml如下: <root> <flw> <name>aa</name> <age>22 ...
- 2 WAN 和1 Evo/3g Routeros PCC 方法负载平衡
陕西中际现代包装科技:Routeros 2 WAN 和1 Evo/3g PCC 方法负载平衡 (Routeros多线负载平衡) 我们将要讨论2Wan和1个Evo/3G 的负载平衡.负载平衡就是在不同 ...
- Spring实战笔记2---Bean的装配
创建应用对象之间协作关系的行为通常成为装配,该篇的主要内容有两个,一个Spring装配Bean的几种方式以及Spring表达式,事实上这两者是分不开的,在Spring中,对象无需自己负责查找或者创建与 ...
- 逆向 Framework.jar
Ref:http://www.addictivetips.com/mobile/what-is-odex-and-deodex-in-android-complete-guide/ Ref:http: ...
- Java流读写
写: package com.wjy.write; import java.io.BufferedWriter; import java.io.FileOutputStream; import jav ...
- UVA 10404 Bachet's Game(dp + 博弈?)
Problem B: Bachet's Game Bachet's game is probably known to all but probably not by this name. Initi ...
- HTML5特性检測
HTML5特性检測: 1.检測全局对象:诸如window或navigator是否拥有特定的属性 2.创建元素:检測该元素的DOM对象是否拥有特定的属性 3.创建元素:检測该元素的DO ...
- XP下採用DirectShow採集摄像头
转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家提出意见,一起讨论! 须要演示样例源代码的请独自联系我. 前提: 摄像头能正常工作.摄像头有创建di ...