Android圆角ImageViev

         前段时间写了关于Android圆角ImageView的博客,感觉写的太差了,其实不用那么麻烦,直接重写一下onMeasure方法就行了。

       需求:两张图片平分屏幕,高度可以不固定,保证图片能完全铺满ImageView且不能变形。也就是这样的效果:

Android圆角ImageViev_第1张图片


如果不自定义ImageView很难做到,所以这里覆写ImageView来实现,下面是代码,里面有注释(为了怕中文乱码,我蛋疼的加了英文注释)。由于采用了LruCache技术图片的点击效果莫名其妙的被屏蔽掉了,求各位大牛解答。

package cn.smc.roundimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.support.v4.util.LruCache;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
/**
 * @author shang ming chao
 * 
 * */
public class RoundImageView extends ImageView {
	private Paint mPaint;
	private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
	private Bitmap mClipBitmap;
	private int mRoundBorderRadius;//corners radius
	private static final int DEFAULT_ROUND_BORDER_RADIUS = 50;//default corners radius
	private LruCache<String, Bitmap> mLruCache;
	private int realWidth;//drawn width
	private int realHeight;//drawn height
	private Drawable drawable;//source image drawable
	public OnTouchListener onTouchListener;
	public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		init();
	}

	public RoundImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		init();
	}

	public RoundImageView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		init();
	}
	public void init() {
		drawable = getDrawable();//source image drawable
		mRoundBorderRadius = DEFAULT_ROUND_BORDER_RADIUS;
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		int maxMemory = (int) Runtime.getRuntime().maxMemory();//32M
		int cacheMemory = maxMemory / 8;//4M
		mLruCache = new LruCache<String, Bitmap>(cacheMemory)
		{
			@Override
			protected int sizeOf(String key, Bitmap value)
			{
				return value.getRowBytes() * value.getHeight();
			}

		};
		onTouchListener = new View.OnTouchListener() {
			@Override
	        public boolean onTouch(View view, MotionEvent event) {
	            switch (event.getAction()) {
	            case MotionEvent.ACTION_UP:
	                changeLight((ImageView) view, 0);
	                //view.performClick();
	                break;
	            case MotionEvent.ACTION_DOWN:
	                changeLight((ImageView) view, -80);
	                break;
	            case MotionEvent.ACTION_MOVE:
	                // changeLight(view, 0);
	                break;
	            case MotionEvent.ACTION_CANCEL:
	                changeLight((ImageView) view, 0);
	                break;
	            default:
	                break;
	            }
	            return false;//must return false,then onclick() will be invoked
	        }

	    };
	    this.setFocusable(true);
	    this.setClickable(true);
	    this.setLongClickable(true);
	    this.setOnTouchListener(onTouchListener);
	}
	private void changeLight(ImageView imageview, int brightness) {
	    ColorMatrix matrix = new ColorMatrix();
	    matrix.set(new float[] { 1, 0, 0, 0, brightness, 0, 1, 0, 0,
	            brightness, 0, 0, 1, 0, brightness, 0, 0, 0, 1, 0 });
	    imageview.setColorFilter(new ColorMatrixColorFilter(matrix));
	    imageview.invalidate();

	}
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);//measure firstly
		realWidth = getMeasuredWidth();//get the real-width(screenWidth/2)
		float srcWidth = drawable.getIntrinsicWidth();//get the width of image
		float srcHeight = drawable.getIntrinsicHeight();//get the height of image
		int expectHeight = Math.round(realWidth*srcHeight/srcWidth);//calculate the ratio
		realHeight = expectHeight;
		int myHeightMeasureSpec = MeasureSpec.makeMeasureSpec(expectHeight,MeasureSpec.EXACTLY);
		setMeasuredDimension(widthMeasureSpec, myHeightMeasureSpec);//specify our height
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		//super.onDraw(canvas);
		Bitmap bmp = getBitmapFromLruCache("url");
		if (bmp == null) {
			if (drawable != null) {
				bmp = getClippedBitmap(drawable);//get round image
				canvas.drawBitmap(bmp, 0, 0, mPaint);
				addBitmapToLruCache("url",bmp);
			}
		} else {
			mPaint.setXfermode(null);
			canvas.drawBitmap(bmp, 0.0f, 0.0f, mPaint);
			return;
		}
	}

	//get rounded bitmap,the drawable has been clipped
	private Bitmap getClippedBitmap(Drawable drawable) {
		Bitmap bmp= Bitmap.createBitmap(realWidth,
						realHeight, Config.ARGB_8888);
				Canvas drawCanvas = new Canvas(bmp);
				drawable.setBounds(0, 0, realWidth,
						realHeight);
				drawable.draw(drawCanvas);//draw source image firstly
				if (mClipBitmap == null || mClipBitmap.isRecycled()) {
					mClipBitmap = getClipBitmap();
				}
				mPaint.reset();
				mPaint.setFilterBitmap(false);
				mPaint.setXfermode(mXfermode);
				drawCanvas.drawBitmap(mClipBitmap, 0, 0, mPaint);//then draw roundRect with dst_in
				mPaint.setXfermode(null);
				return bmp;
	}
	private Bitmap getClipBitmap() {
		Bitmap bitmap = Bitmap.createBitmap(realWidth,
				realHeight, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bitmap);
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(Color.BLACK);
		canvas.drawRoundRect(
				new RectF(0, 0, realWidth, realHeight),
				mRoundBorderRadius, mRoundBorderRadius, paint);
		return bitmap;
	}
	
	public void addBitmapToLruCache(String key, Bitmap bitmap) {    
        if (getBitmapFromLruCache(key) == null && bitmap != null) {    
            mLruCache.put(key, bitmap);    
        }    
    }
	
	public Bitmap getBitmapFromLruCache(String key) {    
        return mLruCache.get(key);    
    }
	
	public int getRoundBorderRadius() {
		return mRoundBorderRadius;
	}

	public void setRoundBorderRadius(int mRoundBorderRadius) {
		if (this.mRoundBorderRadius != mRoundBorderRadius) {
			this.mRoundBorderRadius = mRoundBorderRadius;
			invalidate();
		}
	}
}

