ListView实现字母索引

参考网址:http://blog.csdn.net/appte/article/details/10306341 非常感谢~~!

ListView A~Z快速索引这种效果在通信录和城市列表中经常看到,方便用户查找,是一种增加用户体验的好方法。

实现效果:

实现步骤:

1.自定义一个名叫SlideBar 的View。

2.在布局文件中加入这个自定义的View。

3. 在Activity中处理监听事件。

话不多说,上代码

自定义View:SlideBar.java

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 自定义的View,实现ListView A~Z快速索引效果
 *
 * @author shen
 *
 */
public class SlideBar extends View {
    private Paint paint = new Paint();
    private OnTouchLetterChangeListenner listenner;
    // 是否画出背景
    private boolean showBg = false;
    // 选中的项
    private int choose = -1;
    // 准备好的A~Z的字母数组
    public static String[] letters = { "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" };  

    // 构造方法
    public SlideBar(Context context) {
        super(context);
    }  

    public SlideBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }  

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 获取宽和高
        int width = getWidth();
        int height = getHeight();
        // 每个字母的高度
        int singleHeight = height / letters.length;
        if (showBg) {
            // 画出背景
            canvas.drawColor(Color.parseColor("#ffebedf2"));
        }
        // 画字母
        for (int i = 0; i < letters.length; i++) {
            paint.setColor(Color.BLACK);
            // 设置字体格式
            paint.setAntiAlias(true);
            paint.setTextSize(DensityUtil.dipToPixels(getContext(), 12));//使用dipToPixels()方法动态地将dp转换成px以适应不同的屏幕
            // 要画的字母的x,y坐标
            float posX = width / 2 - paint.measureText(letters[i]) / 2;
            float posY = i * singleHeight + singleHeight;
            // 画出字母
            canvas.drawText(letters[i], posX, posY, paint);
            // 重新设置画笔
            paint.reset();
        }
    }  

    /**
     * 处理SlideBar的状态
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final float y = event.getY();
        // 算出点击的字母的索引
        final int index = (int) (y / getHeight() * letters.length);
        // 保存上次点击的字母的索引到oldChoose
        final int oldChoose = choose;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            showBg = true;
            if (oldChoose != index && listenner != null && index >= 0
                    && index < letters.length) {
                choose = index;
                listenner.onTouchLetterChange(showBg, letters[index]);
                invalidate();
            }
            break;  

        case MotionEvent.ACTION_MOVE:
            if (oldChoose != index && listenner != null && index >= 0
                    && index < letters.length) {
                choose = index;
                listenner.onTouchLetterChange(showBg, letters[index]);
                invalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            showBg = false;
            choose = -1;
            if (listenner != null) {
                if (index <= 0) {
                    listenner.onTouchLetterChange(showBg, "A");
                } else if (index > 0 && index < letters.length) {
                    listenner.onTouchLetterChange(showBg, letters[index]);
                } else if (index >= letters.length) {
                    listenner.onTouchLetterChange(showBg, "Z");
                }
            }
            invalidate();
            break;
        }
        return true;
    }  

    /**
     * 回调方法,注册监听器
     *
     * @param listenner
     */
    public void setOnTouchLetterChangeListenner(
            OnTouchLetterChangeListenner listenner) {
        this.listenner = listenner;
    }  

    /**
     * SlideBar 的监听器接口
     *
     * @author shen
     *
     */
    public interface OnTouchLetterChangeListenner {  

        void onTouchLetterChange(boolean isTouched, String s);
    }  

}

主类 MainActivity.java

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;

import com.zte.server_express_company.SlideBar.OnTouchLetterChangeListenner;

public class MainActivity extends Activity {

