自定义View实现通讯录和索引联动,如丝般顺滑

Android简单使用retrofit实现左右联动 效果图 所需依赖 依赖较多 implementation ‘com.jakewharton:butterknife:10.2.1’//Butterknife配置 annotationProcessor ‘com.jakewharton:butterknife-compiler:10.2.1’//Butterknife注解处理器 implementation ‘com.github.bumptec... 阅读详情

在这里插入图片描述

1,右边索引导航我自定义一个View:WordsNavigator.java

package com.txhl.testapp.cus;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.txhl.testapp.R;
import com.txhl.testapp.act.MailListActivity;
import com.txhl.testapp.listener.OnWordsChangeListener;
import com.txhl.testapp.utils.ScreenUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by chen.yingjie on 2019/4/29
 * description
 */
public class WordsNavigator extends View {
    private int mRealWidth;
    private int mRealHeight;
    private int mWidth;// 画布宽
    private int mHeight;// 画布高
    private int mEachHeight;// 每个字母平均分配到的占位高,宽和画布一样。
    private int mTouchIndex = 0;

    private Paint wordsPaint;// 字母画笔
    private Paint selectedPaint;// 背景圈画笔
    private Rect mRect;
    private OnWordsChangeListener onShowLetterListener;

    private int colorTrans;
    private int colorNormal;
    private int colorChecked;
    private int colorCheckedBg;

    public void setOnShowLetterListener(OnWordsChangeListener onShowLetterListener) {
        this.onShowLetterListener = onShowLetterListener;
    }

    private List<String> letterLists;

    public WordsNavigator(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        letterLists = new ArrayList<>();

        if (getContext() instanceof MailListActivity) {
            MailListActivity activity = (MailListActivity) getContext();
            activity.setOnRecyclerViewScrollListener(new MailListActivity.OnRecyclerViewScrollListener() {
                @Override
                public void onScroll(String firstWords) {
                    int wordsIndex = letterLists.indexOf(firstWords);
                    mTouchIndex = wordsIndex;
                    invalidate();
                }
            });
        }

        colorTrans = getContext().getResources().getColor(R.color.transparent);
        colorNormal = getContext().getResources().getColor(R.color.black);
        colorChecked = getContext().getResources().getColor(R.color.white);
        colorCheckedBg = getContext().getResources().getColor(R.color.red);

        mRect = new Rect();
        wordsPaint = new Paint();
        wordsPaint.setStrokeWidth(0);
        wordsPaint.setAntiAlias(true);
        wordsPaint.setTextSize(ScreenUtils.dip2px(getContext(), 10));

        selectedPaint = new Paint();
        selectedPaint.setStrokeWidth(0);
        wordsPaint.setAntiAlias(true);
        selectedPaint.setStyle(Paint.Style.FILL);
    }

    public void setLetters(List<String> letterLists) {
        this.letterLists = letterLists;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        switch (widthMode) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.EXACTLY:
                mRealWidth = widthSize;
                break;
        }
        switch (heightMode) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.EXACTLY:
                mRealHeight = heightSize;
                break;
        }
        setMeasuredDimension(mRealWidth, mRealHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(colorTrans);
        mWidth = canvas.getWidth();
        mHeight = canvas.getHeight();
        mEachHeight = mHeight / letterLists.size();

        for (int i = 0; i < letterLists.size(); i++) {
            final String _latter = letterLists.get(i);
            wordsPaint.getTextBounds(_latter, 0, 1, mRect);
            final int letterWidth = mRect.width();
            final int letterHeight = mRect.height();

            if (mTouchIndex == i) {
                wordsPaint.setColor(colorChecked);
                selectedPaint.setColor(colorCheckedBg);
            } else {
                wordsPaint.setColor(colorNormal);
                selectedPaint.setColor(colorTrans);
            }
            // 画选中背景圈,x:画笔宽的一半,即画布水平终点;y:每个字母高乘个数减去字母高的一半;radius:半径为画布宽的三分之一。
            canvas.drawCircle(mWidth / 2, mEachHeight * (i + 1) - letterHeight / 2, mWidth / 3, selectedPaint);
            // 画字母,x,y分别为字母的左上角起点,背景圈的圆心取决于字母的起点。
            canvas.drawText(_latter, mWidth / 2 - letterWidth / 2, (i + 1) * mEachHeight, wordsPaint);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                refreshLetterIndex(y);
                break;
            case MotionEvent.ACTION_MOVE:
                refreshLetterIndex(y);
                break;
        }
        return true;
    }

    private void refreshLetterIndex(int y) {
        //y坐标 / 每个字母高度 = 当前字母下标
        int index = y / mEachHeight;
        if (index > letterLists.size() || index < 0) return;
        if (index != mTouchIndex) {
            mTouchIndex = index;
            //回调选中的字母
            if (onShowLetterListener != null) {
                onShowLetterListener.wordsChange(letterLists.get(mTouchIndex));
            }
            invalidate();
        }
    }
}


