Около недели назад я вернулся к этой проблеме и нашел решение.Решение требует, чтобы я сделал большую ручную настройку ширины для столбцов в этой сетке, и я считаю, что это будет крайне неоправданно в наше время.К сожалению, я также продолжал искать более всестороннее решение, встроенное в платформу Android, но я ничего не нашел.
Ниже приведен код для создания этой же сетки, если какой-либо следующиймне это нужно.Ниже я объясню некоторые более важные детали!
Макет: grid.xml
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/lightGrey">
<TableLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="2dip"
android:layout_weight="1"
android:minHeight="100dip">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TableLayout
android:id="@+id/frozenTableHeader"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="2dip"
android:layout_marginLeft="1dip"
android:stretchColumns="1"
/>
<qvtcapital.mobile.controls.ObservableHorizontalScrollView
android:id="@+id/contentTableHeaderHorizontalScrollView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/frozenTableHeader"
android:layout_marginTop="2dip"
android:layout_marginLeft="4dip"
android:layout_marginRight="1dip">
<TableLayout
android:id="@+id/contentTableHeader"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1"/>
</qvtcapital.mobile.controls.ObservableHorizontalScrollView>
</LinearLayout>
<ScrollView
android:id="@+id/verticalScrollView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TableLayout
android:id="@+id/frozenTable"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="2dip"
android:layout_marginLeft="1dip"
android:stretchColumns="1"
/>
<qvtcapital.mobile.controls.ObservableHorizontalScrollView
android:id="@+id/contentTableHorizontalScrollView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/frozenTable"
android:layout_marginTop="2dip"
android:layout_marginLeft="4dip"
android:layout_marginRight="1dip">
<TableLayout
android:id="@+id/contentTable"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1"/>
</qvtcapital.mobile.controls.ObservableHorizontalScrollView>
</LinearLayout>
</ScrollView>
</TableLayout>
Активность: Grid.java
:
public class ResultGrid extends Activity implements HorizontalScrollViewListener {
private TableLayout frozenHeaderTable;
private TableLayout contentHeaderTable;
private TableLayout frozenTable;
private TableLayout contentTable;
Typeface font;
float fontSize;
int cellWidthFactor;
ObservableHorizontalScrollView headerScrollView;
ObservableHorizontalScrollView contentScrollView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result_grid);
font = Typeface.createFromAsset(getAssets(), "fonts/consola.ttf");
fontSize = 11; // Actually this is dynamic in my application, but that code is removed for clarity
final float scale = getBaseContext().getResources().getDisplayMetrics().density;
cellWidthFactor = (int) Math.ceil(fontSize * scale * (fontSize < 10 ? 0.9 : 0.7));
Button backButton = (Button)findViewById(R.id.backButton);
frozenTable = (TableLayout)findViewById(R.id.frozenTable);
contentTable = (TableLayout)findViewById(R.id.contentTable);
frozenHeaderTable = (TableLayout)findViewById(R.id.frozenTableHeader);
contentHeaderTable = (TableLayout)findViewById(R.id.contentTableHeader);
headerScrollView = (ObservableHorizontalScrollView) findViewById(R.id.contentTableHeaderHorizontalScrollView);
headerScrollView.setScrollViewListener(this);
contentScrollView = (ObservableHorizontalScrollView) findViewById(R.id.contentTableHorizontalScrollView);
contentScrollView.setScrollViewListener(this);
contentScrollView.setHorizontalScrollBarEnabled(false); // Only show the scroll bar on the header table (so that there aren't two)
backButton.setOnClickListener(backButtonClick);
InitializeInitialData();
}
protected void InitializeInitialData() {
ArrayList<String[]> content;
Bundle myBundle = getIntent().getExtras();
try {
content = (ArrayList<String[]>) myBundle.get("gridData");
} catch (Exception e) {
content = new ArrayList<String[]>();
content.add(new String[] {"Error", "There was an error parsing the result data, please try again"} );
e.printStackTrace();
}
PopulateMainTable(content);
}
protected void PopulateMainTable(ArrayList<String[]> content) {
frozenTable.setBackgroundResource(R.color.tableBorder);
contentTable.setBackgroundResource(R.color.tableBorder);
TableLayout.LayoutParams frozenRowParams = new TableLayout.LayoutParams(
TableLayout.LayoutParams.WRAP_CONTENT,
TableLayout.LayoutParams.WRAP_CONTENT);
frozenRowParams.setMargins(1, 1, 1, 1);
frozenRowParams.weight=1;
TableLayout.LayoutParams tableRowParams = new TableLayout.LayoutParams(
TableLayout.LayoutParams.WRAP_CONTENT,
TableLayout.LayoutParams.WRAP_CONTENT);
tableRowParams.setMargins(0, 1, 1, 1);
tableRowParams.weight=1;
TableRow frozenTableHeaderRow=null;
TableRow contentTableHeaderRow=null;
int maxFrozenChars = 0;
int[] maxContentChars = new int[content.get(0).length-1];
for (int i = 0; i < content.size(); i++){
TableRow frozenRow = new TableRow(this);
frozenRow.setLayoutParams(frozenRowParams);
frozenRow.setBackgroundResource(R.color.tableRows);
TextView frozenCell = new TextView(this);
frozenCell.setText(content.get(i)[0]);
frozenCell.setTextColor(Color.parseColor("#FF000000"));
frozenCell.setPadding(5, 0, 5, 0);
if (0 == i) { frozenCell.setTypeface(font, Typeface.BOLD);
} else { frozenCell.setTypeface(font, Typeface.NORMAL); }
frozenCell.setTextSize(TypedValue.COMPLEX_UNIT_DIP, fontSize);
frozenRow.addView(frozenCell);
if (content.get(i)[0].length() > maxFrozenChars) {
maxFrozenChars = content.get(i)[0].length();
}
// The rest of them
TableRow row = new TableRow(this);
row.setLayoutParams(tableRowParams);
row.setBackgroundResource(R.color.tableRows);
for (int j = 1; j < content.get(0).length; j++) {
TextView rowCell = new TextView(this);
rowCell.setText(content.get(i)[j]);
rowCell.setPadding(10, 0, 0, 0);
rowCell.setGravity(Gravity.RIGHT);
rowCell.setTextColor(Color.parseColor("#FF000000"));
if ( 0 == i) { rowCell.setTypeface(font, Typeface.BOLD);
} else { rowCell.setTypeface(font, Typeface.NORMAL); }
rowCell.setTextSize(TypedValue.COMPLEX_UNIT_DIP, fontSize);
row.addView(rowCell);
if (content.get(i)[j].length() > maxContentChars[j-1]) {
maxContentChars[j-1] = content.get(i)[j].length();
}
}
if (i==0) {
frozenTableHeaderRow=frozenRow;
contentTableHeaderRow=row;
frozenHeaderTable.addView(frozenRow);
contentHeaderTable.addView(row);
} else {
frozenTable.addView(frozenRow);
contentTable.addView(row);
}
}
setChildTextViewWidths(frozenTableHeaderRow, new int[]{maxFrozenChars});
setChildTextViewWidths(contentTableHeaderRow, maxContentChars);
for (int i = 0; i < contentTable.getChildCount(); i++) {
TableRow frozenRow = (TableRow) frozenTable.getChildAt(i);
setChildTextViewWidths(frozenRow, new int[]{maxFrozenChars});
TableRow row = (TableRow) contentTable.getChildAt(i);
setChildTextViewWidths(row, maxContentChars);
}
}
private void setChildTextViewWidths(TableRow row, int[] widths) {
if (null==row) {
return;
}
for (int i = 0; i < row.getChildCount(); i++) {
TextView cell = (TextView) row.getChildAt(i);
int replacementWidth =
widths[i] == 1
? (int) Math.ceil(widths[i] * cellWidthFactor * 2)
: widths[i] < 3
? (int) Math.ceil(widths[i] * cellWidthFactor * 1.7)
: widths[i] < 5
? (int) Math.ceil(widths[i] * cellWidthFactor * 1.2)
:widths[i] * cellWidthFactor;
cell.setMinimumWidth(replacementWidth);
cell.setMaxWidth(replacementWidth);
}
}
public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY) {
if (scrollView==headerScrollView) {
contentScrollView.scrollTo(x, y);
} else if (scrollView==contentScrollView) {
headerScrollView.scrollTo(x, y);
}
}
Прослушиватель представления прокрутки (чтобы соединить два): HorizontalScrollViewListener.java
:
public interface HorizontalScrollViewListener {
void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
}
Класс ScrollView, который реализует этот прослушиватель: ObservableHorizontalScrollView.java
:
public class ObservableHorizontalScrollView extends HorizontalScrollView {
private HorizontalScrollViewListener scrollViewListener=null;
public ObservableHorizontalScrollView(Context context) {
super(context);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(HorizontalScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (null!=scrollViewListener) {
scrollViewListener.onScrollChanged(this, x, y, oldX, oldY);
}
}
}
Действительно важной частью этого является три вида:
- ObservableHor HorizontalScrollView позволяет синхронизировать прокручивание таблицы заголовков и таблицы содержимого.По сути, это обеспечивает все горизонтальное движение для сетки.
- Способ, которым они остаются выровненными, заключается в обнаружении наибольшей строки, которая будет в столбце.Это делается в конце
PopulateMainTable()
.Пока мы просматриваем каждый из TextView и добавляем их в строки, вы заметите, что есть два массива maxFrozenChars
и maxContentChars
, которые отслеживают, какое наибольшее значение строки мы видели.В конце PopulateMainTable()
мы перебираем каждую строку и для каждой из ячеек мы устанавливаем ее минимальную и максимальную ширину на основе наибольшей строки, которую мы видели в этом столбце.Это обрабатывается setChildTextViewWidths
. - Последний элемент, который делает эту работу, - это использование моноширинного шрифта.Вы заметите, что в
onCreate
я загружаю шрифт consola.ttf, а затем применяю его к каждому из текстовых представлений сетки, которые действуют как ячейки в сетке.Это позволяет нам быть достаточно уверенными, что текст не будет отображаться больше, чем мы установили минимальную и максимальную ширину на предыдущем шаге.Я делаю немного причуды здесь, что со всем cellWidthFactor и максимальным размером этого столбца.Это действительно так, что меньшие строки точно подойдут, в то время как мы можем минимизировать пустое пространство для больших строк, которые (для моей системы) не будут состоять из заглавных букв.Если вы столкнулись с проблемами при использовании этого и получили строки, которые не вписывались в размер столбца, который вы установили, это то место, где вы захотите что-то редактировать.Вы бы хотели изменить переменную replacementWidth
другой формулой для определения ширины ячейки, такой как 50 * widths[i]
, которая была бы довольно большой!Но оставил бы вас с хорошим количеством пробелов в некоторых столбцах.В основном, в зависимости от того, что вы планируете добавить в свою сетку, это может потребоваться изменить.Это то, что сработало для меня.
Надеюсь, это поможет кому-то еще в будущем!