private void addLineView(int l, int t, int r, int b) {
   if (adapter.hasLineView()) {
     lineView = adapter.getView(-2, -2, null, this);
     lineView.layout(l, t, r, b);
     addView(lineView, 0);
   }
 }
 private View makeView(int row, int column, int w, int h) {
   final int itemViewType = adapter.getItemViewType(row, column);
   final View recycledView;
   if (itemViewType == TableAdapter.IGNORE_ITEM_VIEW_TYPE) {
     recycledView = null;
   } else {
     recycledView = recycler.getRecycledView(itemViewType);
   }
   final View view = adapter.getView(row, column, recycledView, this);
   view.setTag(R.id.tag_type_view, itemViewType);
   view.setTag(R.id.tag_row, row);
   view.setTag(R.id.tag_column, column);
   view.measure(
       MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
       MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
   addTableView(view, row, column);
   return view;
 }
  /**
   * Sets the data behind this TableFixHeaders.
   *
   * @param adapter The TableAdapter which is responsible for maintaining the data backing this list
   *     and for producing a view to represent an item in that data set.
   */
  public void setAdapter(TableAdapter adapter) {
    if (this.adapter != null) {
      this.adapter.unregisterDataSetObserver(tableAdapterDataSetObserver);
    }

    this.adapter = adapter;
    tableAdapterDataSetObserver = new TableAdapterDataSetObserver();
    this.adapter.registerDataSetObserver(tableAdapterDataSetObserver);

    this.recycler = new Recycler(adapter.getViewTypeCount());

    scrollX = 0;
    scrollY = 0;
    firstColumn = 0;
    firstRow = 0;

    needRelayout = true;
    requestLayout();
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    final int w;
    final int h;

    if (adapter != null) {
      this.rowCount = adapter.getRowCount();
      this.columnCount = adapter.getColumnCount();

      widths = new int[columnCount + 1];
      for (int i = -1; i < columnCount; i++) {
        widths[i + 1] += adapter.getWidth(i);
      }
      heights = new int[rowCount + 1];
      for (int i = -1; i < rowCount; i++) {
        heights[i + 1] += adapter.getHeight(i);
      }

      if (widthMode == MeasureSpec.AT_MOST) {
        w = Math.min(widthSize, sumArray(widths));
      } else if (widthMode == MeasureSpec.UNSPECIFIED) {
        w = sumArray(widths);
      } else {
        w = widthSize;
        int sumArray = sumArray(widths);
        if (sumArray < widthSize) {
          final float factor = widthSize / (float) sumArray;
          for (int i = 1; i < widths.length; i++) {
            widths[i] = Math.round(widths[i] * factor);
          }
          widths[0] = widthSize - sumArray(widths, 1, widths.length - 1);
        }
      }

      if (heightMode == MeasureSpec.AT_MOST) {
        h = Math.min(heightSize, sumArray(heights));
      } else if (heightMode == MeasureSpec.UNSPECIFIED) {
        h = sumArray(heights);
      } else {
        h = heightSize;
      }
    } else {
      if (heightMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
        w = 0;
        h = 0;
      } else {
        w = widthSize;
        h = heightSize;
      }
    }

    if (firstRow >= rowCount || getMaxScrollY() - getActualScrollY() < 0) {
      firstRow = 0;
      scrollY = Integer.MAX_VALUE;
    }
    if (firstColumn >= columnCount || getMaxScrollX() - getActualScrollX() < 0) {
      firstColumn = 0;
      scrollX = Integer.MAX_VALUE;
    }

    setMeasuredDimension(w, h);
  }