zoukankan      html  css  js  c++  java
  • 自定义view实现圆角图片

    前两天想实现一个圆角图片的效果,通过网络搜索后找到一些答案。这里自己再记录一下,加深一下自己的认识和知识理解。

    实现圆角图片的思路是自定义一个ImageView,然后通过Ondraw()重绘的功能,将drawable和一个圆形进行重叠绘制,这样就可以达到圆角的效果了。

    下面开始具体实现圆角图片的过程。

    第一步:写自定义属性文件

    首先我们需要定义一个属性。在values目录下面新建一个xml文件,这个文件用来自定义一些属性,这样我们就可以写出自己的控件了。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="RoundImageAttrs">
            <attr name="BorderRadius" format="dimension"/>
            <attr name="RoundType">
            	<enum name="circle" value="0"/>
            	<enum name="round" value="1"/>
            </attr>
        </declare-styleable>
    </resources>
    

    我来简单解释一下,declare-styleable这个标签就是用来自定义属性的,attr标签用来定义具体的属性,format可以指定很多种格式,具体有哪些属性,这里不做过多介绍了,请看博客中的介绍http://blog.csdn.net/mayingcai1987/article/details/6216655。在本例子中dimension表示尺寸的意思,这个应该在平常的开发中也用到。

    同时还有一个enum的枚举类型,我们之前在用系统控件的时候,比如 android:orientation="vertical"就是比较典型的枚举类型,只不过在本例中我们自己实现了这个枚举类型。

    定义好这个之后,我们就可以开始写我们的自定义view的代码了。最后注意的一点,这里的这个xml的名字可以随便命名(不过最好命名的比较有意义),android系统会自动找到的。

    第二步,自定义View

      这一步是本文中最重要的一步,也是实现自定义view的核心。那么我们从目的出发来探讨实现圆角image的方式。那么我们的目的是将图片变圆,在这里一般人可能有想到两种方式将图片变圆  1.把本来图片修改为圆形,其他地方都是透明2.只是让显示的时候,动态的裁剪到一些部分,然后让图片变圆。

      这两种方法的优劣我想大家一眼就能看明白。直接修改图片,带来的后果是,我如果换了一种方式了,不再是圆角,而是星型,那么我们的图片已经毁掉了,没办法在重用了。而第二种方式就可以,无论我们换什么样的表现方式,我们是需要换一种裁剪的算法,就可以实现不同的形状的图片了,那么很显然,第二种方法是以不变应万变的。

      那么现在,我们选定了第二种方式,那怎么实现呢?有人说我们自己写一个类,直接继承自view,然后所有的绘图和大小的计算我们都自己来搞,这样行吗?当然可行,但是我们可能是在重复造轮子,因为我们已经有了一个ImageView,而且它里面给我们做了很多关于图片的操作,我们何不继承自ImageView,然后做少量的工作,就可以实现圆角效果呢。方案确定,好,那我们就开始实现自己的类。

      

    public class RoundImageView extends ImageView {

      我们定义一个RoundImageView 继承自ImageView

    private int mBorderRadius;
    private int mType;
    

      然后两个成员变量,分别对应于自定义属性文件中的BorderRadius和RoundType。

    接下来我们重写构造函数

    public RoundImageView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		mPaint = new Paint();  
    	       mPaint.setAntiAlias(true);
    		TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.RoundImageAttrs);
    		
    		mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageAttrs_BorderRadius,
    				(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
    		mType = a.getInt(R.styleable.RoundImageAttrs_RoundType,0);
    		a.recycle();
    		Log.i("Log","mBorderRadius:"+mBorderRadius+"type:"+mType);
    		
    	}
    

     首先是mPaint的初始化,这是一个画刷,用来画图形的,后面会说。

      接下来是最为关键的代码, context.obtainStyledAttributes(attrs,R.styleable.RoundImageAttrs),这句代码用来获取控件上的自定义属性。

      然后下面两句

    mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageAttrs_BorderRadius,
    				(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
    mType = a.getInt(R.styleable.RoundImageAttrs_RoundType,0);

      分别获取这两个属性的值,第一个比较复杂,涉及到默认值单位的转换,(这里10的代表默认值)

      第二个就是获取RoundImageAttrs_RoundType,获取完毕后,记得一定要调用a.recycle();对资源进行释放。以便后面的其他代码可以访问这些属性资源。(理解的不透彻,但记住释放就ok)

      到现在为止我们完成了万里长征第一步,获取到了我们自定义控件的属性了。

      接下来就是我们的重头戏,重绘图片。下面我们重写了OnDraw函数

      

    @Override
    	protected void onDraw(Canvas canvas) {
    		//super.onDraw(canvas);
    		Bitmap bitmap = mWeakBitmap == null?null:mWeakBitmap.get();
    		if(bitmap == null || bitmap.isRecycled())
    		{			
    			Drawable drable = getDrawable();
    			int width = drable.getIntrinsicWidth();
    			int height = drable.getIntrinsicHeight();
    			if(drable!=null)
    			{
    				bitmap = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
    				Canvas dcanvas = new Canvas(bitmap);
    				drable.draw(dcanvas);
    				if(mMashBitmap == null || mMashBitmap.isRecycled())
    				{
    					mMashBitmap = getShapeBitmap();
    				}
    				mPaint.reset();
    				mPaint.setFilterBitmap(false);
    				mPaint.setXfermode(mXfermode);
    				dcanvas.drawBitmap(mMashBitmap, 0,0, mPaint);
    				mPaint.setXfermode(null);
    				canvas.drawBitmap(bitmap, 0,0, null);
    				mWeakBitmap = new WeakReference<Bitmap>(bitmap);
    				
    			}	
    		}
    		else
    		{
    			mPaint.setXfermode(null);
    			canvas.drawBitmap(bitmap, 0,0, null);
    			return;
    		}
    	}
    

      上面的代码可能第一次看比较迷惑,各种paint还有canvas,drawable,各种区分不清。下面我结合代码都说说。

      Bitmap bitmap = mWeakBitmap == null?null:mWeakBitmap.get();这句话是从一个弱引用中取得Bitmap图像,我们在成功创建圆形图像后,会保存起来,以供后面刷新使用。
      
      接下来我们判断bitmap,如果为空说明还没有创建过。接下来我们通过getDrawable();获取当前ImageView的drawable,里面包含了原本的图像。
      
      下面我们创建了一个临时的Bitmap对象,这个对象将保存经过我们处理之后的图像
      bitmap = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
    然后我们创建一个Canvas dcanvas = new Canvas(bitmap); drable.draw(dcanvas); 通过drawable的draw方法将原来ImageView的图像绘制到dcanvas上(其实也是画到bitmap上)。

      接下来我们获取图形(这里是圆形,后面大家可以自己定义形状)mMashBitmap = getShapeBitmap();(这个函数我们后面介绍),然后我们设置了paint的属性
    private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
    mPaint.setXfermode(mXfermode);
      这个mXfermode代表的意思是,当用paint画图时,新绘制的图像与原图像的关系。给大家一张图,就很容易理解各种绘制方式了。
      

    ,在本例子中用的就是DST_IN,想必这张图一看就明白。paint配置完后,我们就开始将新的形状绘制到原来的图像上
    dcanvas.drawBitmap(mMashBitmap, 0,0, mPaint); 此时,bitmap中保存的就是叠加之后的图片了,也就是我们最终需要的圆角图片了。最后我们将这个bitmap绘制到OnDraw函数给我们传递进来的
    canvas上,所有工作就基本做完了。canvas.drawBitmap(bitmap, 0,0, null);
    最后将绘制好的图片保存起来。mWeakBitmap = new WeakReference<Bitmap>(bitmap);

    下一次执行ondraw 的时候,我们就直接用保存好的bitmap进行绘制了,也就是我们代码中else的部分。

    最艰难的部分说完了,哈哈,如果不理解还是得多看几遍。接下来的工作就轻松了很多,对了,我们还没有实现之前那个绘制形状的函数呢。我们来绘制把。很容易的。

      

    private Bitmap getShapeBitmap()
    	{
    		Bitmap bit = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
    		Canvas can = new Canvas(bit);
    		Paint pa = new Paint(Paint.ANTI_ALIAS_FLAG);
    		pa.setColor(Color.BLACK);
    		
    		if(mType == 0)
    		{
    			can.drawCircle(getWidth()/2, getHeight()/2, mBorderRadius, pa);
    		}
    		return bit;
    		
    	}
    

      看看上面的代码,是不是很熟悉,我们之前已经接触过基本的画图方法了。想必,不用解释了,一眼都能看明白。这里我只实现了画圆的,大家可以各自发挥想想,画出各种各样的形状,哈哈,是不是很容易,我们自己实现了圆角图像,同时对于android自定义view的绘制也有了大致了解。

      对了,这里面还有一个问题,如果用户想动态修改图片怎么办,我们在内存里面保存了一个旧的图片,该怎么更新呢。其实好办,我们只需要做下面的操作就行

     public void invalidate()  
    	    {  
    	        mWeakBitmap = null;  
    	        if (mMashBitmap != null)  
    	        {  
    	        	mMashBitmap.recycle();  
    	        	mMashBitmap = null;  
    	        }  
    	        super.invalidate();  
    	    } 
    

      这个函数是当view视图重绘的时候执行的,于是乎,当我们更换图片后,让view重绘,就可以将之前保留的旧的图片信息清空啦。

      真不容易啊,终于写完了,竟然写了三个小时,看来以后自己还是得多多些博客了,不过通过写文章, 更加加深了认识,值得。

      最后如果转载,别忘了注明源地址噢,谢谢各位看官。

      转载请注明出处http://www.cnblogs.com/gaoteng/p/4222207.html     www.gaotenglife.com

  • 相关阅读:
    win7网络共享原来如此简单,WiFi共享精灵开启半天都弱爆了!
    JQUERY UI Datepicker Demo
    Official online document, install svn server in centOS
    JAVE not work in linux
    AMR 转mp3 失败
    XD, XR, DR 股票
    Linux 下MySql 重置密码
    Difinition Of Done
    Apache, Tomcat, JK Configuration Example
    Linux 安装tomcat
  • 原文地址:https://www.cnblogs.com/gaoteng/p/4222207.html
Copyright © 2011-2022 走看看