源码下载

下面的是以前丑陋的写法,虽然丑陋但有些地方还是有参考价值的。


Android自带的ImageView给人感觉很难看,我们希望图片是个圆角的、可以点击、有点击效果的,最重要的是:图片显示时不能因为缩放而变形。

        ImageView有两个重要属性:一个是src(ImageView的前景图片),一个是Background(ImageView的背景)。在布局时,我们一般会指定ImageView的宽高,但这个宽和高不一定是图片的宽和高,所以为了让图片在这个ImageView上显示,Android Framework提供了8种图片缩放方式。最常用的就是center,centerCrop和centerInside。
        但这个ImageView宽高比不一定是图片的宽高比。所以如果我们的图片不是透明背景的话,就会出现下面这样的情况,没有办法在保证不变形的情况下显示漂亮的图片:

原图 center centerCrop centerInside
Android圆角ImageViev_第2张图片 Android圆角ImageViev_第3张图片 Android圆角ImageViev_第4张图片 Android圆角ImageViev_第5张图片
蓝色部分是ImageView的背景。但我们想要达到这样的效果:

Android圆角ImageViev_第6张图片


怎么办呢?只能通过真实的显示宽度来计算要显示的高度,也就是不能把高度定死。(比如屏幕中两张图片平分屏幕,权值一样,那宽度多少像素我们不知道,所以高度多少我们也没办法指定),所以要自定义(覆写)ImageView:

①在自定义ImageView的onDraw方法里自己画出一个圆角图片:

<span style="color:#c0c0c0;">//get rounded bitmap,the drawable has been clipped
	private Bitmap getClippedBitmap(Drawable drawable) {
		Bitmap bmp= Bitmap.createBitmap(realWidth,
						realHeight, Config.ARGB_8888);
				Canvas drawCanvas = new Canvas(bmp);
				drawable.setBounds(0, 0, realWidth,
						realHeight);
				drawable.draw(drawCanvas);//draw source image firstly
				if (mClipBitmap == null || mClipBitmap.isRecycled()) {
					mClipBitmap = getClipBitmap();
				}
				mPaint.reset();
				mPaint.setFilterBitmap(false);
				mPaint.setXfermode(mXfermode);
				drawCanvas.drawBitmap(mClipBitmap, 0, 0, mPaint);//then draw roundRect with dst_in
				mPaint.setXfermode(null);
				return bmp;
	}
	private Bitmap getClipBitmap() {
		Bitmap bitmap = Bitmap.createBitmap(realWidth,
				realHeight, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bitmap);
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(Color.BLACK);
		canvas.drawRoundRect(
				new RectF(0, 0, realWidth, realHeight),
				mRoundBorderRadius, mRoundBorderRadius, paint);
		return bitmap;
	}</span>

