最近有點忙,項目進度跟的比較緊。最近需求那邊讓我們寫一個左右和上下都可滑動的列表,用來展示多個Title的值。這里我把需求簡化了一下。老規(guī)矩,先看圖。

在看到需求的時候,有在網(wǎng)上看看有沒有別人造好的輪子,找是找到了,但是它是用HorizontalScrollView、ScrollView、ListView實現(xiàn)的,效果是有了,但是ListView沒有復(fù)用了,導(dǎo)致我一次性加載800條數(shù)據(jù)時,界面卡頓,體驗很不好。而且它的點擊效果只能分別點擊左邊和右邊,并不能點擊的時候整個item都高亮。
所以,這里我就去研究了一下該怎么實現(xiàn)這個需求。
這里我封裝了一個HRecycleView去繼承RelativeLayout。
分為上下兩部分
① TitleLayout
包括左邊的"名稱"(固定不可滑動),右邊的"Title"(多個可滑動)
② Title的數(shù)據(jù)
使用的是RecyclerView
這里只要處理水平方向的手勢滑動即可,所以,我們需要去攔截手勢,使用
scrollTo方法實現(xiàn)水平滾動。請看代碼的詳細(xì)注釋。
/**
* Created by chawei on 2018/4/29.
*/
public class HRecyclerView extends RelativeLayout {
//頭部title布局
private LinearLayout mRightTitleLayout;
//手指按下時的位置
private float mStartX = 0;
//滑動時和按下時的差值
private int mMoveOffsetX = 0;
//最大可滑動差值
private int mFixX = 0;
//左邊標(biāo)題集合
private String[] mLeftTextList;
//左邊標(biāo)題的寬度集合
private int[] mLeftTextWidthList;
//右邊標(biāo)題集合
private String[] mRightTitleList = new String[]{};
//右邊標(biāo)題的寬度集合
private int[] mRightTitleWidthList = null;
//展示數(shù)據(jù)時使用的RecycleView
private RecyclerView mRecyclerView;
//RecycleView的Adapter
private Object mAdapter;
//需要滑動的View集合
private ArrayList<View> mMoveViewList = new ArrayList();
private Context context;
//右邊可滑動的總寬度
private int mRightTotalWidth = 0;
//右邊單個view的寬度
private int mRightItemWidth = 60;
//左邊view的寬度
private int mLeftViewWidth = 80;
//左邊view的高度
private int mLeftViewHeight=40;
//觸發(fā)攔截手勢的最小值
private int mTriggerMoveDis=30;
public HRecyclerView(Context context) {
this(context, null);
}
public HRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public HRecyclerView(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(createHeadLayout());
linearLayout.addView(createMoveRecyclerView());
addView(linearLayout, new LayoutParams(LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
/**
* 創(chuàng)建頭部布局
* @return
*/
private View createHeadLayout() {
LinearLayout headLayout = new LinearLayout(getContext());
headLayout.setGravity(Gravity.CENTER);
LinearLayout leftLayout = new LinearLayout(getContext());
addListHeaderTextView(mLeftTextList[0], mLeftTextWidthList[0], leftLayout);
leftLayout.setGravity(Gravity.CENTER);
headLayout.addView(leftLayout, 0, new ViewGroup.LayoutParams(dip2px(context, mLeftViewWidth), dip2px(context, mLeftViewHeight)));
mRightTitleLayout = new LinearLayout(getContext());
for (int i = 0; i < mRightTitleList.length; i++) {
addListHeaderTextView(mRightTitleList[i], mRightTitleWidthList[i], mRightTitleLayout);
}
headLayout.addView(mRightTitleLayout);
return headLayout;
}
/**
* 創(chuàng)建數(shù)據(jù)展示布局
* @return
*/
private View createMoveRecyclerView() {
RelativeLayout linearLayout = new RelativeLayout(getContext());
mRecyclerView = new RecyclerView(getContext());
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutManager);
if(null !=mAdapter){
if (mAdapter instanceof CommonAdapter) {
mRecyclerView.setAdapter((CommonAdapter) mAdapter);
mMoveViewList = ((CommonAdapter) mAdapter).getMoveViewList();
}
}
linearLayout.addView(mRecyclerView, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
return linearLayout;
}
/**
* 設(shè)置adapter
* @param adapter
*/
public void setAdapter(Object adapter) {
mAdapter = adapter;
initView();
}
/**
* 設(shè)置頭部title單個布局
* @param headerName
* @param width
* @param leftLayout
* @return
*/
private TextView addListHeaderTextView(String headerName, int width, LinearLayout leftLayout) {
TextView textView = new TextView(getContext());
textView.setText(headerName);
textView.setGravity(Gravity.CENTER);
leftLayout.addView(textView, width, dip2px(context, 50));
return textView;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = ev.getX();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = (int) Math.abs(ev.getX() - mStartX);
if (offsetX > mTriggerMoveDis) {//水平移動大于30觸發(fā)攔截
return true;
} else {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
/**
* 右邊可滑動的總寬度
* @return
*/
private int rightTitleTotalWidth() {
if (0 == mRightTotalWidth) {
for (int i = 0; i < mRightTitleWidthList.length; i++) {
mRightTotalWidth = mRightTotalWidth + mRightTitleWidthList[i];
}
}
return mRightTotalWidth;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
int offsetX = (int) Math.abs(event.getX() - mStartX);
if (offsetX > 30) {
mMoveOffsetX = (int) (mStartX - event.getX() + mFixX);
if (0 > mMoveOffsetX) {
mMoveOffsetX = 0;
} else {
//當(dāng)滑動大于最大寬度時,不在滑動(右邊到頭了)
if ((mRightTitleLayout.getWidth() + mMoveOffsetX) > rightTitleTotalWidth()) {
mMoveOffsetX = rightTitleTotalWidth() - mRightTitleLayout.getWidth();
}
}
//跟隨手指向右滾動
mRightTitleLayout.scrollTo(mMoveOffsetX, 0);
if (null != mMoveViewList) {
for (int i = 0; i < mMoveViewList.size(); i++) {
//使每個item隨著手指向右滾動
mMoveViewList.get(i).scrollTo(mMoveOffsetX, 0);
}
}
}
break;
case MotionEvent.ACTION_UP:
mFixX = mMoveOffsetX; //設(shè)置最大水平平移的寬度
break;
}
return super.onTouchEvent(event);
}
/**
* 列表頭部數(shù)據(jù)
* @param headerListData
*/
public void setHeaderListData(String[] headerListData) {
mRightTitleList = headerListData;
mRightTitleWidthList = new int[headerListData.length];
for (int i = 0; i < headerListData.length; i++) {
mRightTitleWidthList[i] = dip2px(context, mRightItemWidth);
}
mLeftTextWidthList = new int[]{dip2px(context, mLeftViewWidth)};
mLeftTextList = new String[]{"名稱"};
}
}
這里模擬了一次加載1W條數(shù)據(jù),沒有卡頓效果。
/**
* Created by chawei on 2018/4/29.
*/
public class CoinActivity extends AppCompatActivity {
private ArrayList<CoinInfo> mDataModels;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stock_layout);
HRecyclerView hRecyclerView= (HRecyclerView) findViewById(R.id.id_hrecyclerview);
mDataModels = new ArrayList<>();
for(int i=0;i<10000;i++) {
CoinInfo coinInfo = new CoinInfo();
coinInfo.name = "USDT";
coinInfo.priceLast="20.0";
coinInfo.riseRate24="0.2";
coinInfo.vol24="10020";
coinInfo.close="22.2";
coinInfo.open="40.0";
coinInfo.bid="33.2";
coinInfo.ask="19.0";
coinInfo.amountPercent = "33.3%";
mDataModels.add(coinInfo);
}
hRecyclerView.setHeaderListData(getResources().getStringArray(R.array.right_title_name));
CoinAdapter adapter = new CoinAdapter(this, mDataModels, R.layout.item_layout, new CommonViewHolder.onItemCommonClickListener() {
@Override
public void onItemClickListener(int position) {
Toast.makeText(CoinActivity.this, "position--->"+position, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClickListener(int position) {
}
});
hRecyclerView.setAdapter(adapter);
}
}
這里封裝了
RecyclerView的通用Adapter和ViewHolder。所以如果要使用HRecyclerView的setAdapter就必須繼承封裝的通用Adapter。
CommonAdapter和CommonViewHolder請點擊demo地址查看