本文主要介绍如何使用一张资源图片为View设置一个具有按压效果的背景drawable。源于前段时间一个新的开发项目,在版本化过程中,设计师想出了一套项目压机效果规范。规范大致是这样的。对于一般的按钮来说,按钮按下效果只有两种不同的实现方式。1.按使前面的背景图像变暗,特别是在普通可绘制对象之上添加一个20%的黑色遮罩。2.降低前台背景资源按下后的透明度,具体是将正常状态下的drawable在按下时的透明度改为原来的70%。显然,这套规范会带来以下好处。设计师在制作图片的时候只需要制作一张图片,然后只需要告诉开发者相应的按压效果策略即可,可以减轻设计师绘图的负担。在客户端也是如此。无需导入两张资源图片即可实现按下效果,减少了包体大小,也省去了客户端开发者编写selector文件的麻烦。实现方案其实在之前的开发过程中,我也有过这样的想法,想着如何根据一张图片来设置View的背景,让它有压榨的效果。一开始很自然地想到了处理View的触摸事件,然后根据按下时的正常背景动态设置按下后的背景资源。但是后来发现还是比较麻烦,有时候有些View自己需要处理触摸事件,会造成冲突,所以当时就结束了。这次经过一番寻找和思考,最终还是使用了StateListDrawable来实现目标效果。StateListDrawable有一个addState方法,用于设置drawable的不同状态,包括按下、聚焦、不可用等。所以结合需求,只需要在正常状态下的drawable的基础上,计算出按下状态下的drawable,然后设置为按下状态,这样就可以实现一组资源实现按下视图的状态。具体的实施方案已经说的很清楚了,实施起来其实也很简单。代码如下。privatestaticDrawablegetBackground(@NonNullContextcontext,@DrawableResintres,@StatePressedMode.Modeintmode,@FloatRange(from=0.0f,to=1.0f)floatalpha){Drawablenormal=context.getResources().getDrawable(res);Drawablepressed=context.getResources()。getDrawable(res);pressed.mutate();//根据不同的按压要求设置不同的按压drawable]{android.R.attr.state_pressed},pressed);//正常状态stateListDrawable.addState(newint[]{},normal);returnstateListDrawable;}根据不同的按压方式处理按压的drawable.setAlpha(convertAlphaToInt(alpha));break;caseStatePressedMode.DARK:pressed.setColorFilter(alphaColor(Color.BLACK,convertAlphaToInt(alpha)),PorterDuff.Mode.SRC_ATOP);休息;默认值:按ed.setAlpha(convertAlphaToInt(alpha));}}目前该方案的实现已经放在GitHub上,StateBackgroundUtil,欢迎观察支持一些细节当设置drawable处于按下状态时,Drawablenormal=context.getResources().getDrawable(res);Drawablepressed=context.getResources().getDrawable(res);这里的normal和pressedresourceres资源是相同的id。但是在drawable的加载过程中,只要同一个res资源在内存中加载一次,drawable对应的state就会保持一致,所以这里必须设置pressed变量。按下.mutate();mutate方法的官方说明这在您需要修改从资源加载的可绘制对象的属性时特别有用。默认情况下,从同一资源加载的所有可绘制实例共享一个公共状态;接下来的效果只有当clickable设置为true时才能看到,所以当你使用StateBackgroundUtil为View设置背景却发现没有点击效果时,你应该知道该怎么做了。不足目前不支持不可点击状态,不支持彩色背景(当然可以通过形状曲线救国)***,如果发现问题请在issue或评论区,欢迎大家上传更好的方案PR。