在网上看到有人(不知哪位大牛)用WeakReference弱引用来防止绘图开销。这里借鉴了一下:

<span style="color:#c0c0c0;">protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		// super.onDraw(canvas);
		Bitmap bmp = (mBufferBitmap == null ? null : mBufferBitmap.get());
		if (bmp == null || bmp.isRecycled()) {
			if (drawable != null) {
				bmp = getClippedBitmap(drawable);//get round image
				canvas.drawBitmap(bmp, 0, 0, null);
				mBufferBitmap = new WeakReference<Bitmap>(bmp);//save
			}
		} else {
			mPaint.setXfermode(null);
			canvas.drawBitmap(bmp, 0.0f, 0.0f, mPaint);
			return;
		}
	}</span>

如果需要图片有点击变暗效果,可以利用矩阵来实现:

<span style="color:#c0c0c0;">onTouchListener = new View.OnTouchListener() {
			@Override
	        public boolean onTouch(View view, MotionEvent event) {
	            switch (event.getAction()) {
	            case MotionEvent.ACTION_UP:
	                changeLight((ImageView) view, 0);
	                //view.performClick();
	                break;
	            case MotionEvent.ACTION_DOWN:
	                changeLight((ImageView) view, -80);
	                break;
	            case MotionEvent.ACTION_MOVE:
	                // changeLight(view, 0);
	                break;
	            case MotionEvent.ACTION_CANCEL:
	                changeLight((ImageView) view, 0);
	                break;
	            default:
	                break;
	            }
	            return false;//must return false,then onclick() will be invoked
	        }

	    };
	    this.setFocusable(true);
	    this.setClickable(true);
	    this.setLongClickable(true);
	    this.setOnTouchListener(onTouchListener);</span>
<span style="color:#c0c0c0;">private void changeLight(ImageView imageview, int brightness) {
	    ColorMatrix matrix = new ColorMatrix();
	    matrix.set(new float[] { 1, 0, 0, 0, brightness, 0, 1, 0, 0,
	            brightness, 0, 0, 1, 0, brightness, 0, 0, 0, 1, 0 });
	    imageview.setColorFilter(new ColorMatrixColorFilter(matrix));

	}</span>

②在Activity类中调整最终显示的高度(为了保证图片的宽高比)。

<span style="color:#c0c0c0;">RoundImageView iv_am = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		iv_am = (RoundImageView) this.findViewById(R.id.iv_am);
		setImageViewHeightAdjust(iv_am);
	}
	
	public void setImageViewHeightAdjust(final RoundImageView mImageView) {
		mImageView.setRoundBorderRadius(55);//corners radius
		ViewTreeObserver vto2 = mImageView.getViewTreeObserver(); 
		vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
		@Override 
		public void onGlobalLayout() { 
			mImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
			int realWidth = mImageView.getWidth();
			//int realHeight = mImageView.getHeight();
			int srcWidth = mImageView.getDrawable().getIntrinsicWidth();
			int srcHeight = mImageView.getDrawable().getIntrinsicHeight();
			LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
			param.width = realWidth;
			param.height = realWidth*srcHeight/srcWidth;//calculate real_height by the width/height
			param.setMargins(5, 5, 5, 5);
			mImageView.setLayoutParams(param);
		}
		});
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}</span>
③在布局文件中使用时,用自己定义的ImageView。


<span style="color:#c0c0c0;"><com.smc.roundimageview.RoundImageView
        android:id="@+id/iv_am"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:clickable="true"
        android:src="@drawable/image_tuisong" /></span>
        好了,现在这个ImageView就可以按照我们想要的样子显示图片了。有些人就要问了:有必要这么写吗?有必要吗?
        当你有两个或多个ImageView平分屏幕时,每个ImageView分多少像素你肯定不知道,那你就没办法在布局文件中去指定高或者宽了,如果你指定了那么不同大小和分辨率的设备上肯定会变形。在xml文件中指定的高度是无效的,我们在程序中自己算view的高。程序中也许存在一个小bug:在两个ImageView平分屏幕时,如果在java代码中指定两个ImageView的margin,那么第二张图片的margin-right会失效,只能通过xml文件指定最后一个(第二张)ImageView的margin。太累了,写个程序容易吗,连lol的力气都没有了,人在塔在,德玛西亚!



源码下载



你可能感兴趣的:(图片,布局,imageview,圆角)