    private SlideBar mSlideBar;
    private ListView mListView;
    private TextView float_letter;
    private LinearLayout layout1;
    private LinearLayout layout2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.select_express);
        init();
    }

    private void init() {
        layout1 = (LinearLayout) findViewById(R.id.LinearLayout1);
        layout2 = (LinearLayout) findViewById(R.id.LinearLayout2);
        int[] imgId1 = new int[]{R.drawable.yto_express,R.drawable.zto_express,R.drawable.yd_express,R.drawable.post_express,R.drawable.e_ms_express};
        int[] imgId2 = new int[]{R.drawable.zjs_express,R.drawable.sto_express,R.drawable.sf_express,R.drawable.tt_express,R.drawable.best_express};
        String[] logoStr1 = new String[]{"圆通","中通","韵达","邮政","EMS"};
        String[] logoStr2 = new String[]{"宅急送","申通","顺丰","天天","百世汇通"};
        int[] resources = new int[] { R.layout.select_express_item1,
                R.layout.select_express_item2 };
        final String[] strs = new String[] { "A", "安能物流", "B", "百世汇通", "百世快运",
                "C", "菜鸟仓-圆通速递", "菜鸟仓-中通快递", "长城快递", "E", "邮政快递EMS", "F",
                "凤凰快递", "飞康达快递", "G", "国通快递", "广深速递", "H", "华宇物流", "华运物流", "J",
                "京通物流", "佳吉快运", "L", "龙邦快递", "N", "能达快递", "Q", "全一快运", "全封快递",
                "驱达国际快递", "S", "顺丰速递", "T", "天天快递", "X", "星晨急便", "Y", "韵达快递",
                "优速物流", "Z", "中通快递", "宅急送快运", "中邮物流" };

        mSlideBar = (SlideBar) findViewById(R.id.slideBar);
        mListView = (ListView) findViewById(R.id.list);
        float_letter = (TextView) findViewById(R.id.float_letter);
        //动态创建两个用于展示快递公司的布局
        createSettledView(0, logoStr1, imgId1);
        createSettledView(1, logoStr2, imgId2);

        ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
        for (int i = 0; i < strs.length; i++) {
            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put("tv", strs[i]);
            listItem.add(map);
        }
        MultiLayoutSimpleAdapter myAdapter = new MultiLayoutSimpleAdapter(
                MainActivity.this, listItem, resources, new String[] { "tv" },
                new int[] { R.id.tv });

        mListView.setAdapter(myAdapter);

        mSlideBar
                .setOnTouchLetterChangeListenner(new OnTouchLetterChangeListenner() {

                    @Override
                    public void onTouchLetterChange(boolean isTouched, String s) {
                        float_letter.setText(s);
                        if (isTouched) {
                            float_letter.setVisibility(View.VISIBLE);
                        } else {
                            float_letter.postDelayed(new Runnable() {

                                @Override
                                public void run() {
                                    float_letter.setVisibility(View.GONE);
                                }
                            }, 100);
                        }
                        int position = 0;
                        // 尝试用字符串数组定位
                        for (int i = 0; i < strs.length; i++) {
                            if (strs[i] == s) {
                                position = i;
                                break;
                            }
                            if (strs.length - 1 == i) {
                                return;
                            }
                        }
                        mListView.setSelection(position);
                    }
                });
    }

    //创建固定的LinearLayout布局
        private void createSettledView(int ViewId, String[] txtStrs, int[] imgId) {
            for (int i = 0; i < imgId.length; i++){
                //开始创建布局,用于包裹图片+文字
                //首先获取屏幕宽度
                Display display = getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);
                int width = size.x / 5;
                //设置布局参数
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, LayoutParams.WRAP_CONTENT);
                params.setMargins(0, 8, 0, 8);
                //开始创建布局
                LinearLayout view = new LinearLayout(this);
                //绑定布局参数
                view.setLayoutParams(params);
                view.setOrientation(LinearLayout.VERTICAL);//设置布局方向

                //设置view中ImageView和TextView的布局参数
                ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                ImageView imageView = new ImageView(this);
                TextView textView = new TextView(this);
                //绑定子布局的布局参数
                imageView.setLayoutParams(contentParams);
                textView.setLayoutParams(contentParams);
                //向ImageView和TextView中添加内容
                imageView.setImageDrawable(getResources().getDrawable(imgId[i]));
                textView.setText(txtStrs[i]);
                //设置ImageView和TextView控件各自的参数
                imageView.setPadding(0, 8, 0, 8);
                textView.setTextSize(14);
                textView.setPadding(0, 0, 0, 10);
                textView.setGravity(Gravity.CENTER);
                //将ImageView和TextView添加到布局中
                view.addView(imageView);
                view.addView(textView);
                switch(ViewId){
                case 0:
                    layout1.addView(view);
                    break;
                case 1:
                    layout2.addView(view);
                    break;
                }
            }
        }
}

MainActivity.java

屏幕像素转换类 DensityUtil.java

import java.util.logging.Logger;

import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;

