Android 带字母索引的RecyclerView

Android 带字母索引的RecyclerView,android,listview,android-recyclerview,Android,Listview,Android Recyclerview,我正试图建立一个RecyclerView列表,右边有一个字母索引器。我有完美的列表显示,并能够滚动 我想知道是否有人可以给我一些关于建立字母索引的指导。我相信我需要反复浏览我的列表并确定使用的字母(我可以做到这一点)。然后构建字母列表(对于索引器),然后在选择字母时使用scrollToPosition(int位置)移动到列表中的右侧项目 我无法解决的主要问题不是建立另一个列表,而是两个列表之间的通信。任何帮助都将不胜感激。我相信这是您和我一直在寻找的信息 您可以在此处查看这些库: 据我记忆所

我正试图建立一个RecyclerView列表,右边有一个字母索引器。我有完美的列表显示,并能够滚动

我想知道是否有人可以给我一些关于建立字母索引的指导。我相信我需要反复浏览我的列表并确定使用的字母(我可以做到这一点)。然后构建字母列表(对于索引器),然后在选择字母时使用scrollToPosition(int位置)移动到列表中的右侧项目


我无法解决的主要问题不是建立另一个列表,而是两个列表之间的通信。任何帮助都将不胜感激。

我相信这是您和我一直在寻找的信息

您可以在此处查看这些库:

据我记忆所及,他是第一个在recyclerview中制作字母索引器的人。我从来没有让它在我的应用程序中工作,因为定制它有很多问题

自述文件说他做了很多优化,所以我认为这是我能用的最好的一个,但我现在还没有让它在我的应用程序中工作

我想这就是你想要的设计,对吗?因此,您使用的这些LIB中的任何一个都应该满足您的需要


希望它有帮助

如果有人想在Xamarin中使用上面提到的,我只是移植了主要类(您仍然需要创建xml资源和准备索引等-请参阅原始的repo以获得指导,它非常简单)

我包括了问题页面中建议的调整,以改进ScrollToPosition和索引高度(这也使它在横向环境中工作),并且还为async Task.Delay关闭了Java处理程序类。它可能仍然有问题,否则它是一个直端口。不过看起来还行。希望我已经为某人节省了一些时间:)

公共接口IFASTScrollRecycleServiceAdapter
{
字典GetMapIndex();
}
//

公共类FastScrollRecyclerView:RecyclerView { 公共const int INDWIDTH=25; 公共建筑内部高度=18; 公共浮点ScaledWidth{get;set;} 公共浮点缩放高度{get;set;} 公共字符串[]节{get;set;} 公共浮点Sx{get;set;} 公共浮点Sy{get;set;} 公共字符串部分{get;set;} 公共bool ShowLetter{get;set;} 私有ListHandler\u ListHandler; 私有bool_setupThings=false; 私人语境(private Context)(私人语境);; 公共FastScrollRecyclerView(上下文):基本(上下文) { _上下文=上下文; } 公共FastScrollRecyclerView(上下文,IAttributeSet属性):基本(上下文,属性) { _上下文=上下文; } 公共FastScrollRecyclerView(上下文上下文,IAttributeSet属性,int-defStyle):基(上下文,属性,defStyle) { _上下文=上下文; } 公共覆盖无效OnDraw(画布c) { 如果(!\u setupThings&&GetAdapter()!=null) 设置事物(); 碱金属(c); } 私人物品 { //创建az文本数据 var sectionSet=((IFASTScrollRecycleServiceAdapter)GetAdapter()).GetMapIndex().Keys; var listSection=新列表(sectionSet); listSection.Sort(); Sections=新字符串[listSection.Count]; int i=0; foreach(listSection中的var s) { 节[i++]=s; } ScaledWidth=INDWIDTH*_context.Resources.DisplayMetrics.Density; 变量除数=sectionSet.Count==0?1:sectionSet.Count; ScaledHeight=高度/除数;//INDHEIGHT*\u context.Resources.DisplayMetrics.Density; Sx=宽度-填充右侧-(浮动)(1.2*缩放宽度); Sy=(浮动)((高度-(缩放高度*截面长度))/2.0); _事实=真实; } 公共覆盖布尔OnTouchEvent(MotionEvent) { 如果(_) { var adapter=GetAdapter()作为IFASTScrollRecycleServiceAdapter; var x=motionEvent.GetX(); var y=motionEvent.GetY(); 开关(motionEvent.Action) { 案例MotionEventActions.Down: { if(x(Sy+缩放高度*截面长度)) { 返回base.OnTouchEvent(motionEvent); } 其他的 { ////我们触摸了索引栏 浮动yy=y-填充顶部-填充底部-Sy; int currentPosition=(int)数学楼层(yy/标度高度); 如果(currentPosition<0)currentPosition=0; 如果(currentPosition>=Sections.Length)currentPosition=Sections.Length-1; 截面=截面[当前位置]; ShowLetter=true; int positionInData=0; if(adapter.GetMapIndex().ContainsKey(Section.ToUpper())) { PositionData=adapter.GetMapIndex()[Section.ToUpper()]; } (GetLayoutManager()作为LinearLayoutManager)。使用偏移量滚动拓扑位置(位置数据,20); 使无效(); } 打破 } case MotionEventActions。移动: { 如果(!ShowLetter&&(x(Sy+ScaledHeight*Sections.Length))) { 返回base.OnTouchEvent(motionEvent); } 其他的 {
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);
                }
            }
        }
    }