先上效果图:
如图所示,左侧一列固定不动,右侧横滑列表。
实现方案:左侧固定列使用recyclerview,右侧HorizontalScrollView嵌套recyclerview;
布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<variable
name="presenter"
type="com.zj.holdlist.MainActivity"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="120dp"
android:layout_height="match_parent"
android:background="#FAFAFA"
android:orientation="vertical"
android:focusable="true"
>
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="币对名称"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycle_coin_name"
android:overScrollMode="never"
></android.support.v7.widget.RecyclerView>
</LinearLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="NewApi"
android:overScrollMode="never"
android:scrollbars="none"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="40dp"
android:orientation="horizontal"
android:background="#FAFAFA"
>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title1"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title2"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title3"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title4"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title5"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
<TextView
android:layout_width="120dp"
android:layout_height="40dp"
android:text="title6"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="14sp"
android:background="#f0f0f0"
/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:layout_width="720dp"
android:layout_height="match_parent"
android:id="@+id/recycle_coin_data"
android:overScrollMode="never"
></android.support.v7.widget.RecyclerView>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</layout>
布局文件有了,开始写adapter,填充数据。
public class CoinAdapter extends AbsRVAdapter<CoinBean,AbsRVAdapter.BindingViewHolder> {
private final static int TYPE_NAME=0;
private final static int TYPE_DATA=1;
public CoinAdapter(Context context) {
super(context);
}
@NonNull
@Override
public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
ViewDataBinding binding;
if (i==TYPE_NAME){
binding=ItemCoinValueBinding.inflate(mInflater,parent,false);
}else {
binding= ItemDataValueBinding.inflate(mInflater,parent,false);
}
return new BindingViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull BindingViewHolder holder, int position) {
CoinBean bean= (CoinBean) getItem(position);
if (holder.mBinding instanceof ItemCoinValueBinding){
ItemCoinValueBinding binding= (ItemCoinValueBinding) holder.mBinding;
binding.setData(bean);
binding.setPos(position);
}else if (holder.mBinding instanceof ItemDataValueBinding){
ItemDataValueBinding binding= (ItemDataValueBinding) holder.mBinding;
binding.setData(bean);
binding.setPos(position);
}
}
@Override
public int getItemViewType(int position) {
List<CoinBean> list=getDataList();
if (list!=null){
return list.get(position).viewType==0?TYPE_NAME:TYPE_DATA;
}else{
return super.getItemViewType(position);
}
}
}
主要两个布局,一个是左侧item,一个是右侧。差点忘了AbsRVAdapter是我封装的一个基类,代码奉上.
import android.content.Context;
import android.databinding.ViewDataBinding;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import java.util.ArrayList;
import java.util.List;
/**
* {@link RecyclerView}适配器基类
*
* @author Created by zh on 2018/12/09.
*/
public abstract class AbsRVAdapter<E, T extends AbsRVAdapter.BindingViewHolder> extends RecyclerView.Adapter<T> {
private List<E> mDataList;
protected Context mContext;
protected LayoutInflater mInflater;
public AbsRVAdapter(Context context) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
}
@Override
public int getItemCount() {
return mDataList == null ? getHeaderItemCount() : getHeaderItemCount() + mDataList.size();
}
public int getHeaderItemCount() {
// 在列表顶部不包含在数据列表中的Item数量,并非ListView的Header数
// 头部Item数量+dataList数量等于总的Item数量,子类必须返回正确的头部Item数量来保证映射数据列表正确
return 0;
}
public Object getItem(int position) {
if (position < getHeaderItemCount()) {
return getHeaderItem(position);
} else {
return mDataList == null ? null : mDataList.get(position - getHeaderItemCount());
}
}
public Object getHeaderItem(int position) {
return null;
}
/**
* 列表Item的位置获取对应数据列表的索引,如果找不到将返回-1
*
* @param position item位置
* @return item对应的数据项位置
*/
public int getDataIndex(int position) {
if (position < getHeaderItemCount()) {
return -1;
}
return position - getHeaderItemCount();
}
public int getDataCount() {
return mDataList == null ? 0 : mDataList.size();
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addData(List<E> data) {
if (data == null || data.isEmpty()) {
return;
}
int addCount = data.size();
if (mDataList == null) {
mDataList = new ArrayList<>(addCount);
}
int startPos = getItemCount();
mDataList.addAll(data);
notifyItemRangeInserted(startPos, addCount);
}
public void addDataToStart(E data) {
if (data == null) {
return;
}
if (mDataList == null) {
mDataList = new ArrayList<>();
}
mDataList.add(0, data);
notifyItemInserted(0);
}
public void addDataToTail(E data) {
if (data == null) {
return;
}
if (mDataList == null) {
mDataList = new ArrayList<>();
}
int pos = getItemCount();
mDataList.add(data);
notifyItemInserted(pos);
}
public void clearData() {
if (mDataList != null && mDataList.size() > 0) {
mDataList.clear();
notifyDataSetChanged();
}
}
public void setData(List<E> data) {
mDataList = data;
notifyDataSetChanged();
}
public List<E> getDataList() {
return mDataList;
}
public void addData(int index, E data) {
if (data == null) {
return;
}
if (mDataList == null) {
mDataList = new ArrayList<>();
}
mDataList.add(index, data);
notifyItemInserted(getHeaderItemCount() + index);
}
public void setDataIndex(int index,E data){
if (data==null){
return;
}
if (mDataList==null){
mDataList = new ArrayList<>();
}
mDataList.set(index,data);
notifyItemChanged(getHeaderItemCount() + index);
}
public void removeData(int index) {
if (mDataList == null) {
return;
}
mDataList.remove(index);
notifyItemRemoved(getHeaderItemCount() + index);
}
public static class BindingViewHolder extends RecyclerView.ViewHolder {
public ViewDataBinding mBinding;
public BindingViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
}
}
adapter有了开始填充数据:
简单for循环,o(╥﹏╥)o
public void initData(){
List<CoinBean> nameList=new ArrayList<>();
for (int i=0;i<20;i++){
CoinBean bean=new CoinBean();
bean.coinName="BTC"+i;
bean.pairName="pairName"+i;
bean.pairUnit="pairUnit"+i;
bean.current="current"+i;
bean.currentCny="currentCny"+i;
bean.change="change"+i;
bean.changeRate="changeRate"+i;
bean.viewType=0;
nameList.add(bean);
}
nameAdapter.setData(nameList);
List<CoinBean> dataList=new ArrayList<>();
for (int i=0;i<20;i++){
CoinBean bean=new CoinBean();
bean.coinName="BTC"+i;
bean.pairName="pairName"+i;
bean.pairUnit="pairUnit"+i;
bean.current="current"+i;
bean.currentCny="currentCny"+i;
bean.change="change"+i;
bean.changeRate="changeRate"+i;
bean.viewType=1;
dataList.add(bean);
}
dataAdapter.setData(dataList);
}
这时最基本的完成了,看下效果:
大家都能看到问题,没错左右两边不同步;
简单的思路,监听两个recyclerview的滚动事件,让另一个滚动相同的距离。
走起:
binding.recycleCoinName.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
binding.recycleCoinData.scrollBy(0,dy);
}
});
binding.recycleCoinData.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
binding.recycleCoinName.scrollBy(0,dy);
}
});
然后我们发现这么搞不行,哈哈
添加判断条件如果是左侧recyclerview在滑动时,右侧跟着移动,否则不移动;
如何判断呢,setOnTouchListener!!!
//左侧name
binding.recycleCoinName.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//手势在左侧时,右侧跟随滚动
if (isNameScroll){
binding.recycleCoinData.scrollBy(0,dy);
}
}
});
//右侧数据源
binding.recycleCoinData.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//手势在右侧时,左侧跟随滚动
if (!isNameScroll){
binding.recycleCoinName.scrollBy(0,dy);
}
}
});
//
binding.recycleCoinData.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isNameScroll=false;
break;
}
return false;
}
});
binding.recycleCoinName.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isNameScroll=true;
break;
}
return false;
}
});
到这里这个简单的demo完成。