Android 有点击动画效果的View

前言

在项目中使用了Android内部的空间RadioButton,单击该Button时会产生如下效果。

我觉得Button点击时产生的动画效果挺不错的,就寻思着自己做一个这样的效果。

思路

1.  要做出背景图片的轮廓,当点击的时候,轮廓要不断放大;
2.  轮廓放大的过程中,其透明度要不断减小,直至消失。

效果

注意,我并没有考虑图片缩放的动画。
一般的Button图片作为背景,注意,需要使用png图片:

也可以给轮廓动画换一种颜色:

也可以指定任意的png图片作为View背景:

实现

1  提取png图片“轮廓”

起初,提取轮廓我立刻想着使用opencv库来实现,但是为了使用一个View而加入一个库,岂不是太麻烦了。所以我在网上进行搜索找思路,最后定位到了Bitmap.extractAlpha()这个方法。
Bitmap extractAlpha ()

Returns a new bitmap that captures the alpha values of the original. This may be drawn with Canvas.drawBitmap(), where the color(s) will be taken from the paint that is passed to the draw call.

extractAlpha()能返回一个Bitmap,该Bitmap的值为原图alpha通道的值。该对象可以传给Canvas进行绘画。png图片没有内容的地方alpha为0,那么该方法正是我们需要的。
下述代码是我的自定义View中获取轮廓的代码片段。
其中mSrc是BitmapDrawable对象,它由你设置的图片获得;mContainer是Bitmap对象,mContainerCanvas是Canvas对象;mOutline是我们最终获得的图片轮廓。它比原图更大一点,若与原图相同大小,会被原图覆盖住。
需要澄清的是,这里的轮廓并不是opencv提取的只有图片边缘。这里的轮廓是个“伪轮廓”,它实际是由图片的alpha通道填充颜色得到。
这也是网上获取图片“轮廓”的普遍做法。
mContainer=Bitmap.createBitmap(mSrc.getIntrinsicWidth(),mSrc.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
mContainerCanvas=new Canvas(mContainer);
//在container上绘制只有alpha通道的img
mContainerCanvas.drawBitmap(mImg.extractAlpha(),0,0,mContentPainter);
Matrix m=new Matrix();
m.postScale(1.1f,1.1f);
//获得img的轮廓outline,并将它放大(比原图大一点)
mOutline=Bitmap.createBitmap(mContainer,0,0,mContainer.getWidth(),mContainer.getHeight(),m,false);

2  点击图片,使轮廓逐渐变大

这里要用到属性动画,若要兼容老版本的Android,请自行加入nineoldandroids动画库。
在onTouchEvent中使用ValueAnimator。
mAnimator=ValueAnimator.ofInt(ANIMATION_MIN_STEPS,ANIMATION_MAX_STEPS);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
	public void onAnimationUpdate(ValueAnimator animation) {
		int currentVal=(Integer) animation.getAnimatedValue();
		//轮廓逐渐变大,获取当前的放大系数
		float curOutlineRatio=MIN_ANIMATION_RATIO+(mMaxAnimationRatio-MIN_ANIMATION_RATIO)*animation.getAnimatedFraction();
		//计算轮廓大小
	        Matrix m=new Matrix();
	        m.postScale(curOutlineRatio,curOutlineRatio);
		mOutline=Bitmap.createBitmap(mContainer,0,0,mContainer.getWidth(),mContainer.getHeight(),m,false);
		//用于控制绘制状态,可在后面附着的完整代码中查看其使用逻辑
        	if (ratio==mMaxAnimationRatio){
            		mDrawOutline=false;
		}
		invalidate();
	}});
获取动画当前值,然后计算放大系数,最后使用Matrix将mOutline放大。

3  实现透明度的变化

网上有2种改变Bitmap透明度的方法。由于Bitmap没有直接可用的接口,因此只能通过创建新的Bitmap来改变透明度。
第1种方式是遍历Bitmap的每个像素,然后在Alpha非0的地方改变其值。这种方法我实现过,但是效率太低,每一帧动画都要去遍历整个Bitmap,导致动画出现了卡顿。因此我使用的是第2种方法,如下所示:
mAnimator=ValueAnimator.ofInt(ANIMATION_MIN_STEPS,ANIMATION_MAX_STEPS);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
			int currentVal=(Integer) animation.getAnimatedValue();
			//轮廓逐渐变大
			float curOutlineRatio=MIN_ANIMATION_RATIO+(mMaxAnimationRatio-MIN_ANIMATION_RATIO)*animation.getAnimatedFraction();
			//计算透明度
        		//当前透明度逐渐变小
        		int mCurAlpha=(int) (MAX_OUTLINE_ALPHA-(currentVal*1f/100f)*(MAX_OUTLINE_ALPHA-MIN_OUTLINE_ALPHA));
        		//要重新创建bitmap对象以适应新的透明度
        		mContainer=Bitmap.createBitmap(mImg.getWidth(),mImg.getHeight(), Bitmap.Config.ARGB_8888);
        		mContainerCanvas=new Canvas(mContainer);
        		mContentPainter.setAlpha(mCurAlpha);
        		mContainerCanvas.drawBitmap(mImg.extractAlpha(),0,0,mContentPainter);
			//..........................................
			//.............省略了实现轮廓大小变化的代码
		}});
还是在实现轮廓动画的监听器中加入上述代码。

4  onDraw中画出图像和轮廓

        super.onDraw(canvas);
        int width=getMeasuredWidth();
        int height=getMeasuredHeight();

        if (mPaint==null){
            mPaint=new Paint();
        }
	//控制是否绘制轮廓
        if (mDrawOutline){
            if (mOutline!=null){
                float outlineLeft=(width-mOutline.getWidth())/2;
                float outlineTop=(height-mOutline.getHeight())/2;
                canvas.drawBitmap(mOutline,outlineLeft,outlineTop,mPaint);
            }
        }

        if (mImg!=null){
            float imgLeft=(width-mImg.getWidth())/2;
            float imgTop=(height-mImg.getHeight())/2;
            canvas.drawBitmap(mImg,imgLeft,imgTop,mPaint);
        }
注意,要先画轮廓,再画图像。

结语

博客中只是简单的介绍了实现HighlightButton的关键步骤,并没有讲清楚整个自定义View的实现逻辑。
大家有兴趣可以到我的github上提取源码来看看,为了方便使用,view还要根据用户需求制定宽度或高度的自适应,也可以改变轮廓的颜色。
github上面有使用流程和注意事项,有需要改进的地方大家一定要留言告诉我啊~
github地址:https://github.com/lyzirving/HighlightButton






























阅读更多
个人分类: Android
上一篇BDLocationListener中更新UI出错分析
下一篇Android 使用代理加载插件
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