这里定义了一个接口为了在手指滑动索引的时候通知activity滚动item。

public interface OnWordsChangeListener {
    void wordsChange(String words);
}

2,用RecyclerView来显示列表数据,给它设置滚动监听,当滚动列表的时候要让索引跟着重绘,这里通过LinearLayoutManager找到第一个可见item的position,查出对于的首字母,通过OnRecyclerViewScrollListener接口给到WordsNavigator,让其重绘。
3,让RecyclerView滚动到指定item,用的是它的LayoutManager的startSmoothScroll方法,需要一个Scroller:

public class TopSmoothScroller extends LinearSmoothScroller {

    public TopSmoothScroller(Context context) {
        super(context);
    }
    @Override
    protected int getHorizontalSnapPreference() {
        return SNAP_TO_START;//具体见源码注释
    }
    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;//具体见源码注释
    }
}

4,activity

package com.txhl.testapp.act;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.txhl.testapp.R;
import com.txhl.testapp.cus.TopSmoothScroller;
import com.txhl.testapp.cus.WordsNavigator;
import com.txhl.testapp.cus.widget.SectionListView;
import com.txhl.testapp.listener.OnWordsChangeListener;
import com.txhl.testapp.utils.PinYinUtils;
import com.txhl.testapp.utils.SortUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MailListActivity extends AppCompatActivity {

    private final int CHANGE_WORDS = 1;

    @BindView(R.id.recyclerView)
    RecyclerView recyclerView;
    @BindView(R.id.tvHintWord)
    TextView tvHintWord;
    @BindView(R.id.wordsNavigator)
    WordsNavigator wordsNavigator;

    private String[] names = new String[]{
            "露娜", "李白", "韩信", "太乙真人", "李元芳", "阿珂", "夏侯惇", "关羽", "张飞", "刘备", "貂蝉", "吕布", "王昭君", "武则天",
            "百里守约", "百里玄策", "司马懿", "孙策", "干将莫邪", "裴擒虎", "张良", "诸葛亮", "达摩", "蒙奇", "曹操", "钟馗", "钟无艳",
            "程咬金", "米莱狄", "狄仁杰", "后羿", "大乔", "小乔", "刘邦", "杨玉环", "马可波罗", "狂铁", "苏烈", "赵云", "公孙离", "鬼谷子",
            "成吉思汗", "哪吒", "杨戬", "嬴政", "女娲", "周瑜", "弈星", "扁鹊", "甄姬墨子", "高渐离", "亚瑟", "姜子牙", "宫本武藏",
            "牛魔", "庄周", "蔡文姬", "黄忠", "鲁班七号", "铠", "妲己", "白起", "安其拉", "不知火舞", "芈月", "项羽", "刘禅", "橘右京",
            "兰陵王", "典韦", "元歌", "明世隐", "雅典娜", "娜可露露", "东皇太一", "花木兰", "孙尚香", "孙膑", "虞姬", "孙悟空", "老夫子"
    };

    private List<String> letterLists = new ArrayList<>(Arrays.asList("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 String[] newNames;
    private LinearLayoutManager linearLayoutManager;
    private TopSmoothScroller smoothScroller;
    private MailAdapter mailAdapter;
    private OnRecyclerViewScrollListener onRecyclerViewScrollListener;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case CHANGE_WORDS:
                    tvHintWord.setVisibility(View.GONE);
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mail_list);
        ButterKnife.bind(this);

        wordsNavigator.setLetters(letterLists);

        wordsNavigator.setOnShowLetterListener(new OnWordsChangeListener() {
            @Override
            public void wordsChange(String words) {
                mHandler.removeMessages(CHANGE_WORDS);
                tvHintWord.setVisibility(View.VISIBLE);
                tvHintWord.setText(words);
                mHandler.sendEmptyMessageDelayed(CHANGE_WORDS, 500);
                scrollToWords(words);
            }
        });

        newNames = SortUtils.sortChar(names);
        linearLayoutManager = new LinearLayoutManager(this);
        smoothScroller = new TopSmoothScroller(this);
        mailAdapter = new MailAdapter();
        recyclerView.setAdapter(mailAdapter);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                if (layoutManager instanceof LinearLayoutManager) {
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                    int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
                    String firstWords = PinYinUtils.getFirstWords(names[firstVisibleItemPosition]);
                    onRecyclerViewScrollListener.onScroll(firstWords);
                }
            }
        });
    }

    private void scrollToWords(String words) {
        for (int i = 0; i < newNames.length; i++) {
            String firstWrods = PinYinUtils.getFirstWords(newNames[i]);
            if (words.equals(firstWrods)) {
                // 找到列表中汉字首字母和按下的字母相同的汉字
                smoothScroller.setTargetPosition(i);
                linearLayoutManager.startSmoothScroll(smoothScroller);
                return;
            }
        }
    }

    class MailAdapter extends RecyclerView.Adapter<MailAdapter.MailHolder> {

        @NonNull
        @Override
        public MailHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            View view = View.inflate(MailListActivity.this, R.layout.item_mail_list, null);
            return new MailHolder(view);
        }

        @Override
        public void onBindViewHolder(MailHolder viewHolder, int position) {
            String lastFirstChar = "";// 上一个首汉字
            String lastFirstWords = "";// 上一个首汉字的首字母
            String firstChar = "";// 当前首汉字
            String firstWords = "";// 当前首汉字的首字母
            firstChar = newNames[position].substring(0, 1);
            firstWords = PinYinUtils.getFirstWords(firstChar);// 多音字,只取第一个字母。
            if (position > 0) {
                lastFirstChar = newNames[position - 1].substring(0, 1);
                lastFirstWords = PinYinUtils.getFirstWords(lastFirstChar);
            }
            if (firstWords.equals(lastFirstWords)) {
                viewHolder.tvWords.setVisibility(View.GONE);
            } else {
                viewHolder.tvWords.setVisibility(View.VISIBLE);
                viewHolder.tvWords.setText(firstWords);
            }
            viewHolder.tvName.setText(newNames[position]);
        }

        @Override
        public int getItemCount() {
            return newNames == null ? 0 : newNames.length;
        }

        class MailHolder extends RecyclerView.ViewHolder {
            @BindView(R.id.tvWords)
            TextView tvWords;
            @BindView(R.id.tvName)
            TextView tvName;
            @BindView(R.id.tvMsg)
            TextView tvMsg;
            @BindView(R.id.ivPhoto)
            ImageView ivPhoto;

            public MailHolder(@NonNull View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
            }
        }

    }

    public interface OnRecyclerViewScrollListener {
        void onScroll(String firstWords);
    }

    public void setOnRecyclerViewScrollListener(OnRecyclerViewScrollListener onRecyclerViewScrollListener) {
        this.onRecyclerViewScrollListener = onRecyclerViewScrollListener;
    }
}

