Android 2.3.1以降、OverScrollと呼ばれるScrollViewsとListsの新機能があります。
Android:overScrollMode="never"
オフにすることはできますが、オフにしたくない場合はどうすれば色を変更できますか?
Afaikこれを行う方法はありません。そこで私はそれを作成しました。プレゼンテーション:
Graemeのすばらしいカスタムリストビューv2
ListViewsとGridViewsのオーバースクロールを実行するカスタムビューを作成しました(XMLの例は少し複雑なGridView用ですが、ビューは両方で機能します):
<CustomGlowListView Android:id="@+id/searches"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_weight="1"
Android:background="@Android:color/black"
Android:layout_margin="10dp"
Android:tag="GridView"
Android:numColumns="3"
Android:horizontalSpacing="8dp"
Android:verticalSpacing="8dp">
</CustomGlowListView>
CustomGlowListViewは次のようになります。
public class CustomGlowListView extends RelativeLayout{
private ImageView underscrollEdge;
private ImageView underscrollGlow;
private ImageView overscrollGlow;
private ImageView overscrollEdge;
private AbsListView listView;
private final static float MAX_Edge_SIZE = 11f;
private final static float MAX_GLOW_SIZE = 93f;
private float scrollDistanceSinceBoundary = 0;
private Rect paddingRectangle = new Rect();
GestureDetector listViewGestureDetector;
// Gives the option of short circuiting the overscroll glow fade (Such as by scrolling away from the overscrolled Edge)
boolean interruptFade = false;
public CustomGlowListView(Context context, AttributeSet attrs)
{
super(context, attrs);
listViewGestureDetector = new GestureDetector(new ListViewGestureDetector());
if( getTag() == null ||
getTag().toString().equalsIgnoreCase("ListView")) { listView = new ListView(context); }
else if(getTag().toString().equalsIgnoreCase("GridView"))
{
listView = new GridView(context, attrs);
((GridView)listView).getSelector().getPadding(paddingRectangle);
}
listView.setId(Android.R.id.list);
listView.setOverScrollMode(OVER_SCROLL_NEVER);
addView(listView, new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
underscrollEdge = new ImageView(context);
underscrollEdge.setImageResource(R.drawable.underscroll_Edge);
underscrollEdge.setScaleType(ScaleType.FIT_XY);
underscrollGlow = new ImageView(context);
underscrollGlow.setImageResource(R.drawable.underscroll_glow);
underscrollGlow.setScaleType(ScaleType.FIT_XY);
overscrollGlow = new ImageView(context);
overscrollGlow.setImageResource(R.drawable.overscroll_glow);
overscrollGlow.setScaleType(ScaleType.FIT_XY);
overscrollEdge = new ImageView(context);
overscrollEdge.setImageResource(R.drawable.overscroll_Edge);
overscrollEdge.setScaleType(ScaleType.FIT_XY);
addView(underscrollGlow, getWideLayout(ALIGN_PARENT_TOP));
addView(underscrollEdge, getWideLayout(ALIGN_PARENT_TOP));
addView(overscrollGlow, getWideLayout(ALIGN_PARENT_BOTTOM));
addView(overscrollEdge, getWideLayout(ALIGN_PARENT_BOTTOM));
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN) interruptFade = true;
listViewGestureDetector.onTouchEvent(ev);
if(ev.getAction() == MotionEvent.ACTION_UP) reset();
return super.dispatchTouchEvent(ev);
}
private RelativeLayout.LayoutParams getWideLayout(int alignment)
{
RelativeLayout.LayoutParams returnLayout = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0);
returnLayout.addRule(alignment);
return returnLayout;
}
public void reset()
{
interruptFade = false;
new GlowShrinker().execute(scrollDistanceSinceBoundary);
scrollDistanceSinceBoundary = 0;
}
private class ListViewGestureDetector extends SimpleOnGestureListener
{
@Override
public boolean onScroll(MotionEvent downMotionEvent, MotionEvent currentMotionEvent, float distanceX, float distanceY)
{
float distanceTraveled = downMotionEvent.getY() - currentMotionEvent.getY();
if(listIsAtTop() && distanceTraveled < 0) // At top and finger moving down
{
scrollDistanceSinceBoundary -= distanceY;
scaleEdges(underscrollEdge, underscrollGlow, scrollDistanceSinceBoundary);
}
else if(listIsAtTop() && distanceTraveled > 0 && scrollDistanceSinceBoundary > 0) // At top and finger moving up while in overscroll
{
scrollDistanceSinceBoundary -= distanceY;
scaleEdges(underscrollEdge, underscrollGlow, scrollDistanceSinceBoundary);
}
else if(listIsAtBottom() && distanceTraveled > 0) // At bottom and finger moving up
{
scrollDistanceSinceBoundary += distanceY;
scaleEdges(overscrollEdge, overscrollGlow, scrollDistanceSinceBoundary);
}
else if(listIsAtBottom() && distanceTraveled < 0 && scrollDistanceSinceBoundary > 0) // At bottom and finger moving up while in overscroll
{
scrollDistanceSinceBoundary += distanceY;
scaleEdges(overscrollEdge, overscrollGlow, scrollDistanceSinceBoundary);
}
else if(scrollDistanceSinceBoundary != 0) // Neither over scrolling or under scrolling but was at last check. Reset both graphics.
{
reset();
}
Log.v(CustomGlowListView.class.getSimpleName(), "boundaryDistance = " + scrollDistanceSinceBoundary);
return false;
}
private boolean listIsAtTop() { return listView.getChildAt(0).getTop() - paddingRectangle.top == 0; }
private boolean listIsAtBottom(){ return listView.getChildAt(listView.getChildCount()-1).getBottom() + paddingRectangle.bottom == listView.getHeight(); }
}
private class GlowShrinker extends AsyncTask<Float, Integer, Void>
{
ImageView glow;
ImageView Edge;
private final int SHRINK_SPEED = 4;
private final int SHRINK_INCREMENT = 50;
@Override
protected void onPreExecute() {
if(underscrollGlow.getHeight() > 0)
{
glow = underscrollGlow;
Edge = underscrollEdge;
}
else if (overscrollGlow.getHeight() > 0)
{
glow = overscrollGlow;
Edge = overscrollEdge;
}
else
{
return;
}
}
@Override
protected Void doInBackground(Float... scrollDistanceSinceBoundary) {
if(glow != null && Edge != null)
{
int currentSize = (int) scrollDistanceSinceBoundary[0].floatValue();
int shrinkRate = (int) currentSize / SHRINK_INCREMENT;
for(int i=0; i < SHRINK_INCREMENT; i++)
{
if(interruptFade)
{
publishProgress(0);
return null;
}
currentSize -= shrinkRate;
publishProgress(currentSize);
try { Thread.sleep(SHRINK_SPEED); } catch (InterruptedException e) { }
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if(glow != null && Edge != null)
CustomGlowListView.scaleEdges(Edge, glow, 0);
}
@Override
protected void onProgressUpdate(Integer... values) {
CustomGlowListView.scaleEdges(Edge, glow, values[0]);
}
}
private static void scaleEdges(ImageView scrollEdge, ImageView scrollGlow, float scrollBy)
{
float edgeSize = scrollBy / 20;
float glowSize = scrollBy / 2;
if(edgeSize > MAX_Edge_SIZE) edgeSize = MAX_Edge_SIZE;
if(glowSize > MAX_GLOW_SIZE) glowSize = MAX_GLOW_SIZE;
setHeight(scrollEdge, edgeSize);
setHeight(scrollGlow, glowSize);
}
private static void setHeight(ImageView viewIn, float height)
{
ViewGroup.LayoutParams params = viewIn.getLayoutParams();
params.height = (int) height;
viewIn.setLayoutParams(params);
}
public AbsListView getListView()
{
return listView;
}
}
Platform\Android-10\data\res\drawable-mdpi \から取得できるオーバースクロール画像を使用してから、色相と彩度を変更して色を変更します。
これが他のListViewのカスタマイズに役立つスタートになることを願っています-何か聞いて面白いです。
実際には、カスタマイズされたListViewを使用する代わりに、色を変更する方法を単に「ハック」することができます。グロー効果は実際にはOSのリソースに埋め込まれたDrawableであり、それにColorFilterを適用できます。
int glowDrawableId = context.getResources().getIdentifier("overscroll_glow", "drawable", "Android");
Drawable androidGlow = context.getResources().getDrawable(glowDrawableId);
androidGlow.setColorFilter(brandColor, PorterDuff.Mode.MULTIPLY);
詳細については、こちらをご覧ください: http://evendanan.net/Android/branding/2013/12/09/branding-Edge-effect/
次の方法は、エッジラインを含む標準のオーバースクロールカラーを上書きします。
onCreateで1回呼び出すだけで十分です。
...
ChangeOverScrollGlowColor(getResources(), R.color.red);
...
public static final void ChangeOverScrollGlowColor( Resources res, int colorID ) {
try {
final int glowDrawableId = res.getIdentifier("overscroll_glow", "drawable", "Android");
final Drawable overscrollGlow = res.getDrawable(glowDrawableId);
overscrollGlow.setColorFilter(res.getColor(colorID), Android.graphics.PorterDuff.Mode.SRC_ATOP);
final int edgeDrawableId = res.getIdentifier("overscroll_Edge", "drawable", "Android");
final Drawable overscrollEdge = res.getDrawable(edgeDrawableId);
overscrollEdge.setColorFilter(res.getColor(colorID), Android.graphics.PorterDuff.Mode.SRC_ATOP);
} catch (Exception e) {
}
}