/**
* @ClassName: DensityUtil
* @Description: 屏幕像素转换类
* @author soyoungboy
* @date 2014-6-17 下午4:23:26
*/
public class DensityUtil {
    private static DensityUtil densityUtil = null;
    private static final String TAG = DensityUtil.class.getSimpleName();

    // 当前屏幕的densityDpi
    private static float dmDensityDpi = 0.0f;

    private static DisplayMetrics dm;

    private static float scale = 0.0f;

    private DensityUtil(Context context) {
        // 获取当前屏幕
        dm = new DisplayMetrics();
        // 返回当前资源对象的DispatchMetrics信息??
        //dm = context.getApplicationContext().getResources().getDisplayMetrics();
        if (context instanceof Activity) {
            ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
        }else {
            Logger.getLogger(TAG, "your context must is instance of Activity!");//TODO is it true?
            return;
        }
        // 设置DensityDpi
        setDmDensityDpi(dm.densityDpi);
        // 密度因子
        //scale = getDmDensityDpi() / 160;// 等于 scale=dm.density;
        scale=dm.density;
    }

    public static DensityUtil getInstance(Context context) {
        if (densityUtil == null) {
            densityUtil = new DensityUtil(context);
        }
        return densityUtil;
    }

    /**
     * @Description: 屏幕分辨率宽
     * @return
     */
    public int getWindowWidth(){
        if (dm != null) {
            return dm.widthPixels;
        }
        return 0;
    }

    /**
     * @Description: 屏幕分辩类高
     * @return
     */
    public int getWindowHeight(){
        if (dm != null) {
            return dm.heightPixels;
        }
        return 0;
    }

    /**
     * @Description: 屏幕的ppi
     * @return:float
     */
    public float getDmDensityDpi() {
        return dmDensityDpi;
    }

    public void setDmDensityDpi(float dmDensityDpi) {
        DensityUtil.dmDensityDpi = dmDensityDpi;
    }

    public int dip2px(float dipValue) {

        return (int)(dipValue * scale + 0.5f);

    }

    public int px2dip(float pxValue) {
        return (int)(pxValue / scale + 0.5f);
    }

    /**
     * @Description: dip转像??
     * @param context
     * @param dip
     * @return:int
     */
    public static int dipToPixels(Context context, int dip) {
        final float SCALE = context.getResources().getDisplayMetrics().density;
        float valueDips = dip;
        int valuePixels = (int)(valueDips * SCALE + 0.5f);
        return valuePixels;
    }

    /**
     * @Description: 像素转dip
     * @param context
     * @param Pixels
     * @return:float
     */
    public static float pixelsToDip(Context context, int Pixels) {
        final float SCALE = context.getResources().getDisplayMetrics().density;
        float dips = Pixels / SCALE;
        return dips;
    }
}

DensityUtil.java

自定义适配器 MultiLayoutSimpleAdapter.java

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zte.server_express_company;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Checkable;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;

/**
 * 这是一个简单的适配器,可以将静态数据映射到XML文件中定义好的视图. 你可以将 Maps 的 ArrayList 指定为用于列表的数据.ArrayList
 * 中的每一项对应列表中的一行. Maps 中包含用于一行的数据.你也可以指定 XML 文件,其中定义了用于显示行的视图,通过 Map
 * 的关键字映射到指定的视图. 绑定数据到视图分两个阶段.首先,如果
 * {@link android.widget.SimpleAdapter.ViewBinder} 是有效的, 则调用
 * {@link ViewBinder#setViewValue(android.view.View, Object, String)} 方法.
 * 如果返回值为真,则执行绑定.如果返回值为假,则按以下顺序绑定视图:
 * <ul>
 * <li>实现了 Checkable 的视图(例如 CheckBox),期望绑定值是布尔类型.
 * <li>TextView,期望绑定值是字符串类型,通过调用 {@link #setViewText(TextView, String)} 绑定.
 * <li>ImageView,期望绑定值是资源 ID 或者一个字符串,通过调用 {@link #setViewImage(ImageView, int)}
 * 或 {@link #setViewImage(ImageView, String)}绑定.
 * </ul>
 * 如果没有合适的绑定发生,将会抛出 {@link IllegalStateException} 异常.
 *
 * @author translate by 德罗德
 * @author convert by cnmahj
 */
