Fair warning: I am new to Android, Xamarin Forms and MVVM (though I am a seasoned desktop Windows application developer). And though I have used SO as a valuable resource for years, this is my first posted question. So I know I have a lot to learn, and I would appreciate any feedback.
For my project, I need a read-only multi-line text view (Xamarin Editor) that offers selectable text. Poking around SO and other places, I saw how to implement a custom Editor Renderer. This was pretty straight forward and seems to work well:
- a keyboard is not displayed when the Editor is focused
- the user can select and copy text
- the context menu (displayed when text is selected) contains only Copy and Select All
Most of the code below was taken from a medium.com post: https://medium.com/@anna.domashych/selectable-read-only-multiline-text-field-on-android-169c27c55408 (thank you Anna!)
Working in VS 2019, my shared project has a simple class derived from Editor, which is named SelectableLabel.
namespace RO_Editor
{
public class SelectableLabel : Editor
{
}
}
The Android project has the custom renderer:
[assembly: ExportRenderer(typeof(SelectableLabel), typeof(SelectableLabelRenderer))]
namespace RO_Editor
{
class SelectableLabelRenderer : EditorRenderer
{
public SelectableLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
Control.Background = null;
Control.SetPadding(0, 0, 0, 0);
Control.ShowSoftInputOnFocus = false;
Control.SetTextIsSelectable(true);
Control.CustomSelectionActionModeCallback = new CustomSelectionActionModeCallback();
Control.CustomInsertionActionModeCallback = new CustomInsertionActionModeCallback();
}
private class CustomInsertionActionModeCallback : Java.Lang.Object, ActionMode.ICallback
{
public bool OnCreateActionMode(ActionMode mode, IMenu menu) => false;
public bool OnActionItemClicked(ActionMode m, IMenuItem i) => false;
public bool OnPrepareActionMode(ActionMode mode, IMenu menu) => true;
public void OnDestroyActionMode(ActionMode mode) { }
}
private class CustomSelectionActionModeCallback : Java.Lang.Object, ActionMode.ICallback
{
private const int CopyId = Android.Resource.Id.Copy;
private const int SelAllId = Android.Resource.Id.SelectAll;
public bool OnActionItemClicked(ActionMode m, IMenuItem i) => false;
public bool OnCreateActionMode(ActionMode mode, IMenu menu) => true;
public void OnDestroyActionMode(ActionMode mode) { }
public bool OnPrepareActionMode(ActionMode mode, IMenu menu)
{
try
{
IMenuItem copyItem = menu.FindItem(CopyId);
var title = copyItem.TitleFormatted;
IMenuItem selAllItem = menu.FindItem(SelAllId);
var selAllTitle = selAllItem.TitleFormatted;
menu.Clear();
menu.Add(0, CopyId, 0, title);
menu.Add(0, SelAllId, 1, selAllTitle);
}
catch
{
// ignored
}
return true;
}
}
}
}
That's what I'm working with thus far. My next task is to offer a search function. That is, allow the user to specify a search string (via SearchBar) and to programmatically find the search string in the SelectableLabel text, and to select the search hits in the SelectableLabel view.
The Xamarin Editor maps to a native Android TextEdit view. TextEdit has a SetSelection() method, and I'd like to expose this method in my SelectableLabel control. What is a generally accepted way to do this?