从Ancroid2.3开始,HorizontalScrollView有一个滑动到边缘的阴影阻尼效果,同样Listview,ScrollView都有,要去除这个阴影很简单,在xml里面写上android:overScrollMode=”never”就可以了,但是这个问题,就是阻尼效果也消失了,那么怎么能保留阻尼的效果并且干掉这个阴影呢,表面上看很简单,把阴影的颜色改为透明就行了,可惜的是Api并没有提供直接修改这个颜色的方法,当时产品提出了这个需求,我就研究了一下,分享出来和大家一同探讨.
之前也在他人的博客中看到一个方法,系统对于这个颜色是有内置的,只要替换掉系统提供的颜色就可以了,方法如下:
首先定义几个类
public class ResourcesEdgeEffect extends Resources {
private int overscroll_edge = getPlatformDrawableId("overscroll_edge");
private int overscroll_glow = getPlatformDrawableId("overscroll_glow");
private ContextWrapperEdgeEffect mContextWrapperEdgeEffect;
public ResourcesEdgeEffect(AssetManager assets, DisplayMetrics metrics, Configuration config) {
super(assets, metrics, config);
}
private int getPlatformDrawableId(String name) {
try {
int i = ((Integer) Class.forName("com.android.internal.R$drawable").getField(name).get(null)).intValue();
return i;
} catch (ClassNotFoundException e) {
InKeLog.e("[ContextWrapperEdgeEffect].getPlatformDrawableId()", "Cannot find internal resource class");
return 0;
} catch (NoSuchFieldException e1) {
InKeLog.e("[ContextWrapperEdgeEffect].getPlatformDrawableId()", "Internal resource id does not exist: " + name);
return 0;
} catch (IllegalArgumentException e2) {
InKeLog.e("[ContextWrapperEdgeEffect].getPlatformDrawableId()", "Cannot access internal resource id: " + name);
return 0;
} catch (IllegalAccessException e3) {
InKeLog.e("[ContextWrapperEdgeEffect].getPlatformDrawableId()", "Cannot access internal resource id: " + name);
}
return 0;
}
public Drawable getDrawable(int resId) {
try {
mContextWrapperEdgeEffect = new ContextWrapperEdgeEffect(InKeApplication.getInstance());
if (resId == this.overscroll_edge)
return mContextWrapperEdgeEffect.getResources().getDrawable(R.drawable.overscroll_edge);
if (resId == this.overscroll_glow)
return mContextWrapperEdgeEffect.getResources().getDrawable(R.drawable.overscroll_glow);
return super.getDrawable(resId);
}catch (Resources.NotFoundException e){
return InKeApplication.getInstance().getResources().getDrawable(R.drawable.overscroll_edge);
}
}
}
public class ContextWrapperEdgeEffect extends ContextWrapper {
private static ResourcesEdgeEffect RES_EDGE_EFFECT;
public ContextWrapperEdgeEffect(Context context) {
super(context);
Resources resources = context.getResources();
if (RES_EDGE_EFFECT == null)
RES_EDGE_EFFECT = new ResourcesEdgeEffect(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
}
//返回自定义的Resources
public Resources getResources() {
return RES_EDGE_EFFECT;
}
}
其中overscroll_edge,overscroll_glow是你自定义的drawable文件,改为你希望的颜色,用法很简单
public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(new ContextWrapperEdgeEffect(context), attrs, defStyle);
}
自定义一个HorizontalScrollView,姑且叫做MyHorizontalScrollView吧,在这个构造函数中把原来的super方法替换为super(new ContextWrapperEdgeEffect(context), attrs, defStyle),这样MyHorizontalScrollView自带的阴影颜色就可以改为你想要的颜色,不需要颜色的话就改为透明颜色
这个方法替换了系统的颜色属性,很有效果,但是其中也有一个问题,就是对于安卓5.0及5.0系统以上是无效的,那么问题出在哪里呢,我翻看了5.0的源码并对比了5.0之前的源码,原来源码在此做了修改,这个属性在5.0系统做了源码修改,HorizontalScrollView源码里面有两个变量mEdgeGlowLeft和mEdgeGlowRight,由他们来控制滑动到左右边缘的阴影颜色,他们都是一个EdgeEffect对象,EdgeEffect提供有setColor方法,只要修改这个颜色就可以了,可惜的是HorizontalScrollView并没有提供修改方法,mEdgeGlowLeft和mEdgeGlowRight也是私有变量无法访问,那么使用反射就可以了
public static void changeEffectColor(HorizontalScrollView view, Context context, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
Field fieldLeft = null;
Field fieldRight = null;
fieldLeft = HorizontalScrollView.class.getDeclaredField("mEdgeGlowLeft");
fieldRight = HorizontalScrollView.class.getDeclaredField("mEdgeGlowRight");
fieldLeft.setAccessible(true);
fieldRight.setAccessible(true);
EdgeEffect edgeEffect = new EdgeEffect(context);
edgeEffect.setColor(color);
fieldLeft.set(view, edgeEffect);
fieldRight.set(view, edgeEffect);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
在此方法里面进行了系统版本号的判断,只处理5.0及5.0系统以上的,配合之前介绍的方法,那么可以解决目前所有版本的适配问题,这样既改变了阴影颜色,又保留了阻尼效果,或许还有更好的方法,欢迎大家探讨交流,对此有什么问题也可以留言沟通,谢谢,元旦快乐!