public class MultiLayoutSimpleAdapter extends BaseAdapter implements Filterable {
    private int[] mTo;
    private String[] mFrom;
    private ViewBinder mViewBinder;

    protected List<? extends Map<String, ?>> mData;

    private int[] mResources;
    private int[] mDropDownResources;
    private LayoutInflater mInflater;

    private SimpleFilter mFilter;
    private ArrayList<Map<String, ?>> mUnfilteredData;

    /**
     * 构造函数
     *
     * @param context
     *            与 SimpleAdapter 关联的运行着的视图的上下文.
     * @param data
     *            Map 的列表.列表中的每个条目对应一行.Maps 中包含所有在 from 中指定的数据.
     * @param resource
     *            定义列表项目的视图布局的资源 ID.布局文件至少应该包含在 to 中定义了的名称.
     * @param from
     *            与 Map 中的项目建立关联的列名的列表.
     * @param to
     *            用于显示 from 中参数中的列的视图列表.这些视图应该都是 TextView 类型的. 该列表中的第 N 个视图显示从参数
     *            from 中的第 N 列获取的值.
     */
    public MultiLayoutSimpleAdapter(Context context,
            List<? extends Map<String, ?>> data, int[] resources,
            String[] from, int[] to) {
        mData = data;
        mResources = mDropDownResources = resources;
        mFrom = from;
        mTo = to;
        mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getViewTypeCount() {
        return mResources.length;
    }

    /**
     * @see android.widget.Adapter#getCount()
     */
    public int getCount() {
        return mData.size();
    }

    /**
     * @see android.widget.Adapter#getItem(int)
     */
    public Object getItem(int position) {
        return mData.get(position);
    }

    /**
     * @see android.widget.Adapter#getItemId(int)
     */
    public long getItemId(int position) {
        return position;
    }

    /**
     * @see android.widget.Adapter#getView(int, View, ViewGroup)
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        int type = 0;

        /**
         * 判断当前Viwe将要放置的内容是字母还是中文,如果是字母则置 type = 1 ,使用item2布局;如果是中文则置 type = 0,使用item1布局
         */
        final Map<String, ?> myDataSet = mData.get(position);
        final String[] myFrom = mFrom;
        final Object data = myDataSet.get(myFrom[0]);
        String text = data == null ? "" : data.toString();

        if ((text + "").getBytes().length == 1) {
            type = 1;// 英文
            System.out.print("type=" + type + " 执行完毕!");
        }

        return createViewFromResource(position, convertView, parent,
                mResources[type]);
    }

    private View createViewFromResource(int position, View convertView,
            ViewGroup parent, int resource) {
        View v;
        // if (convertView == null) {
        // v = mInflater.inflate(resource, parent, false);
        // } else {
        // v = convertView;
        // }
        v = mInflater.inflate(resource, parent, false);
        bindView(position, v);

        return v;
    }

