平常软件开发中经常要用到自定义列表控件,难得有时间今天来分享一个常用的自定义列表控件。
话不多说,先上效果图:
刚好最近有需求需要实现一个列宽能够随着内容变化的列表控件,在网上看了好多已经造好的轮子,最后觉得最下面这个轮子比较适合我的项目,当然得对这个项目进行一些改造。
主架构:左侧固定头部textview+右边可滑动头部分由横向LinearLayout实现,下方是一个recycleview,里面item布局左侧固定,右侧也由横向LinearLayout实现。好了,话不多说直接上代码。
package com.sc.basictable.weight;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.sc.basictable.BaseListAdapter;
import com.sc.basictable.R;
import java.util.ArrayList;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* class description
* 左侧固定,右侧可滑动的列表类
* @author sc
* @date 2021-04-29
*/
public class TableGroup extends LinearLayout {
private String mHeadList[];
//标题的宽度集合
private int[] mHeadTextWidthList;
private int mHeadHeight;
private LinearLayout mRightTitleLayout;
//展示数据时使用的RecycleView
private RecyclerView mRecyclerView;
private BaseListAdapter mAdapter;
public TableGroup(Context context) {
super(context);
initData();
}
public TableGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initData();
}
public TableGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
private void initData(){
setOrientation(VERTICAL);
mHeadHeight = getResources().getDimensionPixelSize(R.dimen.dip40);
}
/**
* 创建头部布局
* @return
*/
private View createHeadLayout(){
LinearLayout headLayout = new LinearLayout(getContext());
headLayout.setGravity(Gravity.CENTER);
addHeaderTextView(mHeadList[0],mHeadTextWidthList[0],headLayout);
mRightTitleLayout = new LinearLayout(getContext());
for(int i=1;i<mHeadList.length;i++){
addHeaderTextView(mHeadList[i],mHeadTextWidthList[i],mRightTitleLayout);
}
headLayout.addView(mRightTitleLayout);
return headLayout;
}
//需要滑动的View集合
private ArrayList<View> mMoveViewList = new ArrayList();
/**
* 创建数据展示布局
* @return
*/
private View createContentLayout(){
RelativeLayout linearLayout = new RelativeLayout(getContext());
mRecyclerView = new RecyclerView(getContext());
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutManager);
if(mAdapter != null){
if(mAdapter instanceof BaseListAdapter) {
mRecyclerView.setAdapter(mAdapter);
mMoveViewList = mAdapter.getMoveViewList();
}
}
linearLayout.addView(mRecyclerView, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT));
return linearLayout;
}
private void addHeaderTextView(String name,int width,LinearLayout parentView){
TextView textView = new TextView(getContext());
textView.setText(name);
textView.setGravity(Gravity.CENTER);
parentView.addView(textView,width,mHeadHeight);
}
//手指按下时的位置
private float mStartX = 0;
//滑动时和按下时的差值
private int mMoveOffsetX = 0;
//最大可滑动差值
private int mFixX = 0;
//触发拦截手势的最小值
private int mTriggerMoveDis = 30;
@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触发拦截
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@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{
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:
case MotionEvent.ACTION_CANCEL:
mFixX = mMoveOffsetX; //设置最大水平平移的宽度
mAdapter.setFixX(mFixX); //列表得同步移动
break;
}
return super.onTouchEvent(event);
}
//右边可滑动的总宽度
private int mRightTotalWidth = 0;
/**
* 右边可滑动的总宽度
* @return
*/
private int rightTitleTotalWidth() {
if (0 == mRightTotalWidth) {
for (int i = 1; i < mHeadList.length; i++) {
mRightTotalWidth = mRightTotalWidth + mHeadTextWidthList[i];
}
}
return mRightTotalWidth;
}
/**
* 设置表头,和表头每一栏的宽度
* @param headers
* @param headWidth
*/
public void setHeaders(String headers[],int headWidth[]){
mHeadList = headers;
mHeadTextWidthList = headWidth;
}
/**
* 添加视图
* @param adapter
*/
public void setAdapter(BaseListAdapter adapter){
mAdapter = adapter;
addView(createHeadLayout());
addView(createContentLayout());
}
}
这是基础的recycleview的适配器,具体的可以根据自己的需求实现定制。
package com.sc.basictable;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* class description
* 基础适配器
* @author
* @date
*/
public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private LayoutInflater mLayoutInflater;
private List<T> mDataList;
private int mLayoutId;
private int mFixX;
private ArrayList<View> mMoveViewList = new ArrayList<>();
public BaseListAdapter(Context context,List<T> dataList,int layoutId){
mLayoutInflater = LayoutInflater.from(context);
mDataList = dataList;
mLayoutId = layoutId;
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = mLayoutInflater.inflate(mLayoutId,parent,false);
BaseViewHolder holder = new BaseViewHolder(itemView);
//获取列表右侧可滑动的部分
LinearLayout moveLayout = holder.getView(R.id.id_move_layout);//由继承的子控件自定义
setWidths(holder,moveLayout);//设置右侧滑动部分的宽度
moveLayout.scrollTo(mFixX,0);//当有新的item创建出来的时候需要滑动到之前已经滑动到的位置
mMoveViewList.add(moveLayout);
return holder;
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
bindData(holder, mDataList.get(position));
}
public abstract void bindData(BaseViewHolder holder, T data);
public abstract void setWidths(BaseViewHolder holder, LinearLayout moveLayout);
public ArrayList<View> getMoveViewList(){
return mMoveViewList;
}
@Override
public int getItemCount() {
return mDataList == null?0:mDataList.size();
}
/**
* 设置列表滚动的位置
* @param fixX
*/
public void setFixX(int fixX){
mFixX=fixX;
}
}
根据自己的需求实现提供的接口,点击事件与长按事件,当然也可自己添加实现。
package com.sc.basictable;
import android.util.SparseArray;
import android.view.View;
import android.widget.AdapterView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* class description
* 基础viewholder
* @author
* @date
*/
public class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener {
private SparseArray<View> viewSparseArray;
private onItemClickListener mOnItemClickListener;
public BaseViewHolder(@NonNull View itemView) {
super(itemView);
itemView.setOnLongClickListener(this);
itemView.setOnClickListener(this);
viewSparseArray = new SparseArray<>();
}
/**
* 根据 ID 来获取 View
*
* @param viewId viewID
* @param <T> 泛型
* @return 将结果强转为 View 或 View 的子类型
*/
public <T extends View> T getView(int viewId) {
// 先从缓存中找,找打的话则直接返回
// 如果找不到则 findViewById ,再把结果存入缓存中
View view = viewSparseArray.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
viewSparseArray.put(viewId, view);
}
return (T) view;
}
public BaseViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public interface onItemClickListener {
void onItemClickListener(int position);
void onItemLongClickListener(int position);
}
public void setOnItemClickListener(onItemClickListener listener){
mOnItemClickListener = listener;
}
@Override
public void onClick(View view) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemClickListener(getAdapterPosition());
}
}
@Override
public boolean onLongClick(View view) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemLongClickListener(getAdapterPosition());
}
return false;
}
}
详细请看:源码地址
参考文章:https://www.jianshu.com/p/320c499da51d