用pinyin4j.jar来取首字母,可能有多音字,pinyinName是一个类似(比如重 pinyinName=c,z),这里只取第一个:

public static String getFirstWords(String chines) {
        StringBuffer pinyinName = new StringBuffer();
        char[] nameChar = chines.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < nameChar.length; i++) {
            if (nameChar[i] > 128) {
                try {
                    String[] strs = PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat);
                    if (strs != null) {
                        for (int j=0; j<strs.length; j++) {
                            pinyinName.append(strs[j].substring(0,1));
                            if (j != strs.length - 1) {
                                pinyinName.append(",");
                            }
                        }
                    }
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                pinyinName.append(nameChar[i]);
            }
        }
        return pinyinName.toString().substring(0, 1);
    }

排序方式有很多:

 * 按首字母排序
     * @param strs
     * @return
     */
    public static String[] sortChar(String[] strs) {
        Comparator<Object> com = Collator.getInstance(Locale.CHINA);
        List<String> list = Arrays.asList(strs);
        Collections.sort(list, com);
        return list.toArray(new String[0]);
    }

5,布局
activity_mail_list

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".act.MailListActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.txhl.testapp.cus.WordsNavigator
        android:id="@+id/wordsNavigator"
        android:layout_marginRight="12dp"
        android:layout_marginTop="100dp"
        android:layout_marginBottom="40dp"
        android:background="@color/transparent"
        android:layout_gravity="right"
        android:layout_width="30dp"
        android:layout_height="match_parent" />

    <TextView
        android:id="@+id/tvHintWord"
        android:text="a"
        android:background="@color/gray"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:layout_gravity="center"
        android:textSize="30dp"
        android:textColor="@color/red"
        android:layout_width="60dp"
        android:layout_height="80dp" />