    /**
     * <p>
     * 设置创建下拉列表视图的布局资源 ID.
     * </p>
     *
     * @param resource
     *            定义下拉列表视图的布局资源 ID.
     * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
     */
    public void setDropDownViewResource(int[] resources) {
        this.mDropDownResources = resources;
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent,
                mResources[getItemViewType(position)]);
    }

    private void bindView(int position, View view) {
        final Map<String, ?> dataSet = mData.get(position);
        if (dataSet == null) {
            return;
        }

        final ViewBinder binder = mViewBinder;
        final String[] from = mFrom;
        final int[] to = mTo;
        final int count = to.length;

        for (int i = 0; i < count; i++) {
            final View v = view.findViewById(to[i]);
            if (v != null) {
                final Object data = dataSet.get(from[i]);
                String text = data == null ? "" : data.toString();
                if (text == null) {
                    text = "";
                }

                boolean bound = false;
                if (binder != null) {
                    bound = binder.setViewValue(v, data, text);
                }

                if (!bound) {
                    if (v instanceof Checkable) {
                        if (data instanceof Boolean) {
                            ((Checkable) v).setChecked((Boolean) data);
                        } else if (v instanceof TextView) {
                            // Note: keep the instanceof TextView check at the
                            // bottom of these
                            // ifs since a lot of views are TextViews (e.g.
                            // CheckBoxes).
                            setViewText((TextView) v, text);
                        } else {
                            throw new IllegalStateException(v.getClass()
                                    .getName()
                                    + " should be bound to a Boolean, not a "
                                    + (data == null ? "<unknown type>"
                                            : data.getClass()));
                        }
                    } else if (v instanceof TextView) {
                        // Note: keep the instanceof TextView check at the
                        // bottom of these
                        // ifs since a lot of views are TextViews (e.g.
                        // CheckBoxes).
                        setViewText((TextView) v, text);
                    } else if (v instanceof ImageView) {
                        if (data instanceof Integer) {
                            setViewImage((ImageView) v, (Integer) data);
                        } else {
                            setViewImage((ImageView) v, text);
                        }
                    } else if (v instanceof Spinner) {
                        if (data instanceof Integer) {
                            ((Spinner) v).setSelection((Integer) data);
                        } else {
                            continue;
                        }
                    } else {
                        throw new IllegalStateException(
                                v.getClass().getName()
                                        + " is not a "
                                        + " view that can be bounds by this SimpleAdapter");
                    }
                }
            }
        }
    }

    /**
     * 返回用于将数据绑定到视图的 {@link ViewBinder}.
     *
     * @return ViewBinder,如果绑定器不存在则返回 null.
     *
     * @see #setViewBinder(android.widget.SimpleAdapter.ViewBinder)
     */
    public ViewBinder getViewBinder() {
        return mViewBinder;
    }

    /**
     * 设置用于将数据绑定到视图的绑定器.
     *
     * @param viewBinder
     *            用于将数据绑定到视图的绑定器.设置为 null,可以删除既存的绑定器.
     *
     * @see #getViewBinder()
     */
    public void setViewBinder(ViewBinder viewBinder) {
        mViewBinder = viewBinder;
    }

    /**
     * 由 bindView() 方法调用,用于为 ImageView 设置图像.只在 ViewBinder 不存在或者既存的 ViewBinder
     * 无法处理 ImageView 的绑定时才调用.
     *
     * 如果调用 {@link #setViewImage(ImageView, String)} 方法时提供的 value
     * 参数可以转换为整数类型,则会自动调用本方法.
     *
     * @param v
     *            接收图像的 ImageView.
     * @param value
     *            从数据集获取到的值
     *
     * @see #setViewImage(ImageView, String)
     */
    public void setViewImage(ImageView v, int value) {
        v.setImageResource(value);
    }

    /**
     * 由 bindView() 方法调用,用于为 ImageView 设置图像.只在 ViewBinder 不存在或者既存的 ViewBinder
     * 无法处理 ImageView 的绑定时才调用.
     *
     * 本方法默认将 value 作为图像资源 ID 来对待;当 value 无法变换为整数类型时,才作为图像的 Uri 来使用.
     *
     * @param v
     *            接收图像的 ImageView.
     * @param value
     *            从数据集获取到的值.
     *
     * @see #setViewImage(ImageView, int)
     */
    public void setViewImage(ImageView v, String value) {
        try {
            v.setImageResource(Integer.parseInt(value));
        } catch (NumberFormatException nfe) {
            v.setImageURI(Uri.parse(value));
        }
    }

    /**
     * 由 bindView() 方法调用,用于为 TextView 设置文本.只在 ViewBinder 不存在或者既存的 ViewBinder
     * 无法处理 TextView 的绑定时才调用.
     *
     * @param v
     *            接收文本的 TextView.
     * @param text
     *            设置到 TextView 的文本.
     */
    public void setViewText(TextView v, String text) {
        v.setText(text);
    }

    public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new SimpleFilter();
        }
        return mFilter;
    }

    /**
     * 该类用于 SimpleAdapter 的外部客户将适配器的值绑定到视图.
     *
     * 你可以使用此类将 SimpleAdapter 不支持的值绑定到视图,或者改变 SimpleAdapter 支持的视图的绑定方式.
     *
     * @see MultiLayoutSimpleAdapter#setViewImage(ImageView, int)
     * @see MultiLayoutSimpleAdapter#setViewImage(ImageView, String)
     * @see MultiLayoutSimpleAdapter#setViewText(TextView, String)
     */
    public static interface ViewBinder {
        /**
         * 绑定指定的数据到指定的视图.
         *
         * 当使用 ViewBinder 绑定了数据时,必须返回真.如果该方法返回假, SimpleAdapter 会用自己的方式去绑定数据.
         *
         * @param view
         *            要绑定数据的视图
         * @param data
         *            绑定用的数据
         * @param textRepresentation
         *            代表所提供的数据的安全字符串: 或者是 data.toString(),或者是空串,不能为空.
         *
         * @return 如果将数据绑定到了视图,返回真;否则返回假.
         */
        boolean setViewValue(View view, Object data, String textRepresentation);
    }

    /**
     * <p>
     * An array filters constrains the content of the array adapter with a
     * prefix. Each item that does not start with the supplied prefix is removed
     * from the list.
     * </p>
     */
    private class SimpleFilter extends Filter {

        @Override
        protected FilterResults performFiltering(CharSequence prefix) {
            FilterResults results = new FilterResults();

            if (mUnfilteredData == null) {
                mUnfilteredData = new ArrayList<Map<String, ?>>(mData);
            }

            if (prefix == null || prefix.length() == 0) {
                ArrayList<Map<String, ?>> list = mUnfilteredData;
                results.values = list;
                results.count = list.size();
            } else {
                String prefixString = prefix.toString().toLowerCase();

                ArrayList<Map<String, ?>> unfilteredValues = mUnfilteredData;
                int count = unfilteredValues.size();

                ArrayList<Map<String, ?>> newValues = new ArrayList<Map<String, ?>>(
                        count);

                for (int i = 0; i < count; i++) {
                    Map<String, ?> h = unfilteredValues.get(i);
                    if (h != null) {

                        int len = mTo.length;

                        for (int j = 0; j < len; j++) {
                            String str = (String) h.get(mFrom[j]);

                            String[] words = str.split(" ");
                            int wordCount = words.length;

                            for (int k = 0; k < wordCount; k++) {
                                String word = words[k];

                                if (word.toLowerCase().startsWith(prefixString)) {
                                    newValues.add(h);
                                    break;
                                }
                            }
                        }
                    }
                }

                results.values = newValues;
                results.count = newValues.size();
            }

            return results;
        }

        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {
            // noinspection unchecked
            mData = (List<Map<String, ?>>) results.values;
            if (results.count > 0) {
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    }
}

MultiLayoutSimpleAdapter.java

xml文件

select_express.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="@color/background" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="49dp"
        android:background="#39AFFF" >

        <ImageView
            android:id="@+id/back"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_marginLeft="10dp"
            android:layout_marginStart="10dp"
            android:contentDescription="@null"
            android:src="@drawable/back" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="选择快递公司"
            android:textColor="#FFFFFF"
            android:textSize="18sp" />
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/white">

        <!-- 1 -->
        <LinearLayout
            android:id="@+id/LinearLayout1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        </LinearLayout>

        <!-- 2 -->
        <LinearLayout
            android:id="@+id/LinearLayout2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:orientation="horizontal">

        </LinearLayout>
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/divider_efedf1"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white" >

        <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none" />

        <TextView
            android:id="@+id/float_letter"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_gravity="center"
            android:background="@color/color_39afff"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:textSize="40sp"
            android:visibility="gone" />

        <com.zte.server_express_company.SlideBar
            android:id="@+id/slideBar"
            android:layout_width="30dp"
            android:layout_height="match_parent"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true" />
    </RelativeLayout>

</LinearLayout>

select_express.xml

select_express_item1.xml

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

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="16sp"
        android:textColor="@color/textColor"
        android:layout_marginLeft="20dp"
        android:layout_marginStart="20dp"
        android:gravity="center_vertical"
        android:text="@string/hello_world"/>

</LinearLayout>

select_express_item1.xml

select_express_item2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="24dp"
    android:orientation="vertical"
    android:background="@color/background" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="16sp"
        android:textColor="@color/color_afb8c9"
        android:layout_marginLeft="20dp"
        android:layout_marginStart="20dp"
        android:gravity="center_vertical"
        android:text="@string/hello_world"/>

</LinearLayout>

select_express_item2.xml

时间: 2024-10-11 11:05:04

ListView实现字母索引的相关文章

android之ListView分组及字母索引导航(2)重构-接口

上篇文章对listView 分组和字母索引导航的实现思路做了分析,并依照思路一步步实现,到最后已经较好的实现了全部功能.但是仔细研究就会发现其实现不够好,主要问题: 1.               对于一个使用范围比较广泛的布局,以上实现不够通用,尤其是Bo中需加上一些多余的字段,这些字字段本身并没有意义. 2.               代码都糅合在activity中. 针对以上两点做一些代码重构.首先我们把其优化为一个通用的activity.这样做成通用的View就很容易:然后对代码进行

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

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

ListView的快速索引

在玩即时通讯app比如微信的联系人列表的时候,发现联系人列表的右侧有个竖排的字母索引,点击字母,可以快速索引到相应的联系人.本篇就是实现这个功能. 效果图: 快速索引 快速索引 需要实现: 将右侧的字母画到屏幕上去: 联系人列表排序: 根据联系人名字的首字母为ListView打上TAG 选中索引的某个字母,出现相应的TAG下的联系人: 画索引字母到屏幕上 新建一个类,继承自View public class QuickIndexBar extends View 并提供构造器方法,在构造器中完成相

仿联系人列表或其他上的字母索引

这个小功能github有很多.不同的应用可能需求稍微有些差别,比如listview滑动时字母是不是跟随滑动:手动点击字母是不是在屏幕中间实现一个提示.实现思路上也有多种,比如自己去draw每个字母,然后处理滑动:有些可能就是借助TextView来展示字母列表.有些是点击索引的时候,整个索引的背景出来,离开后背景消失.当然这些都是细节问题.看了几个demo,感觉还目前自己的需求有些差别,而且为了实现"大而全"有些多余的东西,因此决定自己写个. 需求是这样的:中文参与索引,字母或者其他开头

Android 自定义 View 实现通讯录字母索引(仿微信通讯录)

一.效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等.这里我截了一张美团选择城市的图片来看看: 我们今天就来实现图片中右侧模块的索引功能,包括触摸显示以选中的索引字母.这里我的UI界面主要是参照微信的界面来实现,所以各位也可以对照微信来看看效果,什么都不说了,只有效果图最具有说服力! 二.分析: 我们看到这样的效果我们心理都回去琢磨,他是如何实现的: 首先,它肯定是通过自定义 View 来实现的,因为 Android 没有提供类似这样的控件

仿IOS 带字母索引的滑轮控件

效果大概就是这样,右边是字母索引效果 做开发的时候,经常碰到产品经理设计出来的界面是参考IOS控件设计出来的 ,比如上图效果  ios有个控件是UIPickerView  就是可以上下滑动 并有些3d效果,非常炫. 但是android并没有提供这样的原生控件支持,所以需要通过其他方式实现类似效果.上图就是我开发中用到的一个效果. 话不多说 ,直接上代码: Activity package com.example.picscrollview; import java.util.ArrayList;

Windows Phone 7 LongListSelector控件实现分类列表和字母索引

在wp7手机里面的联系人列表和程序里面里面我们可以看到一个根据字母索引来定位联系人或者应用程序的控件,那么这个控件就是LongListSelector控件了. LongListSelector是一种比ListBox更加强大的列表控件,你可以根据你列表的信息来分类排列,根据类别快速定位到你选中的类别的列表下,在数据量很大的情况下这种分类的优势很明显.LongListSelector可以自定义列表头,列表尾.类表头.列别尾等的样式和数据,可以实现各种个性化的列表样式和不同的数据的展现方式.Windo

快速集成android实现listview的字母A-Z排序,界面侧边字母索引

 转载请标明出处 Android手机字母A-Z排序侧边索引是非常常见的功能,在此提供快速集成框架.教你用Android studio工具一分钟搞定这个效果. 实现效果: 以及点击F跳转效果 第一步库包导入实现拼音检索功能 -------拼音检索详细见: compile 'com.github.promeg:tinypinyin:1.0.0'// ~80KB同步后后面会下载80k的文件,就可以使用 -------测试一下: public void go1(View view){//按钮go1点击测

android字母索引实现ListView定位

最近闲的很,没什么事干 ,在玩手机的时间看到android系统自带的那个通讯录软件对联系人的快速定位功能.  感觉这个功能也比较实用自己就试着自己去实现. 虽然网络上还是有大牛封闭好了的框架,但是如果自己来实现一下也是不错的, 个人比较喜欢自己写的东西,别人写好的东西可以拿来借鉴,还是不推荐看也不看直接拿 来用,代码可以复制,作者的思想就需要慢慢体会的. 基本介绍: 首先安卓本身已经提供一个接口来实现快速定位的, SectionIndexer接口共有三个方法. Object[] getSecti