使用ScrollerView+RecycleView实现股票列表联动效果;一个可以拿来可以直接使用的解决方案,而且滑动流畅,带标题栏悬浮功能,支持整行的点击效果,支持长安事件;
效果如下图所示:
实现原理:
使用两个ScrollView:一个控制垂直方向上的滑动,一个控制水平方向上的滚动;
public class SVerticallScrollView extends ScrollView {
private int downY;
private int mTouchSlop;
private static SVYOnScrollListener myOnScrollListener;
public SVerticallScrollView(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public SVerticallScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public SVerticallScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downY = (int) e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) e.getRawY();
if (Math.abs(moveY - downY) > mTouchSlop) {
return true;
}
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(e);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(myOnScrollListener != null) {
myOnScrollListener.onScroll(t);
}
}
public void setSVYOnScrollListener(SVYOnScrollListener listener) {
myOnScrollListener = listener;
}
public interface SVYOnScrollListener{
void onScroll(int scrolly);
}
}
public class SHorizontalScrollView extends HorizontalScrollView {
private static SHorizontalScrollView.SHXOnScrollListener myOnScrollListener;
public SHorizontalScrollView(Context context) {
super(context);
}
public SHorizontalScrollView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SHorizontalScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private float lastX, lastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = e.getX();
lastY = e.getY();
break;
case MotionEvent.ACTION_MOVE:
// 只要横向大于竖向,就拦截掉事件。
// 部分机型点击事件(slopx==slopy==0),会触发MOVE事件。
// 所以要加判断(slopX > 0 || sloy > 0)
float slopX = Math.abs(e.getX() - lastX);
float slopY = Math.abs(e.getY() - lastY);
if((slopX > 0 || slopY > 0) && slopX >= slopY){
requestDisallowInterceptTouchEvent(true);
return true;
}
break;
}
return super.onInterceptTouchEvent(e);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(myOnScrollListener != null) {
myOnScrollListener.onScrollX(l);
}
}
public void setSVHOnScrollListener(SHorizontalScrollView.SHXOnScrollListener listener) {
myOnScrollListener = listener;
}
public interface SHXOnScrollListener{
void onScrollX(int scrollX);
}
}
两个RecycleView:一个固定展示股票名称,一个可以水平滑动展示更多的股票数据信息;
使用手势来控制点击事件和长按事件:给RecycleView添加addOnItemTouchListener来获取点击和长按事件的回调
点击效果和长按效果的实现:通过手势获取到当前点击RecycleView的Item的index,获取到两个RecycleView对应位置上的ViewHold,设置其状态为pressed的状态(会显示出布局文件中设置的backgroud的selector效果),再定时恢复状态;
recyclerView.addOnItemTouchListener(new OnItemTouchListener(recyclerView, brecyclerView) {
@Override
public void onItemClick(RecyclerView.ViewHolder vh, int index) {
Toast.makeText(getBaseContext(), " onItemClick index = " + index, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(RecyclerView.ViewHolder vh, int index) {
Toast.makeText(getBaseContext(), " onItemLongClick index = " + index, Toast.LENGTH_SHORT).show();
}
});
public static abstract class OnItemTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetectorCompat mGestureDetectorCompat;
private RecyclerView mRecyclerView;
private RecyclerView mBindRecyclerView;
public OnItemTouchListener(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mGestureDetectorCompat = new GestureDetectorCompat(mRecyclerView.getContext(),
new MyGestureListener());
}
public OnItemTouchListener(RecyclerView recyclerView, RecyclerView brecyclerView) {
mRecyclerView = recyclerView;
mBindRecyclerView = brecyclerView;
mGestureDetectorCompat = new GestureDetectorCompat(mRecyclerView.getContext(),
new MyGestureListener());
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetectorCompat.onTouchEvent(e);
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetectorCompat.onTouchEvent(e);
return false;
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public abstract void onItemClick(RecyclerView.ViewHolder vh, int index);
public abstract void onItemLongClick(RecyclerView.ViewHolder vh, int index);
Handler handler = new Handler();
private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
View childe = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (childe != null) {
RecyclerView.ViewHolder VH = mRecyclerView.getChildViewHolder(childe);
int index = VH.getLayoutPosition();
setPressState(VH, index);
onItemClick(VH, index);
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View childe = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (childe != null) {
RecyclerView.ViewHolder VH = mRecyclerView.getChildViewHolder(childe);
int index = VH.getLayoutPosition();
setPressState(VH, index);
onItemLongClick(VH, index);
}
}
private void setPressState(RecyclerView.ViewHolder VH, int index) {
setPressedState(VH);
if(mBindRecyclerView != null) {
RecyclerView.ViewHolder BVH = mBindRecyclerView.findViewHolderForLayoutPosition(index);
setPressedState(BVH);
}
}
private void setPressedState(RecyclerView.ViewHolder VH) {
if(VH != null) {
if(VH instanceof NameItemAdapter.MyViewHolder) {
setNameItemPressed(VH);
} else if(VH instanceof DataItemAdapter.MyViewHolder) {
setDataItemPressed(VH);
}
}
}
private void setNameItemPressed(RecyclerView.ViewHolder VH) {
final NameItemAdapter.MyViewHolder viewHolder = (NameItemAdapter.MyViewHolder) VH;
setItemViewPressed(viewHolder.ItemView);
}
private void setDataItemPressed(RecyclerView.ViewHolder VH) {
final DataItemAdapter.MyViewHolder viewHolder = (DataItemAdapter.MyViewHolder) VH;
setItemViewPressed(viewHolder.ItemView);
}
private void setItemViewPressed(final View view) {
view.setPressed(true);
handler.postDelayed(new Runnable() {
@Override
public void run() {
view.setPressed(false);
}
}, 100);
}
}
}
悬浮效果的实现:监听垂直方向上ScrollView的滑动事件,获取滑动的y值,控制悬浮view的显示和隐藏
@Override
public void onScroll(int scrollY) {
int height = mVScrollView.getTop();
//判断滑动距离scrollY是否大于0,因为大于0的时候就是可以滑动了,此时mTabViewLayout.getTop()才能取到值。
if (scrollY > 0 && scrollY >= height) {
mTopLabView.setVisibility(View.VISIBLE);
} else {
mTopLabView.setVisibility(View.GONE);
}
}
悬浮栏左右滑动的联动效果实现:监听水平方向ScrollView的滑动事件,获取滑动的x值,实现标题栏和HScrollView的联动,达到一致的效果;
@Override
public void onScrollX(int scrollX) {
mTabDataView.setTranslationX(-scrollX);
}
界面布局xml文件:
整个项目的下载资源后续上传;