I am attempting to build a RecyclerView list, with an alphabetical indexer down the right hand side. I have the list displaying perfectly and am able to scroll.
I am wondering if someone could give me some guidance in building the alphabetical indexer. I believe I need to iterate through my list and determine the letters used (I can do this). Then build the list of letters (for the indexer) and then use scrollToPosition(int position) to move to the right item in the list when a letter is selected.
The main issue that I can not work out is not building another list, it is rather the communication between the two lists. Any help would be greatly appreciated.
I believe this is the information you have been looking for as I have
You can check these libraries here:
danoz73/RecyclerViewFastScroller
as far as I remember he is the one who actually made the first alphabet indexer in recyclerview. I never made it work in my app because there was lots of problem customizing it.
AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
the readme said hes got lots of optimizations made, So i think this is the best one i could use, but i havent made it work in my app for now.
code-computerlove/FastScrollRecyclerView
I think this is the design you want right? So either of these libs you used it should suffice your needs.
hope it helps
If anyone would like to use the FastScrollRecyclerView mentioned above in Xamarin, I just ported the main classes (you'll still need to create the xml resource and prep your index etc etc - see the original repo for a guide, it's pretty simple).
I included the tweaks suggested in the issues page to improve the ScrollToPosition and index height (which also makes it work in landscape) and also switched out the Java Handler class for an async Task.Delay. It may still have issues, it's a straight port otherwise. Seems to be ok though. Hopefully I have saved someone some time :)
public interface IFastScrollRecyclerViewAdapter
{
Dictionary<string, int> GetMapIndex();
}
//
public class FastScrollRecyclerView : RecyclerView
{
public const int INDWIDTH = 25;
public const int INDHEIGHT = 18;
public float ScaledWidth { get; set; }
public float ScaledHeight { get; set; }
public string[] Sections { get; set; }
public float Sx { get; set; }
public float Sy { get; set; }
public string Section { get; set; }
public bool ShowLetter { get; set; }
private ListHandler _listHandler;
private bool _setupThings = false;
private Context _context;
public FastScrollRecyclerView(Context context) : base(context)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
_context = context;
}
public override void OnDraw(Canvas c)
{
if (!_setupThings && GetAdapter() != null)
SetupThings();
base.OnDraw(c);
}
private void SetupThings()
{
//create az text data
var sectionSet = ((IFastScrollRecyclerViewAdapter)GetAdapter()).GetMapIndex().Keys;
var listSection = new List<string>(sectionSet);
listSection.Sort();
Sections = new string[listSection.Count];
int i = 0;
foreach (var s in listSection)
{
Sections[i++] = s;
}
ScaledWidth = INDWIDTH * _context.Resources.DisplayMetrics.Density;
var divisor = sectionSet.Count == 0 ? 1 : sectionSet.Count;
ScaledHeight = Height / divisor ;// INDHEIGHT * _context.Resources.DisplayMetrics.Density;
Sx = Width - PaddingRight - (float)(1.2 * ScaledWidth);
Sy = (float)((Height - (ScaledHeight * Sections.Length)) / 2.0);
_setupThings = true;
}
public override bool OnTouchEvent(MotionEvent motionEvent)
{
if (_setupThings)
{
var adapter = GetAdapter() as IFastScrollRecyclerViewAdapter;
var x = motionEvent.GetX();
var y = motionEvent.GetY();
switch (motionEvent.Action)
{
case MotionEventActions.Down:
{
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
//// We touched the index bar
float yy = y - PaddingTop - PaddingBottom - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
{
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
}
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Move:
{
if (!ShowLetter && (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length)))
{
return base.OnTouchEvent(motionEvent);
}
else
{
float yy = y - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Up:
{
_listHandler = new ListHandler(this);
_listHandler.DelayClear();
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
return true;
}
}
}
}
return true;
}
private class ListHandler
{
FastScrollRecyclerView _parent;
public ListHandler (FastScrollRecyclerView parent)
{
_parent = parent;
}
public async void DelayClear()
{
await Task.Delay(100);
_parent.ShowLetter = false;
_parent.Invalidate();
}
}
}
//
public class FastScrollRecyclerViewItemDecoration : ItemDecoration
{
private Context _context;
public FastScrollRecyclerViewItemDecoration(Context context)
{
_context = context;
}
public override void OnDrawOver(Canvas canvas, RecyclerView parent, State state)
{
base.OnDrawOver(canvas, parent, state);
float scaledWidth = ((FastScrollRecyclerView)parent).ScaledWidth;
float sx = ((FastScrollRecyclerView)parent).Sx;
float scaledHeight = ((FastScrollRecyclerView)parent).ScaledHeight;
float sy = ((FastScrollRecyclerView)parent).Sy;
string[] sections = ((FastScrollRecyclerView)parent).Sections;
string section = ((FastScrollRecyclerView)parent).Section;
bool showLetter = ((FastScrollRecyclerView)parent).ShowLetter;
// We draw the letter in the middle
if (showLetter & section != null && !section.Equals(""))
{
//overlay everything when displaying selected index Letter in the middle
Paint overlayDark = new Paint();
overlayDark.Color = Color.Black;
overlayDark.Alpha = 100;
canvas.DrawRect(0, 0, parent.Width, parent.Height, overlayDark);
float middleTextSize = _context.Resources.GetDimension(Resource.Dimension.fast_scroll_overlay_text_size );
Paint middleLetter = new Paint();
middleLetter.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
middleLetter.TextSize = middleTextSize;
middleLetter.AntiAlias = true;
middleLetter.FakeBoldText = true;
middleLetter.SetStyle(Paint.Style.Fill);
int xPos = (canvas.Width - (int)middleTextSize) / 2;
int yPos = (int)((canvas.Height / 2) - ((middleLetter.Descent() + middleLetter.Ascent()) / 2));
canvas.DrawText(section.ToUpper(), xPos, yPos, middleLetter);
}
// // draw indez A-Z
Paint textPaint = new Paint();
textPaint.AntiAlias = true;
textPaint.SetStyle(Paint.Style.Fill);
for (int i = 0; i < sections.Length; i++)
{
if (showLetter & section != null && !section.Equals("") && section != null
&& sections[i].ToUpper().Equals(section.ToUpper()))
{
textPaint.Color = Color.White;
textPaint.Alpha = 255;
textPaint.FakeBoldText = true;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
textPaint.TextSize = scaledWidth;
canvas.DrawText("•",
sx - textPaint.TextSize / 3, sy + parent.PaddingTop
+ scaledHeight * (i + 1) + scaledHeight / 3, textPaint);
}
else
{
textPaint.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
textPaint.Alpha = 200;
textPaint.FakeBoldText = false;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With