1、自定义滚动控件
@SuppressLint({ "HandlerLeak", "Recycle" })
public class FloatScrollView extends ScrollView {private static final String STICKY = "sticky";
private View mCurrentStickyView;
private Drawable mShadowDrawable;
private List<View> mStickyViews;
private int mStickyViewTopOffset;
private int defaultShadowHeight = 10;
private float density;
private boolean redirectTouchToStickyView;
//
private float mMarginTop;
private OnScrollListener onScrollListener;
/**
* 当点击Sticky的时候,实现某些背景的渐变
*/
private Runnable mInvalidataRunnable = new Runnable() {
@Override
public void run() {
if (mCurrentStickyView != null) {
int left = mCurrentStickyView.getLeft();
int top = mCurrentStickyView.getTop();
int right = mCurrentStickyView.getRight();
int bottom = getScrollY()
+ (mCurrentStickyView.getHeight() + mStickyViewTopOffset);
invalidate(left, top, right, bottom);
}
postDelayed(this, 16);
}
};
public FloatScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// mShadowDrawable =
// context.getResources().getDrawable(R.drawable.sticky_shadow_default);
mStickyViews = new LinkedList<View>();
density = context.getResources().getDisplayMetrics().density;
mMarginTop = context.getResources().getDimension(
R.dimen.actionbar_height);
}
/**
* 找到设置tag的View
*
* @param viewGroup
*/
private void findViewByStickyTag(ViewGroup viewGroup) {
int childCount = ((ViewGroup) viewGroup).getChildCount();
for (int i = 0; i < childCount; i++) {
View child = viewGroup.getChildAt(i);
if (getStringTagForView(child).contains(STICKY)) {
mStickyViews.add(child);
}
if (child instanceof ViewGroup) {
findViewByStickyTag((ViewGroup) child);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
findViewByStickyTag((ViewGroup) getChildAt(0));
}
showStickyView();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
showStickyView();
if (onScrollListener != null) {
onScrollListener.onScroll(getScrollY());
}
}
private void showStickyView() {
View curStickyView = null;
View nextStickyView = null;
for (View v : mStickyViews) {
int topOffset = v.getTop() - getScrollY();
if (topOffset <= 0 + mMarginTop) {
if (curStickyView == null
|| topOffset > curStickyView.getTop() - getScrollY()) {
curStickyView = v;
}
} else {
if (nextStickyView == null
|| topOffset < nextStickyView.getTop() - getScrollY()) {
nextStickyView = v;
}
}
}
if (curStickyView != null) {
mStickyViewTopOffset = nextStickyView == null ? 0 : Math.min(
0,
nextStickyView.getTop() - getScrollY()
- curStickyView.getHeight());
mCurrentStickyView = curStickyView;
post(mInvalidataRunnable);
} else {
mCurrentStickyView = null;
removeCallbacks(mInvalidataRunnable);
}
}
private String getStringTagForView(View v) {
Object tag = v.getTag();
return String.valueOf(tag);
}
/**
* 将sticky画出来
*/
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mCurrentStickyView != null) {
// 先保存起来
canvas.save();
// 将坐标原点移动到(0, getScrollY() + mStickyViewTopOffset)
canvas.translate(0, getScrollY() + mStickyViewTopOffset
+ mMarginTop);
if (mShadowDrawable != null) {
int left = 0;
int top = mCurrentStickyView.getHeight() + mStickyViewTopOffset;
int right = mCurrentStickyView.getWidth();
int bottom = top + (int) (density * defaultShadowHeight + 0.5f);
mShadowDrawable.setBounds(left, top, right, bottom);
mShadowDrawable.draw(canvas);
}
canvas.clipRect(0, mStickyViewTopOffset,
mCurrentStickyView.getWidth(),
mCurrentStickyView.getHeight());
mCurrentStickyView.draw(canvas);
// 重置坐标原点参数
canvas.restore();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
redirectTouchToStickyView = true;
}
if (redirectTouchToStickyView) {
redirectTouchToStickyView = mCurrentStickyView != null;
if (redirectTouchToStickyView) {
redirectTouchToStickyView = ev.getY() <= (mCurrentStickyView
.getHeight() + mStickyViewTopOffset + mMarginTop)
&& ev.getX() >= mCurrentStickyView.getLeft()
&& ev.getX() <= mCurrentStickyView.getRight();
}
}
if (redirectTouchToStickyView) {
ev.offsetLocation(
0,
-1
* ((getScrollY() + mStickyViewTopOffset + mMarginTop) - mCurrentStickyView
.getTop()));
}
return super.dispatchTouchEvent(ev);
}
private boolean hasNotDoneActionDown = true;
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (redirectTouchToStickyView) {
ev.offsetLocation(
0,
((getScrollY() + mStickyViewTopOffset + mMarginTop) - mCurrentStickyView
.getTop()));
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
hasNotDoneActionDown = false;
}
if (hasNotDoneActionDown) {
MotionEvent down = MotionEvent.obtain(ev);
down.setAction(MotionEvent.ACTION_DOWN);
super.onTouchEvent(down);
hasNotDoneActionDown = false;
}
if (ev.getAction() == MotionEvent.ACTION_UP
|| ev.getAction() == MotionEvent.ACTION_CANCEL) {
hasNotDoneActionDown = true;
}
return super.onTouchEvent(ev);
}
public OnScrollListener getOnScrollListener() {
return onScrollListener;
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public interface OnScrollListener {
void onScroll(int scrollY);
}
}
2、使用方式:在布局的时候对需要悬浮的布局leyout设置标签tag="sticky";进行标记即可;同时注意这个边的marginTop是用来设置悬浮位置离上方的距离,如有渐变的导航栏时需要用到。
*备注:个人备忘录!