一、背景
- 收到了设计小姐姐的一张设计图,如下所示
- 需求分析,一个可以横纵向四个方向滚动的列表
- 注意:文章穿插太多代码,最好目录跳转查看。文字部分为主要思想,代码可略过。
二、开始敲代码
方案1 自定义粗糙辣鸡儿View
- 不考虑横纵向都可以滚动的要求的话,这个图一看就像是一个RecyclerView,然后通过LayoutManager(GridManager,表格布局)控制其布局的显示方式。
- 接着考虑滚动的问题,RecycleView自己可以滚动,假如设置为纵向滚动,那么我们需要在RecyclerView监测到横向滚动的时候拦截事件。
- 实现方式:使用RelativeLayout 嵌套一个RecyclerView 。根据事件分发机制,当监测到事件时,任务首先层层向下传递,没人拦截,就传给最底层View。此时监测到横向滚动的时候,我们在父布局中onInterceptTouchEvent拦截事件,返回true,不再向下传递。由父布局直接处理。
- 问题:基础效果实现了,但是用户体验度会非常差
1.如此实验的界面,没有考虑到惯性滑动,用户滑动多少距离就移动多少距离,会觉得很卡顿;
解决办法:手势识别的onfling方法中进行处理,可以参考PhotoView 解析一文
2、同一时间只能横向移动或纵向移动,必须等一个行为停止之后,另一个才会被响应;
package com.snap.awesomeserial.ui.widget;
public class FullInformationView extends RelativeLayout {
private float mStartX = 0;
private float mMoveOffsetX = 0;
private RecyclerView mRecyclerView;
private FullInformationAdapter mAdapter;
private Context context;
private int mTriggerMoveDis = 30;
private float currentOffsetX;
private ScrollListener horizontalListener;
private ScrollListener verticalListener;
public FullInformationView(Context context) {
this(context, null);
}
public FullInformationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FullInformationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
private void initView() {
LinearLayout linearLayout = new LinearLayout(getContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.addView(createMoveRecyclerView());
addView(linearLayout, new LayoutParams(LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
public void setHorizontalListener(ScrollListener horizontalListener) {
this.horizontalListener = horizontalListener;
}
public void setVerticalListener(ScrollListener verticalListener) {
this.verticalListener = verticalListener;
}
private View createMoveRecyclerView() {
FrameLayout linearLayout = new FrameLayout(getContext());
mRecyclerView = new RecyclerView(getContext());
GridLayoutManager layoutManager = new GridLayoutManager(context, 12, GridLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
if (null != mAdapter) {
mRecyclerView.setAdapter(mAdapter);
}
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int extent = mRecyclerView.computeVerticalScrollExtent();
int range = mRecyclerView.computeVerticalScrollRange();
int offset = mRecyclerView.computeVerticalScrollOffset();
float percent = offset / ((range - extent) * 1f);
verticalListener.onScroll(percent);
}
});
linearLayout.addView(mRecyclerView, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
return linearLayout;
}
public void setAdapter(FullInformationAdapter adapter) {
mAdapter = adapter;
initView();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = ev.getX();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
int offsetX = (int) Math.abs(ev.getX() - mStartX);
if (offsetX > mTriggerMoveDis) {
return true;
} else {
return false;
}
default:
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float totalX = (getWidth() - AutoSizeUtils.dp2px(context, 1740));
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
currentOffsetX = (mStartX - event.getX()) + currentOffsetX;
if (currentOffsetX