</FrameLayout>

item_mail_list

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvWords"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="16sp"
        android:background="@color/bg_focus"
        android:text="A"
        android:textColor="@color/blue" />

    <LinearLayout
        android:padding="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/ivPhoto"
            android:layout_gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher_round" />

        <LinearLayout
            android:layout_gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tvName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text=""
                android:textColor="@color/black"
                android:textSize="18sp"/>

            <TextView
                android:id="@+id/tvMsg"
                android:layout_width="wrap_content"
                
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:maxLines="2"
                android:text="我是一个小学生,你不要欺负我,否则我会哭的!"
                android:textColor="@color/gray"
                android:textSize="13sp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

补充:测试发现字母导航器在滑动的时候不跟手,没有那种如丝般顺滑的手感,经过分析原因是,在手指滑动导航器的时候,会触发recyclerView的onScroll监听,这个监听里又不断的告诉导航器你该重绘了,但是如果我们滑动导航器,这时候是不应该再让RecyclerView告诉导航器重绘的,所以导致导航器不断的重绘导致滑动像大鼻涕一样粘稠。故我们在activity里定义一个boolean值来控制是否需要重绘导航器,而且这个值默认为true,否则第一次进页面不会绘制导航器,导致其看不见。

MailListActivity.java

private boolean needInvalidate = true;// 是否需要重绘字母导航

是手指滑动recyclerView的时候需要重绘导航器
recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                needInvalidate = true;
                return false;
            }
        });

在导航器滑动导致recyclerView滑动的时候不需要重绘导航器
wordsNavigator.setOnShowLetterListener(new OnWordsChangeListener() {
            @Override
            public void wordsChange(String words) {
                needInvalidate = false;

                mHandler.removeMessages(CHANGE_WORDS);
                tvHintWord.setVisibility(View.VISIBLE);
                tvHintWord.setText(words);
                mHandler.sendEmptyMessageDelayed(CHANGE_WORDS, 500);
                scrollToWords(words);
            }
        });

控制导航器是否重绘
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (!needInvalidate) return;
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                if (layoutManager instanceof LinearLayoutManager) {
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                    int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
                    String firstWords = PinYinUtils.getFirstWords(names[firstVisibleItemPosition]);
                    onRecyclerViewScrollListener.onScroll(firstWords);
                }
            }
        });
Flutter 只需要几步就可以完成级联索引列表(仿微信通讯录/城市索引列表) 索引列表、放微信通讯录、城市索引列表 阅读详情

相关推荐

Flutter —— 通讯录页面&索引条

