本项目已经上传至码云,有兴趣的哥们可以参考。
码云:https://git.oschina.net/xuzhaoyu/customviewdemo.git
跟大家分享一下最近写的两个自定义控件。
一个公职教育类的App,材料题有一个分屏的自定义控件;
另外一个金融股票类的App,股票列表界面有一个联动的RecyclerView控件,同理可以拓展到N级联动。
先看看效果图。
不要吐槽UI,咱们是程序员,重要的是逻辑的实现。UI不就是在实现基础逻辑的基础上换上美美素材嘛,给我们一套美美的素材:
咱就能飞上天,和太阳肩并肩。

分屏自定义控件与传统的DrawerLayout不同,它从下方划出,并且可以跟随手指停留到一定位置。而Android原生的通常是根据手势惯性滑动到最末端,占据整个屏幕。
联动RecyclerView,左边的RecyclerView可以上下滑动,右边的RecyclerView可以上下滑动,也可以单独左右滑动。两个RecyclerView上下滑动是一致的。
接下来分析一下代码的实现。
一、自定义分屏控件
在自定义分屏控件中,上部分为ViewPager,可以左右滑动,ViewPager的每个ItemView为可以上下滑动的ScrollView。下部分为自定义横向滑动的RecyclerView(呆会讲述为啥不用ViewPager),RecyclerView的每一个ItemView为可以上下滑动的ScrollView。
本人项目的要求是,分屏控件的下方,页面数量可变,页面内容也可以即时更新。如果使用了ViewPager,由于其缓存机制,默认情况下无法对当前页面、前一个页面(如果有的话)、后一个页面(如果有的话)即时刷新数据。本人在更改了ViewPager的缓存机制后,仅缓存当前页面,虽然可以即时刷新前一个页面和后一个页面的数据,但是当前页面依旧无法即时更新数据。因此选择自定义RecyclerView来实现类似ViewPager的效果。本人写了Helper类绑定RecyclerView来仿ViewPager,实现的代码如下:
public class PagingScrollHelper {
//需要绑定的RecyclerView
RecyclerView mRecyclerView;
//自定义的监听
private MyOnScrollListener mOnScrollListener = new MyOnScrollListener();
private MyOnFlingListener mOnFlingListener = new MyOnFlingListener();
//当前位置,RecyclerView相对于屏幕的偏移量
private int mOffsetY;
private int mOffsetX;
//前一个位置所在的位置(页面Index)
private int mPreIndex;
//可以根据需要自定义这个RecyclerView仿的ViewPager是垂直滚动还是水平滚动,默认为水平
enum ORIENTATION {
HORIZONTAL, VERTICAL, NULL
}
ORIENTATION mOrientation = ORIENTATION.HORIZONTAL;
public void setUpRecycleView(RecyclerView recycleView) {
if (recycleView == null) {
throw new IllegalArgumentException("recycleView must be not null");
}
mRecyclerView = recycleView;
//处理滑动
recycleView.setOnFlingListener(mOnFlingListener);
//设置滚动监听,记录滚动的状态,和总的偏移量
recycleView.setOnScrollListener(mOnScrollListener);
//获取滚动的方向
updateLayoutManger();
}
//更新布局管理器
public void updateLayoutManger() {
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (layoutManager != null) {
if (layoutManager.canScrollVertically()) {
mOrientation = ORIENTATION.VERTICAL;
} else if (layoutManager.canScrollHorizontally()) {
mOrientation = ORIENTATION.HORIZONTAL;
} else {
mOrientation = ORIENTATION.NULL;
}
if (mAnimator != null) {
mAnimator.cancel();
}
mOffsetX = 0;
mOffsetY = 0;
}
}
ValueAnimator mAnimator = null;
//自定义的惯性滑动监听
public class MyOnFlingListener extends RecyclerView.OnFlingListener {
@Override
public boolean onFling(int velocityX, int velocityY) {
if (mOrientation == ORIENTATION.NULL) {
return false;
}
//获取开始滚动时所在页面的index
mPreIndex = getPageIndex();
//记录滚动开始和结束的位置
int endPoint = 0;
int startPoint = 0;
//如果是垂直方向
if (mOrientation == ORIENTATION.VERTICAL) {
startPoint = mOffsetY;
if (velocityY > 0) {
endPoint = (mPreIndex + 1) * mRecyclerView.getHeight();
} else {
endPoint = mPreIndex * mRecyclerView.getHeight();
}
} else {
startPoint = mOffsetX;
if (velocityX > 0) {
endPoint = (mPreIndex + 1) * mRecyclerView.getWidth();
} else {
endPoint = mPreIndex * mRecyclerView.getWidth();
}
}
//使用动画处理滚动
if (mAnimator == null) {
mAnimator = ValueAnimator.ofInt(startPoint, endPoint);
mAnimator.setDuration(200);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int nowPoint = (int) animation.getAnimatedValue();
if (mOrientation == ORIENTATION.VERTICAL) {
int dy = nowPoint - mOffsetY;
//这里通过RecyclerView的scrollBy方法实现滚动。
mRecyclerView.scrollBy(0, dy);
} else {
int dx = nowPoint - mOffsetX;
mRecyclerView.scrollBy(dx, 0);
}
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//回调监听
if (null != mOnPageChangeListener) {
int curIndex = getPageIndex();
if (mPreIndex != curIndex) {
mOnPageChangeListener.onPageChange(curIndex);
}
}
}
});
} else {
mAnimator.cancel();
mAnimator.setIntValues(startPoint, endPoint);
}
mAnimator.start();
return true;
}
}
//自定义的滑动监听
public class MyOnScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//newState==0表示滚动停止,此时需要处理回滚
if (newState == 0 && mOrientation != ORIENTATION.NULL) {
boolean moveToNext;
int vX = 0, vY = 0;
if (mOrientation == ORIENTATION.VERTICAL) {
int absY = Math.abs(mOffsetY % recyclerView.getHeight());
//如果滑动的距离超过屏幕的一半表示需要滑动到下一页
moveToNext = absY > recyclerView.getHeight() / 2;
vY = moveToNext ? 1000 : 0;
} else {
int absX = Math.abs(mOffsetX % recyclerView.getWidth());
moveToNext = absX > recyclerView.getWidth() / 2;
vX = moveToNext ? 1000 : 0;
}
mOnFlingListener.onFling(vX, vY);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//滚动结束记录滚动的偏移量
mOffsetY += dy;
mOffsetX += dx;
}
}
//获取RecyclerVeiw当前所在的位置(索引Index)
private int getPageIndex() {
int p = 0;
if (mOrientation == ORIENTATION.VERTICAL) {
p = mOffsetY / mRecyclerView.getHeight();
} else {
p = mOffsetX / mRecyclerView.getWidth();
}
return p;
}
//定义页面切换的回调监听
onPageChangeListener mOnPageChangeListener;
public void setOnPageChangeListener(onPageChangeListener listener) {
mOnPageChangeListener = listener;
}
public interface onPageChangeListener {
void onPageChange(int index);
}
这里涉及到一个水平滑动的控件嵌套一个垂直滑动的控件。到底是水平滑动控件先响应,还是垂直滑动控件先响应,取决于用户落下手指的那一瞬间,相对于屏幕的滑动向量增量(类似于数学上导数),但有时候这个向量的增量方向为水平,而用户实际上想垂直滑动,但是垂直滑动控件是不会响应的,需要用户抬起手指,重新落下,并且落下的那一瞬间,相对于屏幕的滑动向量增量方向垂直。这样的体验很不友好。
为了优化用户体验,我们通常会做如下逻辑:判断用户滑动的手势的总向量,如果在水平方向上的位移大于垂直方向上的位移,那么让水平滑动控件响应触摸事件,否则让垂直滑动控件响应触摸事件。因此,本人在自定义分屏控件中,下方的RecyclerView也进行了处理,覆写了onInterceptTouchEvent( )方法,对手势进行拦截,代码如下:
public class SoftRecyclerView extends RecyclerView {
private float mStartX;
private float mStartY;
public SoftRecyclerView(Context context) {
super(context);
}
public SoftRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SoftRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = ev.getX();
mStartY = ev.getY();
break;
case MotionEvent.ACTION_UP:
float endX = ev.getX();
float endY = ev.getY();
float diffX = Math.abs(endX - mStartX);
float diffY = Math.abs(endY - mStartY);
mStartX = endX;
mStartY = endY;
if (diffX > diffY) {
return true;
} else {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean fling(int velocityX, int velocityY) {
return false;
}
}
可能有哥们发现我同时覆写了fling( )方法,在这里讲述一下原因。
这里存在另外一个用户体验不佳的地方。不管任何垂直与水平滑动控件的嵌套,都会存在的问题。图片不好表达,只有亲身体验一下才会明白,简要叙述一下:
假设,一个水平滚动的ViewPager,ViewPager的每一个子页面为垂直滚动的ScrollView。理论上这两个滑动控件没有手势冲突。但是实际上,Android中有一种滑动状态叫“Fling”(飞行中,即惯性滑动)。如上假设,ScrollView为子的滑动控件,当用户垂直滚动它的时候,如果滚动的速度非常大,突然间用户抬起手指,想进行水平滚动(即让水平滚动的ViewPager响应用户操作),而此时ViewPager是不会响应的,因为ScrollView处于惯性滑动中。为什么会出现这种现象呢?我们节选一部分Android系统的源码中ScrollView对onTouchEvent( )中处理。
case MotionEvent.ACTION_DOWN: {
if (getChildCount() == 0) {
return false;
}
if ((mIsBeingDragged = !mScroller.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
if (mFlingStrictSpan != null) {
mFlingStrictSpan.finish();
mFlingStrictSpan = null;
}
}
// Remember where the motion event started
mLastMotionY = (int) ev.getY();
mActivePointerId = ev.getPointerId(0);
startNestedScroll(SCROLL_AXIS_VERTICAL);
break;
}
原来源码中对Fling状态进行了判断(判断滚动Scroller的isFinished( ),如果未完成,就requestDisallowInterceptTouchEvent( ),即请求父控件不要拦截触摸事件)。
同理,RecyclerView的源码中也有类似的逻辑,截取onInterceptTouchEvent( )中的处理。
case MotionEvent.ACTION_DOWN:
if (mIgnoreMotionEventTillDown) {
mIgnoreMotionEventTillDown = false;
}
mScrollPointerId = e.getPointerId(0);
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
if (mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
}
// Clear the nested offsets
mNestedOffsets[0] = mNestedOffsets[1] = 0;
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontally) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertically) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
startNestedScroll(nestedScrollAxis);
break;
同样也是在惯性滑动的时候,请求父容器requestDisallowInterceptTouchEvent( ),不要拦截触摸手势。
根据源码的逻辑,本人将SoftRecyclerView中fling( )中逻辑去除,直接返回false,避免此RecyclerVeiw在fling状态下,内部ScrollVeiw不响应用户的触摸事件。 如果各位看官使用本人的Demo,建议把内部的ScrollView也自定义,并覆写其中的逻辑,本人不再累述。
接下来看看分屏控件的封装,先看一下布局文件。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_bottom_drawer_title"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#999999"
android:gravity="end|center_vertical"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_arrow_btn"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:background="#999999"
android:src="@mipmap/arrow"/>
</LinearLayout>
<com.demo.xuzhaoyu.customviewdemo.view.SoftRecyclerView
android:id="@+id/rv_bottom_drawer_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
这是分屏控件的下半部分,实现的代码逻辑如下:
public class BottomDrawer extends LinearLayout {
private View mView;
private float mTitleStartY;
private ViewGroup.LayoutParams mLayoutParams;
private SoftRecyclerView mRvContent;
private RecyclerView.LayoutManager mLayoutManager;
private PagingScrollHelper mScrollHelper = new PagingScrollHelper();
private ArrayList<String> mData = new ArrayList<>();
private BottomDrawerAdapter mAdapter;
private float mArrowDegree;
public BottomDrawer(Context context) {
this(context, null);
}
public BottomDrawer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomDrawer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
final ScreenUtil screenUtil = ScreenUtil.getInstance(getContext());
mView = LayoutInflater.from(getContext()).inflate(R.layout.view_bottom_drawer, this, true);
View flTitle = mView.findViewById(R.id.ll_bottom_drawer_title);
final View ivArrow = mView.findViewById(R.id.iv_arrow_btn);
mRvContent = (SoftRecyclerView) mView.findViewById(R.id.rv_bottom_drawer_content);
flTitle.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLayoutParams = BottomDrawer.this.getLayoutParams();
mTitleStartY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float endY = event.getRawY();
float diffY = endY - mTitleStartY;
mTitleStartY = endY;
mLayoutParams.height -= diffY;
if (mLayoutParams.height <= screenUtil.dip2px(40)) {
mLayoutParams.height = screenUtil.dip2px(40);
}
if (mLayoutParams.height >= screenUtil.getScreenHeight()) {
mLayoutParams.height = screenUtil.getScreenHeight();
}
mArrowDegree = (mLayoutParams.height - screenUtil.dip2px(40)) * 180f / (screenUtil.getScreenHeight() - screenUtil.dip2px(40));
ivArrow.setRotation(mArrowDegree);
BottomDrawer.this.setLayoutParams(mLayoutParams);
break;
}
return true;
}
});
mAdapter = new BottomDrawerAdapter(getContext(), mData);
mAdapter.setItemClickedListener(new BottomDrawerAdapter.ItemClickedListener() {
@Override
public void onItemClicked(View view, int position, List<String> data) {
if (mOnItemClickedListener != null) {
mOnItemClickedListener.onItemClicked(view, position, data);
}
}
});
mAdapter = new BottomDrawerAdapter(getContext(), mData);
mRvContent.setAdapter(mAdapter);
mScrollHelper.setUpRecycleView(mRvContent);
mScrollHelper.setOnPageChangeListener(new PagingScrollHelper.onPageChangeListener() {
@Override
public void onPageChange(int index) {
// TODO: 2017/7/2
}
});
mLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
mRvContent.setLayoutManager(mLayoutManager);
mScrollHelper.updateLayoutManger();
}
public void setData(ArrayList<String> data) {
mData.clear();
mData.addAll(data);
mAdapter.notifyDataSetChanged();
}
public interface OnItemClickedListener {
void onItemClicked(View view, int position, List<String> data);
}
private OnItemClickedListener mOnItemClickedListener;
public void setOnItemClickedListener(OnItemClickedListener onItemClickedListener) {
mOnItemClickedListener = onItemClickedListener;
}
}
这里的主要逻辑就是对分屏控件中的灰色条的触摸事件进行覆写,flTitle.setOnTouchListener( )。思路是当用户触摸到这个灰色条的时候,动态改变下方SoftRecyclerVeiw的高度。高度的改变量与手指触摸在垂直方向上移动的距离一致。同时根据高度的改变量,动态地控制箭头图标的旋转。
最后看一下分屏控件在Activity中的使用。这是Activity中的布局文件,一个ViewPager和一个分屏控件。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/vp_top_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.demo.xuzhaoyu.customviewdemo.view.BottomDrawer
android:id="@+id/bottom_drawer"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>
Activity中的主要代码逻辑如下:
public class BottomDrawerActivity extends AppCompatActivity {
private ViewPager mVpContent;
private BottomDrawer mBottomDrawer;
private ViewPagerAdapter mViewPagerAdapter;
public static void startAction(Context context) {
Intent intent = new Intent(context, BottomDrawerActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_drawer);
initView();
initData();
}
private void initView() {
mVpContent = (ViewPager) findViewById(R.id.vp_top_content);
mBottomDrawer = (BottomDrawer) findViewById(R.id.bottom_drawer);
}
private void initData() {
ArrayList<View> views = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
View view = LayoutInflater.from(this).inflate(R.layout.view_page_item, null);
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvTitle.setText(new StringBuilder("这是第").append(i).append("篇阅读材料"));
views.add(view);
}
mViewPagerAdapter = new ViewPagerAdapter(views);
mVpContent.setAdapter(mViewPagerAdapter);
ArrayList<String> strings = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
strings.add(new StringBuilder("这是第").append(i).append("个相关问题\n\n")
.append("材料是谁写的?\n")
.append("他身高多少?\n")
.append("他体重多少?\n")
.append("他从事什么岗位?\n\n")
.append("材料是谁写的?\n")
.append("他身高多少?\n")
.append("他体重多少?\n")
.append("他从事什么岗位?\n\n")
.append("材料是谁写的?\n")
.append("他身高多少?\n")
.append("他体重多少?\n")
.append("他从事什么岗位?\n\n")
.append("材料是谁写的?\n")
.append("他身高多少?\n")
.append("他体重多少?\n")
.append("他从事什么岗位?\n\n")
.toString()
);
}
mBottomDrawer.setData(strings);
mBottomDrawer.setOnItemClickedListener(new BottomDrawer.OnItemClickedListener() {
@Override
public void onItemClicked(View view, int position, List<String> data) {
Toast.makeText(BottomDrawerActivity.this, new StringBuilder("点击了").append(position).append("个相关问题"), Toast.LENGTH_SHORT).show();
}
});
}
}
这个控件到此为止,逻辑已经讲解完毕。
二、联动RecyclerView
这个控件相对比较简单。主题部分包括:左右两个部分,左边是一个垂直方向的RecyclerVeiw,右边是一个水平方向的ScrollVeiw,这个ScrollView中嵌套一个垂直方向的RecyclerView。这两个RecyclerVeiw在垂直滚动的方向上完全一致。
先看一下主体布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/view_self_select_title_head"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#CDCDCD"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="5dp"/>
</LinearLayout>
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/view_self_select_content_head"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#CDCDCD"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_content"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="5dp"/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
其中有两个head不属于RecyclerView,view_self_select_title_head和view_self_select_content_head,就是效果图中联动RecyclerView最上面的那行,不再累述。
看一下自定义控件的代码逻辑:
public class DoubleRecyclerView extends LinearLayout {
private RecyclerView mRvTitle;
private RecyclerView mRvContent;
private View mView;
private TextView mTvEditBtn;
private TextView mTvLastTrade;
private TextView mTvChangePercent;
private TextView mTvChange;
private TextView mTvBid;
private TextView mTvAsk;
private TextView mTvPreClose;
private TextView mTvOpen;
private TextView mTvVolume;
private TextView mTvTurOver;
private ArrayList<SelfSelectBean> mData = new ArrayList<>();
private SelfSelectAdapter mTitleAdapter;
private SelfSelectAdapter mContentAdapter;
public DoubleRecyclerView(Context context) {
this(context, null);
}
public DoubleRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DoubleRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
initData();
}
private void initData() {
mRvTitle.setLayoutManager(new LinearLayoutManager(getContext()));
mRvContent.setLayoutManager(new LinearLayoutManager(getContext()));
mTitleAdapter = new SelfSelectAdapter(getContext(), mData, SelfSelectAdapter.TYPE_TITLE);
mContentAdapter = new SelfSelectAdapter(getContext(), mData, SelfSelectAdapter.TYPE_CONTENT);
mRvTitle.setAdapter(mTitleAdapter);
mRvContent.setAdapter(mContentAdapter);
syncScroll(mRvTitle, mRvContent);
}
private void initView() {
mView = LayoutInflater.from(getContext()).inflate(R.layout.view_double_recycler_view, this, true);
mRvTitle = (RecyclerView) mView.findViewById(R.id.rv_title);
mRvContent = (RecyclerView) mView.findViewById(R.id.rv_content);
mTvEditBtn = (TextView) mView.findViewById(R.id.tv_edit_btn);
mTvLastTrade = (TextView) mView.findViewById(R.id.tv_head_last_trade);
mTvChangePercent = (TextView) mView.findViewById(R.id.tv_head_change_percent);
mTvChange = (TextView) mView.findViewById(R.id.tv_head_change);
mTvBid = (TextView) mView.findViewById(R.id.tv_head_bid);
mTvAsk = (TextView) mView.findViewById(R.id.tv_head_ask);
mTvPreClose = (TextView) mView.findViewById(R.id.tv_head_pre_close);
mTvOpen = (TextView) mView.findViewById(R.id.tv_head_open);
mTvVolume = (TextView) mView.findViewById(R.id.tv_head_volume);
mTvTurOver = (TextView) mView.findViewById(R.id.tv_head_tur_over);
}
private void syncScroll(final RecyclerView leftList, final RecyclerView rightList) {
leftList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
rightList.scrollBy(dx, dy);
}
}
});
rightList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
leftList.scrollBy(dx, dy);
}
}
});
}
public void setData(ArrayList<SelfSelectBean> data) {
if (data != null && data.size() > 0) {
mData.clear();
mData.addAll(data);
mTitleAdapter.notifyDataSetChanged();
mContentAdapter.notifyDataSetChanged();
}
}
public void addData(ArrayList<SelfSelectBean> data) {
if (data != null && data.size() > 0) {
mData.addAll(data);
mTitleAdapter.notifyDataSetChanged();
mContentAdapter.notifyDataSetChanged();
}
}
}
这里最主要的逻辑就是syncScroll( )方法,同步滚动的方法,看代码即可理解,同理,也可以扩充到N级联动。
就到此结束吧,有需要的同志可以在码云上下载Demo修改并使用,欢迎大家继续完善Demo。
网友评论