自定义View:快速索引实现

这篇博客道长给大家说一下快速索引的实现,这都是道长以前积攒的自定义view,之前没整理。现在抽空整理出来和小伙伴们分享一下。

先看一下效果图:
这里写图片描述

一、绘制View

  • 构造方法
    /**
     * 构造方法
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    public QuickIndexBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public QuickIndexBar(Context context) {
        super(context);
        init(context, null, 0);
    }

我们在构造方法中对View进行初始化,代码如下:

    /**
     * 初始化属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    private void init(Context context, AttributeSet attrs, int defStyle) {
        // 默认属性
        defaultData(context);

        // 读取xml中设置的属性
        if (attrs != null) {

            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.QuickIndexBar);
            textSize = (int) typedArray.getDimension(R.styleable.QuickIndexBar_barTextSize, textSize);
            textColor = typedArray.getColor(R.styleable.QuickIndexBar_barTextColor, textColor);
        }

        // 出事化画笔
        paint = new Paint();
        // 设置抗锯齿
        paint.setAntiAlias(true);
        paint.setColor(textColor);
        paint.setTextSize(textSize);
        // 画笔绘制文本默认的起点是文本的左下角,将文本的起点设置为文本底边的中心
        paint.setTextAlign(Paint.Align.CENTER);
    }

我们看到这里有默认属性和从xml中读取的属性值,当然这个是自定义属性,关于自定义属性道长就不多说了,不知道如何使用自定义属性的小伙伴可以看一下道长以前的博客:自定义View:自定义属性(自定义按钮实现)。这篇博客中也提供了另外一种读取自定义属性值的方法。
然后,我们还可以通过代码动态设置属性值,代码如下:

    /**
     * 设置索引字体的大小
     *
     * @param size
     */
    public void setTextSize(int size) {
        textSize = size;
        paint.setTextSize(textSize);
        invalidate();
    }

    /**
     * 设置索引字体的颜色
     *
     * @param color
     */
    public void setTextColor(int color) {
        textColor = color;
        paint.setTextSize(textColor);
        invalidate();
    }
  • 绘制文字

绘制文字之前需要计算每个字母所在的位置,代码如下:

    /**
     * 绘制字母
     *
     * @param canvas:
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < indexArr.length; i++) {
            float x = width / 2;
            float y = cellHeight / 2 + getTextHeight(indexArr[i]) / 2 + i * cellHeight;
            // 判断触摸的和正在绘制的是否是同一个字母
            paint.setColor(lastIndex == i ? Color.DKGRAY : textColor);
            canvas.drawText(indexArr[i], x, y, paint);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = getMeasuredWidth();
        // 计算格子的高度
        cellHeight = getMeasuredHeight() * 1f / indexArr.length;
    }

    /**
     * 获取文本的高度
     *
     * @return :
     */
    private int getTextHeight(String text) {
        Rect bounds = new Rect();
        // 只要下面的方法功能一执行,则bound就有值了
        paint.getTextBounds(text, 0, text.length(), bounds);
        return bounds.height();
    }
  • 回调方法的调用
    在点击相应字母时,回调该方法。这样使用者可以根据返回值跳转到相应的条目,代码如下:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                // 获取当前触摸字母索引
                int index = (int) (event.getY() / cellHeight);
                // 增强代码的健壮性
                if (index >= 0 && index < indexArr.length) {
                    // 如果当前触摸的和上一次触摸的不是同一个则打印
                    if (index != lastIndex) {
                        Log.i("tag", indexArr[index]);
                        if (listener != null) {
                            listener.onLetterChange(indexArr[index]);
                        }
                    }
                }
                lastIndex = index;
                break;
            case MotionEvent.ACTION_UP:
                // 抬起的时候重置lastIndex
                lastIndex = -1;
                break;
        }
        // 重绘
        postInvalidate();
        return true;
    }

    private OnLetterChangeListener listener;

    public void setOnLetterChangeListener(OnLetterChangeListener listener) {
        this.listener = listener;
    }

    /**
     * 回调接口
     */
    public interface OnLetterChangeListener {
        void onLetterChange(String letter);
    }
  • 根据相应的回调返回值跳转到相应的条目

有的小伙伴发现了这里集合的排序有问题(但是这些不是重点啦 ^_^)。实现代码如下:

    private void initView() {
        listview = (ListView) findViewById(R.id.listview);
        currentWord = (TextView) findViewById(R.id.currentWord);

        quickIndexBar = (QuickIndexBar) findViewById(R.id.quickIndexBar);
        quickIndexBar.setOnLetterChangeListener(new QuickIndexBar.OnLetterChangeListener() {
            @Override
            public void onLetterChange(String letter) {
                //根据触摸的字母去集合中找首字母和letter相同的条目,然后将条目置顶
                for (int i = 0; i < friends.size(); i++) {
                    String word = PinYinUtil.getPinYin(friends.get(i)).charAt(0) + "";
                    if (word.equals(letter)) {
                        //说明当前的i就是需要的,则直接置顶
                        listview.setSelection(i);
                        //由于只需要找到第一个就行了,所以要中断
                        quickIndexBar.showCurrentWord(currentWord, letter);
                        break;
                    }
                }
            }
        });
    }

关于快速索引的实现就到这里,希望这篇博客能够为小伙伴提供一些帮助。这个控件功能比较简单。如果小伙伴们有什么好的想法,可以告诉道长。

源码下载

UtilDemo


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Qt中自定义Model/View模型,您可以继承QAbstractItemModel类,并实现其相关的虚拟函数来定义自己的数据模型。 下面是一个简单的示例,展示如何自定义一个简单的树形模型(Tree Model): ```cpp #include <QAbstractItemModel> #include <QModelIndex> #include <QVariant> class MyTreeModel : public QAbstractItemModel { public: explicit MyTreeModel(QObject *parent = nullptr); ~MyTreeModel(); // 重写父类的虚拟函数 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; MyTreeModel::MyTreeModel(QObject *parent) : QAbstractItemModel(parent) { // 在构造函数中初始化数据模型 } MyTreeModel::~MyTreeModel() { // 在析构函数中清理数据 } QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent) const { // 返回指定行、列和父索引的模型索引 } QModelIndex MyTreeModel::parent(const QModelIndex &child) const { // 返回指定子索引的父索引 } int MyTreeModel::rowCount(const QModelIndex &parent) const { // 返回指定父索引的子项数目 } int MyTreeModel::columnCount(const QModelIndex &parent) const { // 返回模型的列数 } QVariant MyTreeModel::data(const QModelIndex &index, int role) const { // 返回指定模型索引和角色的数据 } ``` 在这个示例中,我们自定义了一个名为MyTreeModel的树形模型,继承自QAbstractItemModel类,并重写了QAbstractItemModel的一些虚拟函数。在这些函数中,您可以根据自己的数据结构和需求来实现相应的逻辑。 要使用自定义的模型,您可以在视图中设置该模型,如: ```cpp MyTreeModel model; QTreeView view; view.setModel(&model); ``` 当您设置了自定义的模型后,视图将使用模型提供的数据来显示和管理数据。 当然,这只是一个简单的示例。在实际应用中,您可能需要更复杂的数据结构和逻辑来实现您的自定义模型。但是通过继承QAbstractItemModel类,并实现其虚拟函数,您可以很好地控制自己的数据模型和视图之间的交互。 希望这个示例能够帮助您开始自定义Model/View模型的开发!如果您有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值