Flutter —— 通讯录页面&索引条1. 通讯录页面2. 索引条 1. 通讯录页面 做完了发现页面和我的页面,接下来做通讯录页面。将_currentIndex 改成1方便开发。 发现通讯录页面还是要用到之前的_themColor,那么就将_themColor抽取出来以便公用。 创建一个文件来存放这些需要公用的常量。然后放入主题色之后,在别的地方import这个文件就能使用了。 //主题色 const Color weChatThemColor = Color.fromRGBO(220, 220,

LinShunIos的博客 1584

浅谈android中手机联系人字母索引表的实现

实际上字母索引表的效果,可以说在现在的众多APP中使用的非常流行,比如支付宝,微信中的联系人,还有购物,买票的APP中选择全国城市,切换城市的时候,这时候的城市也就是按照一个字母索引的顺序来显示,看起来是很方便的.其实这种字母索引表的效果最开始是出现在微信的联系人中.因为觉得这种效果功能在今后的项目中可以说是非常常见,可能会用的上,所以准备来波博客讲述一下实现的原理,一来方便以后自己复习,二来如果

Mikyou的专栏 1万+

阿里钉钉ios面经及参考答案

iOS 开发面试中重点阐述的操作系统包含 Unix、Linux、macOS、iOS、Android、Windows、RTOS 实时操作系统,面试加分关键点是优先侧重类 Unix 体系,因为 iOS、macOS 内核都源自这套体系,和日常 iOS 底层调试、RunLoop、内存机制强相关。记忆采用体系归类记忆法,分为原生 Unix、开源类 Unix、苹果系、移动端其他、通用桌面、实时系统六组,再搭配特征关键词记忆法,每个系统提炼 3 个核心关键词方便口述。 Unix 诞生于 1970 年,是经典分时多用户操作

大模型大数据攻城狮的专栏 232

android双联动列表,Android中ListView左右联动

效果图: 思路:1左右是两个listView,左边显示类型,右边显示类型和内容2左边控制右边左边的条目点击事件中拿到左边点击的类型,循环右边的数据,当判断左边类型和右边类型相等时,定位右边listView的位置lvTest.setSelection(i);并且return;代码:// 类型listView的条目点击事件,定位右边列表 lvType.setOnItemClickListener(ne...

weixin_32075603的博客 1632

android开发必备 手机联系人,字母索引列表,联动

1、自定义view——–字母索引列表view 2、listview数据的展示 3、查询本地联系人的相关信息,如姓名、电话号 4、6.0权限适配

android手机联系人,字母索引列表,实现联动

1、自定义view——–字母索引列表view 2、listview数据的展示 3、查询本地联系人的相关信息,如姓名、电话号 4、6.0权限适配

android通讯录字母特效,Flutter 城市/通讯录列表字母索引联动效果实现

前言在像通讯录,联系人列表,城市选择列表等数据量比较多的长列表页面中,我们经常会留意到产品设计会在页面的右侧区域提供一个竖向的字母索引列表,供用户点击选择快速定位到长列表中的指定索引位置,以便于用户快速定位到自己要筛选的数据,从而提升用户体验,今天我们就以城市列表为例,来剖析一下,这样的体验效果如果用Flutter来实现技术实现分析页面城市列表布局采用ListView嵌套ListView,其中外层...

weixin_29879697的博客 980

联系人列表跟索引条结合

这是常见的联系人列表界面,然后跟索引条结合使用,但是我这里没有把索引条的字母写成活的,不过要改成活的也很简单,有需要的自己进行修改,这是Eclipse项目。然后重点是索引条的绘制,和排序。将汉字变成拼音用到的是一个jar包

实现自己的首字母索引导航列表(四)——字母导航条与列表联动

文章目录1、修改SuspensionDecoration类,完善悬浮头效果2、修改MainActivity的布局文件,查看界面效果3、设置字母导航条的回调函数,完成联动功能   目标效果如下。 实现步骤: 修改SuspensionDecoration类,完善悬浮头效果 修改MainActivity的布局文件,查看界面效果 设置字母导航条的回调函数,完成联动功能 1、修改SuspensionD...

LmingAn的博客 1522

Android 联系人字母排序(仿微信)

这是博文配套DEMO,博文地址:http://www.cnblogs.com/scetopcsa/p/4724308.html。欢迎指导、交流。

