Android自定义头部悬浮,快速索引ListView

52 篇文章 0 订阅
1 篇文章 0 订阅

现在的App的ListView大部分都有分组头部和快速滑动索引,而且分组头部还有挤压的效果,看起来比较炫,于是就在各路大神的博客里找思路,果然发现了几种比较好的实现思路。其中夏神的关于列表快速索引是最好的,我个人认为,git上也有类似的效果但好多是重写ListView实现的,导致可扩展性变得不是很好,夏神是通过自定义View和SectionIndex实现此效果的。而头部分组挤压动画则是郭神的思路最好。通过添加OnScrollListener实现,也没有修改到ListView.

效果图

现在:我主要将两位大神的代码合并,调整优化部分代码,顺便改几个小bug。
思路上面基本说清楚了,下面看主要代码实现:
1.快速索引的实现:

/** 
* @description 依附于列表的索引View
* @author rzq
* @date 2015年9月19日
*/

public class SideBar extends View
{
    /**
     * 要绘制的内容
     */
    private static String[] biao =
    { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z", "#" };

    private Context mContext;
    private Paint mPaint;
    /**
     * View的宽高
     */
    private int mWidth, mHeight;
    /**
     * 单个表的高度
     */
    private int mSingleHeight;
    private int choose = -1;
    /**
     * 
     */
    private boolean isDown;

    private OnTouchingLetterListener mChangedListener;

    public SideBar(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public SideBar(Context context)
    {
        this(context, null, 0);
    }

    public SideBar(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }

    private void init()
    {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mPaint.setTextSize(20);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        mWidth = getWidth();
        mHeight = getHeight();
        mSingleHeight = mHeight / biao.length;
    }

    /**
     * onDraw()尽量值负责与绘制相关的东西 
     */
    @Override
    protected void onDraw(Canvas canvas)
    {
        setBackgroundDrawable(isDown ? new ColorDrawable(0x99C60000) : new ColorDrawable(0x00000000));
        for (int i = 0; i < biao.length; i++)
        {
            mPaint.setColor(i == choose ? Color.parseColor("#3399ff") : Color.rgb(33, 65, 98));
            float xPos = (mWidth - mPaint.measureText(biao[i])) / 2;
            float yPos = mSingleHeight * i + mSingleHeight;
            canvas.drawText(biao[i], xPos, yPos, mPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        final int oldChoose = choose;
        float y = event.getY();
        int c = (int) (y / mHeight * biao.length);
        switch (event.getAction())
        {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            isDown = true;
            if (oldChoose != c)
            {
                if (c >= 0 && c < biao.length)
                {
                    if (mChangedListener != null)
                    {
                        mChangedListener.onTouchingletterChanged(biao[c]);
                    }
                    choose = c;
                    invalidate();
                }
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            isDown = false;
            choose = -1;
            invalidate();
            break;
        }
        // 将点击事件吃掉
        return true;
    }

    public void setOnTouchingLetterChangedListener(OnTouchingLetterListener onTouchingLetterListener)
    {
        this.mChangedListener = onTouchingLetterListener;
    }

    public interface OnTouchingLetterListener
    {
        public void onTouchingletterChanged(String s);
    }
}

package view;

import com.example.sortlistview.R;
import com.example.sortlistview.SortModel;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;

import java.util.List;

/** 
* @description 实现了SectionIndexer的可快速滑动Adapter
* @author rzq
* @date 2015年9月19日
*/
public class SortAdapter extends BaseAdapter implements SectionIndexer
{
    private List<SortModel> list = null;
    private Context mContext;

    public SortAdapter(Context mContext, List<SortModel> list)
    {
        this.mContext = mContext;
        this.list = list;
    }

    @Override
    public int getCount()
    {
        return list.size();
    }

    @Override
    public Object getItem(int position)
    {
        return list.get(position);
    }

    @Override
    public long getItemId(int position)
    {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder viewHolder = null;
        final SortModel mContent = list.get(position);
        if (convertView == null)
        {
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item, null);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.title);
            viewHolder.tvLetter = (TextView) convertView.findViewById(R.id.catalog);
            convertView.setTag(viewHolder);
        }
        else
        {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        int section = getSectionForPosition(position);
        if (position == getPositionForSection(section))
        {
            viewHolder.tvLetter.setVisibility(View.VISIBLE);
            viewHolder.tvLetter.setText(mContent.getSortLetters());
        }
        else
        {
            viewHolder.tvLetter.setVisibility(View.GONE);
        }
        viewHolder.tvTitle.setText(this.list.get(position).getName());

        return convertView;
    }

    @Override
    public Object[] getSections()
    {
        return null;
    }

    @Override
    public int getPositionForSection(int sectionIndex)
    {
        for (int i = 0; i < getCount(); i++)
        {
            String sortStr = list.get(i).getSortLetters();
            char firstChar = sortStr.toUpperCase().charAt(0);
            if (firstChar == sectionIndex)
            {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int getSectionForPosition(int position)
    {
        return list.get(position).getSortLetters().charAt(0);
    }

    final static class ViewHolder
    {
        TextView tvLetter;
        TextView tvTitle;
    }
}

代码基本copy的大神的,对一些代码的位置做了优化。主要是onDraw()让其只负责绘制相关。

2.头部挤压动画实现:

sortListView.setOnScrollListener(new OnScrollListener()
        {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState)
            {
            }

            /**
             * 滑动结束时调用 
             */
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
            {
                    /**
                     * 获取firstVisibleItem所在section
                     */
                int section = adapter.getSectionForPosition(firstVisibleItem);
                /**
                 * 获取下一item所在section
                 */
                int nextSection = adapter.getSectionForPosition(firstVisibleItem + 1);
                /**
                 * 获取下一item所在section的position
                 */
                int nextSecPosition = adapter.getPositionForSection(nextSection);
                /**
                 * 显示firstVisibleItem所在分组的头部
                 */
                if (firstVisibleItem != lastFirstVisibleItem)
                {
                    MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
                    params.topMargin = 0;
                    titleLayout.setLayoutParams(params);
                    if (String.valueOf((char) section) != null)
                    {
                        title.setText(String.valueOf((char) section));
                    }
                }
                /**
                 * 说明到了临界位置
                 */
                if (nextSecPosition == firstVisibleItem + 1)
                {
                    View childView = view.getChildAt(0);
                    if (childView != null)
                    {
                        int titleHeight = titleLayout.getHeight();
                        int bottom = childView.getBottom();
                        MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
                        /**
                         * 产生了碰撞,上移titleLayout
                         */
                        if (bottom < titleHeight)
                        {
                            float pushedDistance = bottom - titleHeight;
                            params.topMargin = (int) pushedDistance;
                            titleLayout.setLayoutParams(params);
                        }
                        else
                        {
                            if (params.topMargin != 0)
                            {
                                params.topMargin = 0;
                                titleLayout.setLayoutParams(params);
                            }
                        }
                    }
                }
                lastFirstVisibleItem = firstVisibleItem;
            }
        });

为ListView添加OnScrollListener,监听ListView的滑动。。。

夏神博客地址:http://blog.csdn.net/xiaanming/article/details/12684155
郭神博客地址:http://blog.csdn.net/guolin_blog/article/details/9033553

Demo下载

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值