Don't render empty TextCells for TableSections without Title (bugs 26104 and 42926) (#287)

* Don't render empty TextCells for TableSections without Title (bugs 26104 and 42926)

* Cache the Cells so that GetCellForPosition doesn't have to iterate all Cells every time it is called
This commit is contained in:
Michael Rumpler 2016-12-06 12:59:43 +01:00 коммит произвёл Rui Marinho
Родитель ff1bf0b5ef
Коммит 75258aa800
2 изменённых файлов: 108 добавлений и 68 удалений

Просмотреть файл

@ -4,6 +4,8 @@ using Android.Views;
using Android.Widget; using Android.Widget;
using AView = Android.Views.View; using AView = Android.Views.View;
using AListView = Android.Widget.ListView; using AListView = Android.Widget.ListView;
using System;
using System.Collections.Generic;
namespace Xamarin.Forms.Platform.Android namespace Xamarin.Forms.Platform.Android
{ {
@ -13,41 +15,62 @@ namespace Xamarin.Forms.Platform.Android
protected readonly Context Context; protected readonly Context Context;
ITableViewController Controller => _view; ITableViewController Controller => _view;
Cell _restoreFocus; Cell _restoreFocus;
Cell[] _cellCache;
Cell[] CellCache
{
get
{
if (_cellCache == null)
FillCache();
return _cellCache;
}
}
bool[] _isHeaderCache;
bool[] IsHeaderCache
{
get
{
if (_isHeaderCache == null)
FillCache();
return _isHeaderCache;
}
}
bool[] _nextIsHeaderCache;
bool[] NextIsHeaderCache
{
get
{
if (_nextIsHeaderCache == null)
FillCache();
return _nextIsHeaderCache;
}
}
public TableViewModelRenderer(Context context, AListView listView, TableView view) : base(context) public TableViewModelRenderer(Context context, AListView listView, TableView view) : base(context)
{ {
_view = view; _view = view;
Context = context; Context = context;
Controller.ModelChanged += (sender, args) => NotifyDataSetChanged(); Controller.ModelChanged += (sender, args) =>
{
InvalidateCellCache();
NotifyDataSetChanged();
};
listView.OnItemClickListener = this; listView.OnItemClickListener = this;
listView.OnItemLongClickListener = this; listView.OnItemLongClickListener = this;
} }
public override int Count public override int Count => CellCache.Length;
{
get
{
var count = 0;
//Get each adapter's count + 1 for the header
ITableModel model = Controller.Model;
int section = model.GetSectionCount();
for (var i = 0; i < section; i++)
count += model.GetRowCount(i) + 1;
return count;
}
}
public override object this[int position] public override object this[int position]
{ {
get get
{ {
bool isHeader, nextIsHeader; if (position < 0 || position >= CellCache.Length)
Cell cell = GetCellForPosition(position, out isHeader, out nextIsHeader); return null;
return cell;
return CellCache[position];
} }
} }
@ -55,15 +78,11 @@ namespace Xamarin.Forms.Platform.Android
{ {
get get
{ {
//The headers count as a view type too // 1 for the headers + 1 for each non header cell
var viewTypeCount = 1; var viewTypeCount = 1;
ITableModel model = Controller.Model; foreach (var b in IsHeaderCache)
if (!b)
//Get each adapter's ViewTypeCount viewTypeCount++;
int section = model.GetSectionCount();
for (var i = 0; i < section; i++)
viewTypeCount += model.GetRowCount(i);
return viewTypeCount; return viewTypeCount;
} }
} }
@ -80,12 +99,10 @@ namespace Xamarin.Forms.Platform.Android
public override AView GetView(int position, AView convertView, ViewGroup parent) public override AView GetView(int position, AView convertView, ViewGroup parent)
{ {
object obj = this[position];
if (obj == null)
return new AView(Context);
bool isHeader, nextIsHeader; bool isHeader, nextIsHeader;
Cell item = GetCellForPosition(position, out isHeader, out nextIsHeader); Cell item = GetCellForPosition(position, out isHeader, out nextIsHeader);
if (item == null)
return new AView(Context);
var makeBline = true; var makeBline = true;
var layout = convertView as ConditionalFocusLayout; var layout = convertView as ConditionalFocusLayout;
@ -170,22 +187,13 @@ namespace Xamarin.Forms.Platform.Android
{ {
ITableModel model = Controller.Model; ITableModel model = Controller.Model;
int sectionCount = model.GetSectionCount(); if (position < 0 || position >= CellCache.Length)
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) return;
{
if (position == 0)
return;
int size = model.GetRowCount(sectionIndex) + 1; if (IsHeaderCache[position])
return;
if (position < size) model.RowSelected(CellCache[position]);
{
model.RowSelected(sectionIndex, position - 1);
return;
}
position -= size;
}
} }
Cell GetCellForPosition(int position, out bool isHeader, out bool nextIsHeader) Cell GetCellForPosition(int position, out bool isHeader, out bool nextIsHeader)
@ -193,42 +201,66 @@ namespace Xamarin.Forms.Platform.Android
isHeader = false; isHeader = false;
nextIsHeader = false; nextIsHeader = false;
if (position < 0 || position >= CellCache.Length)
return null;
isHeader = IsHeaderCache[position];
nextIsHeader = NextIsHeaderCache[position];
return CellCache[position];
}
void FillCache()
{
ITableModel model = Controller.Model; ITableModel model = Controller.Model;
int sectionCount = model.GetSectionCount(); int sectionCount = model.GetSectionCount();
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex ++) var newCellCache = new List<Cell>();
var newIsHeaderCache = new List<bool>();
var newNextIsHeaderCache = new List<bool>();
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++)
{ {
int size = model.GetRowCount(sectionIndex) + 1; var sectionTitle = model.GetSectionTitle(sectionIndex);
var sectionRowCount = model.GetRowCount(sectionIndex);
if (position == 0) if (!string.IsNullOrEmpty(sectionTitle))
{ {
isHeader = true; Cell headerCell = model.GetHeaderCell(sectionIndex);
nextIsHeader = size == 0 && sectionIndex < sectionCount - 1; if (headerCell == null)
headerCell = new TextCell { Text = sectionTitle };
headerCell.Parent = _view;
Cell header = model.GetHeaderCell(sectionIndex); newIsHeaderCache.Add(true);
newNextIsHeaderCache.Add(sectionRowCount == 0 && sectionIndex < sectionCount - 1);
Cell resultCell = null; newCellCache.Add(headerCell);
if (header != null)
resultCell = header;
if (resultCell == null)
resultCell = new TextCell { Text = model.GetSectionTitle(sectionIndex) };
resultCell.Parent = _view;
return resultCell;
} }
if (position < size) for (int i = 0; i < sectionRowCount; i++)
{ {
nextIsHeader = position == size - 1; newIsHeaderCache.Add(false);
return (Cell)model.GetItem(sectionIndex, position - 1); newNextIsHeaderCache.Add(i == sectionRowCount - 1 && sectionIndex < sectionCount - 1);
newCellCache.Add((Cell)model.GetItem(sectionIndex, i));
} }
position -= size;
} }
return null; _cellCache = newCellCache.ToArray();
_isHeaderCache = newIsHeaderCache.ToArray();
_nextIsHeaderCache = newNextIsHeaderCache.ToArray();
}
void InvalidateCellCache()
{
_cellCache = null;
_isHeaderCache = null;
_nextIsHeaderCache = null;
}
protected override void Dispose(bool disposing)
{
if (disposing)
InvalidateCellCache();
base.Dispose(disposing);
} }
} }
} }

Просмотреть файл

@ -45,5 +45,13 @@ namespace Xamarin.Forms.Platform.Android
TableViewModelRenderer source = GetModelRenderer(listView, view); TableViewModelRenderer source = GetModelRenderer(listView, view);
listView.Adapter = source; listView.Adapter = source;
} }
protected override void Dispose(bool disposing)
{
if(disposing)
Control?.Adapter?.Dispose();
base.Dispose(disposing);
}
} }
} }