flutter_contacts,一个flutter插件,用于检索和管理android和ios设备上的联系人。维护者:@lukasgit.zip

这个插件在https://github.com/lukasgit/flutter_contacts上得到了有效维护。

5、Flutter通讯录索引条效果实现

3.然后在ListView.builder中有个controller属性,将我们创建的_scrollController设置给它,此时的_scrollController就相当于代理。4. 接着在IndexBar构造方法调用的地方设置,来到friends_page.dart中.将悬浮检索控件的调用改为。4.来到IndexBar调用处.在回调响应方法中,设置滚动控制器的响应方法。3. 在IndexBar中与拖拽索引控件对应的回调相互联动,设置滚动偏移量。3. 接下来在IndexBar的回调中做逻辑判断处理。

Holothurian 1665

Android实现通讯录(带有拼音首字母排序的和过滤搜索

ListView实现A-Z字母排序和过滤搜索功能并且实现汉字转成拼音的功能,获取通讯录联系人和头像

【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!

在 Flutter 开发中,AzListView 是实现字母索引分类列表的理想选择。它支持 A-Z 快速跳转、悬浮分组标题、自定义 UI 和高效性能,适用于通讯录、城市选择等场景。本文将详细解析 AzListView 的核心参数和实战示例,助你轻松实现流畅的索引列表。

小韩的博客 875

银河麒麟系统V10 Server SP1 安装Oracle 19c

•oracle-database-preinstall-19c 这个工具会自动配置系统参数,创建 oracle 用户和组。比较方便。•••••银河麒麟高级服务器操作系统V10 - 国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站。

fuguimashu的博客 4321

首个基于LBS方案的3D传感器,VoxelSensor解析

该传感器将结合OQmented的Lissajous模式的MEMS扫描镜,与每帧逐行工作的光栅扫描相比,Lissajous轨迹扫描速度更快,并且能耗很低,它可以更快地捕获完整的场景和快速移动,并且需要更少的数据处理,可满足AR/VR对于低延迟、高效率的需求。简单来讲,Switching Pixels的区别是专注于识别激光事件,性能比普通事件相机更好,但也可以使用事件相机的算法。在具体应用中,它需要在X和Y方向发射一个或多个脉冲光束,以在X和Y轴上定位,而Z轴,也就是深度,则是通过计算光返回的时间来测量。

qingtingwang的博客 575

flutter 通讯录

flutter 通讯录 工程仓库 国内的话需要梯子打开 https://www.dropbox.com/sh/u9s38957amql3yc/AABc3ncZd8L2gMtMAy83FKL0a?dl=0 如果不能下载的话请私信我 样式长这样 需求分析: 1、滑动会影响到右边的导航栏。 2、右边的导航栏具有点击和拖动的手势,位置点到的位置会影响到左边的list。 3、当list的导航栏的索引没有对应的list会选择最近的list。 4、当最后的list不占满的话,选倒数第二个list,若倒数第二个也不占满

SCDNBB的博客 1087

自制电吉他效果器 DIY PCB(二)电路图分析

自制DIY电吉他效果器 PCB(二)电路图分析 要自制一款效果器,看懂电路图是必须的,即使你已经有了完整的设计电路图,在电路的绘制已久焊接的时候电路知识都会用到。 在效果器电路中,比较简单的是过载失真类效果器的电路。混响延迟等与声相有关的由于电路比较复杂本人也没有尝试过。其他的例如EQ、哇音等等效果器复杂程度依设计各不相同,在能做出过载失真类效果器后,可以自行尝试。 我这里主要为大家介绍失真效果器...

qq_41342525的博客 2万+

jquery获取元素索引值index()

jquery获取元素索引值index() 作者: 梅川内酷 于 13-04-26 热度:384 jquery获取元素索引值index()方法: jquery的index()方法 搜索匹配的元素,并返回相应元素的索引值,从0开始计数。 如果不给 .index() 方法传递参数,那么返回值就是这个jQuery对象集合中第一个元素相对于其同辈元素的位置。 如果参数是一组DOM元素或者jQ

wh1374256765的专栏 1621

MacOS上VScode配置PlatformIO Core的疑难杂症与提速实战

本文详细解析了在MacOS上使用VScode配置PlatformIO Core时遇到的安装卡死、新建项目缓慢等常见问题,并提供了官方脚本安装、网络优化、命令行预下载等实用解决方案。通过环境变量配置和镜像源加速,大幅提升开发效率,特别适合ESP32等嵌入式开发场景。

weixin_42567046的博客 220

注意力机制篇 | YOLOv8改进之在C2f模块添加EMA注意力机制(附2种改进方法)

EMA(Exponential Moving Average)注意力机制是一种用于增强模型性能的注意力机制,它通过对模型的特征图进行加权平均来提取更有用的特征信息。具体来说,EMA注意力机制通过引入一个权重因子来调整特征图中每个位置的重要性,从而使模型能够更好地关注重要的特征。🌈

小哥谈 4180

MySQL索引之联合索引

目录联合索引联合索引的存储结构建表 联合索引 在平时开发中,我们最常见的是聚集索引,但在我们需要多条件查询的时候,就不得不建立联合索引,来提高我们的查询效率 联合索引:也称复合索引,就是建立在多个字段上的索引。联合索引的数据结构依然是 B+ Tree 一颗 B+ Tree 只能根据一个值来构建,所以联合索引使用 最左 的字段来构建 B+ Tree 联合索引的存储结构 建表 CREATE TABLE `t1` ( `a` int(11) NOT NULL AUTO_INCREMENT, `b`

桐花思雨 2万+

数字电视中的 Closed Caption (CC) 字幕标准协议

本文将详细介绍 DVB中的 CC 字幕标准协议,包括数据结构格式、字段定义、字段占用比特数以及字段用途。此外,本文还将举例说明如何从 TS 传输流中解析 CC 字幕信息,并组织打包到电视节目中进行显示。

以实战经验介绍行业知识 670

python三级联动函数式_三级联动,jquery获取元素索引值index()

jquery获取元素索引值index()方法:jquery的index()方法 搜索匹配的元素,并返回相应元素的索引值,从0开始计数。如果不给 .index() 方法传递参数,那么返回值就是这个jQuery对象集合中第一个元素相对于其同辈元素的位置。如果参数是一组DOM元素或者jQuery对象,那么返回值就是传递的元素相对于原先集合的位置。如果参数是一个选择器,那么返回值就是原先元素相对于选择器匹...

weixin_32280985的博客 132

03. 线性规划与混合整数规划 (LP/MIP)

本文介绍了OR-Tools中的线性规划(LP)和混合整数规划(MIP)求解器。主要内容包括:1) LP/MIP的基本概念和数学形式,对比了LP、MIP、ILP和BIP的区别;2) OR-Tools支持的求解器类型,如GLOP、SCIP等,以及它们各自的特点和适用场景;3) 详细展示了如何使用GLOP求解器进行线性规划求解,包括变量定义、约束添加和目标函数设置;4) 介绍了SCIP求解器的配置和使用方法;5) 讨论了变量类型、约束类型、多目标优化等进阶功能。文章通过Java代码示例演示了如何实现一个完整的优化

智慧是做事用的 156

27、振动信号实时并行处理与风力发电机模型鲁棒控制设计

本文探讨了振动信号实时并行处理与风力发电机模型鲁棒控制设计两大核心技术。在振动信号处理方面,提出基于FPGA、RT和PC三级架构的并行采集与处理系统,实现高效、实时的振动分析,适用于电机状态监测与预测性维护。在风力发电控制方面,研究风力农场布局优化(WFLO)问题,比较九种多目标元启发式算法性能,并提出基于扰动观察法的最大功率点跟踪(MPPT)控制策略,提升发电效率与系统鲁棒性。研究成果为工业设备智能监控与可再生能源优化提供了有效技术路径。

sunny的博客 72
上一篇: 沉浸式状态栏,包括折叠布局的顶部显示到状态栏
下一篇: 关于MVP的学习总结
陳英傑
博客等级 码龄11年 19粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值