最近想做一个TableView,主要用于展示表格数据,并且要支持滑动和自定义表格内部的元素(子view)样式
所以先占个坑,做一些准备工作。
第一步:确定一下需求
左上角的“lock”白色块,我把它称为“lockView”;上方的“abc”列我把它称为“horizontalView”;左侧的“1234567”列我把它称为“verticalView”。中间标为“GridView”的地方,表示数据域。slide表示滑动方向;lock代表滑动锁定。例如当该表格发生水平滑动时,左侧的“verticalView”是不发生变化的,同理,当发生垂直滑动时,上方的“horizontalView”是不发生变化的。这就是表格的基础功能。
另外左上角lockView控制了horizontalView的高度和verticalView的宽度,我的设想lockView是一个可自定义的View,用于展示多种不一样的功能和样式。
并且horizontalView、verticalView和GridView中每一个子元素(view)都要是可以自定义的。例如在horizontalView中,可以出现好几个不同的标签:姓名、年龄、性别、吃饭速度,这时我不一定全部使用文字来描述该标签,比如性别一栏中我可以使用图标,再比如吃饭速度一栏在数据域(GridView)中的描述不一定要用枯燥的文字,可以使用颜色标签,颜色越深,则吃饭速度越快。
第二步:理清楚了以上基本需求,接下来就是研究具体如何实现了。
我设想是自定义一个TableView继承自FrameLayout,随后是注意重写onMeasure方法来设定宽高
public class TableView extends FrameLayout{
private int width,height;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int tempWidth = getMeasuredSize(widthMeasureSpec,true);
int tempHeight = getMeasuredSize(heightMeasureSpec,false);
width = tempWidth;
height = tempHeight;
setMeasuredDimension(tempWidth,tempHeight);
}
/**
* 计算控件的实际大小
* @param length onMeasure方法的参数,widthMeasureSpec或者heightMeasureSpec
* @param isWidth 是宽度还是高度
* @return int 计算后的实际大小
*/
private int getMeasuredSize(int length, boolean isWidth){
// 模式
int specMode = MeasureSpec.getMode(length);
// 尺寸
int specSize = MeasureSpec.getSize(length);
// 计算所得的实际尺寸,要被返回
int retSize = 0;
// 得到两侧的padding(留边)
int padding = (isWidth? getPaddingLeft()+getPaddingRight():getPaddingTop()+getPaddingBottom());
// 对不同的指定模式进行判断
if(specMode==MeasureSpec.EXACTLY){ // 显式指定大小,如40dp或fill_parent
retSize = specSize + padding;
}else{ // 如果使用wrap_content
retSize = (int) (isWidth? DEF_WIDTH + padding : DEF_HEIGHT + padding);
if(specMode==MeasureSpec.AT_MOST){
retSize = Math.min(retSize, specSize);
}
}
return retSize;
}
}
完成确定宽高方法之后,随后在其中添加控件,horizontalView和verticalView我想通过RecyclerView来实现。也就是在TableView添加两个RecyclerView。
public class TableView extends FrameLayout {
//本方法在构造方法中调用
private void init(){
horizontal = new RecyclerView(mContext);
vertical = new RecyclerView(mContext);
hAdapter = new Adapter();
vAdapter = new Adapter2();
GridLayoutManager hGrid = new GridLayoutManager(mContext, 1, LinearLayoutManager.HORIZONTAL, false);
GridLayoutManager vGrid = new GridLayoutManager(mContext, 1, LinearLayoutManager.VERTICAL, false);
horizontal.setLayoutManager(hGrid);
horizontal.setAdapter(hAdapter);
vertical.setLayoutManager(vGrid);
vertical.setAdapter(vAdapter);
horizontal.setTag("horizontal");
vertical.setTag("vertical");
addView(horizontal,0);
addView(vertical,0);
}
//添加了View之后要在这里给每个View配置对应的位置
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
View view = findViewWithTag("horizontal");
view.layout(100,0,right,100);
View view1 = findViewWithTag("vertical");
view1.layout(0,100,100,bottom);
}
}
随后是按照设想,配置滑动,我可以选择在TableView中任何一个地方触发滑动,并且相应的要将horizontalView和verticalView也联动起来。也就是说需要重写onTouchEvent来实现联动
private RecyclerView tmpTouch;
int startX,startY;
@Override
public boolean onTouchEvent(MotionEvent ev) {
//先判断是垂直滑动还是水平滑动
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startX = (int) ev.getX();
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int distanceX = (int) (ev.getX() - startX);
int distanceY = (int) (ev.getY() - startY);
if (distanceX < 10 && distanceY < 10) break;
if (Math.abs(distanceX) > Math.abs(distanceY)){
tmpTouch = horizontal;
} else {
tmpTouch = vertical;
}
break;
}
if (tmpTouch != null){
tmpTouch.onTouchEvent(ev);
}
return true;
}
这里首先实现了滑动冲突的问题解决,其次将滑动事件直接传递给对应的RecyclerView。
未完待续
如果大家有兴趣可以去GitHub上看看其它大神已经写好的TableView项目
来源:CSDN
作者:w366549434
链接:https://blog.csdn.net/w366549434/article/details/104016337