https://github.com/emilsjolander/StickyListHeaders
https://github.com/emilsjolander/StickyScrollViewItems
用這個開源的包也可以是想我想要的效果,但是這裏沒有引用這個包,而是自己寫了自定義scrollview
1 在widget目錄中定義自定義scrollview控件
public class StickyScrollView 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 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 StickyScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StickyScrollView(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;
}
/**
* 找到設置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();
}
/**
*
*/
private void showStickyView() {
View curStickyView = null;
View nextStickyView = null;
for (View v : mStickyViews) {
int topOffset = v.getTop() - getScrollY();
if (topOffset <= 0) {
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)
int left = DataUtil.dp2Px(getContext(), 10);
canvas.translate(left, getScrollY() + mStickyViewTopOffset);
if (mShadowDrawable != null) {
int top = mCurrentStickyView.getHeight() + mStickyViewTopOffset;
int right = mCurrentStickyView.getWidth();
int bottom = top + (int) (density * defaultShadowHeight + 0.5f);
mShadowDrawable.setBounds(0, 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)
&& ev.getX() >= mCurrentStickyView.getLeft()
&& ev.getX() <= mCurrentStickyView.getRight();
}
}
if (redirectTouchToStickyView) {
ev.offsetLocation(
0,
-1
* ((getScrollY() + mStickyViewTopOffset) - mCurrentStickyView
.getTop()));
}
return super.dispatchTouchEvent(ev);
}
private boolean hasNotDoneActionDown = true;
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (redirectTouchToStickyView) {
ev.offsetLocation(0,
((getScrollY() + mStickyViewTopOffset) - 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);
}
}
2 在xml文件中,需要此效果的srollview中引用此自定義scrollview,並在需要固定的view中,添加屬性: android:tag="sticky"
例如:
<com.blueware.rpm.widget.StickyScrollView
android:id="@+id/SV_Pandect"
style="@style/layout_vertical"
android:layout_weight="3"
android:scrollbars="vertical" >
...........
<Button
android:id="@+id/B_browser"
android:layout_width="match_parent"
android:layout_height="@dimen/stitle_hight"
android:layout_weight="1"
android:background="#F8F8F8"
android:padding="3dp"
android:text="瀏覽器"
<span style="background-color: rgb(153, 153, 255);"> android:tag="sticky"</span>
android:textSize="@dimen/stitle_size" />
.....................
</com.blueware.rpm.widget.StickyScrollView>