android字母导航条实现(原创)

package net.liuyx.test.product.list;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.liuyx.test.product.interfaces.JsonParseListener;
import net.liuyx.test.product.utils.JsonParse;
import net.liuyx.test.product.utils.Logs;
import net.liuyx.test.view.CarSlectClogTxtView;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;

public class BrandCarSelect extends Activity {
    private static Context self;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        self = this;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.brand_car_select);
        initContent();
        initCatalog();
        initOther();
    }
    
    /**
     * 初始化右边的导航条
     */
    private void initCatalog() {
        LinearLayout catalog = (LinearLayout) findViewById(R.id.catalog);
        final int englishWordsCounts = 26;
        final TextView t = initLetterNavi();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < englishWordsCounts; i++) {
            char c = (char) ('A' + i);
            sb.append(c).append("").append("\n");
        }
        sb.delete(sb.lastIndexOf("\n"), sb.length());
        t.setText(sb.toString());
        catalog.addView(t);
    }
    
    private TextView initLetterNavi() {
        final CarSlectClogTxtView t = new CarSlectClogTxtView(this);
        t.setLayoutParams(new LinearLayout.LayoutParams(
                LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        t.setTextSize(17.5f);
        t.setTextColor(Color.BLACK);
        t.setGravity(Gravity.CENTER);
        return t;
    }
    

    private static ListView listview;
    private static TextView hint;
    
    /**
     * 初始化ListView的内容
     */
    private void initContent() {
        listview = (ListView) findViewById(R.id.listview);
        listview.setAdapter(new CarTypeAdapter());
        hint = initHintTextView();
    }

    public TextView getHintTextView() {
        return hint;
    }

    public WindowManager getManager() {
        WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        return mWindowManager;
    }

    public WindowManager.LayoutParams getLayoutParams() {
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        return lp;
    }
    //hintTextView为显示在屏幕中间的提示字母,将该TextView添加在WindowManager比较好看,不会出现连续点击字母导航条出现TextView频闪的情况
    private TextView initHintTextView() {
        TextView t = new TextView(self);
        t.setTextColor(Color.BLACK);
        t.setTextSize(35);
        t.setVisibility(View.GONE);
        getManager().addView(t, getLayoutParams());
        return t;
    }

    private void initOther() {
        // ExecutorService exec = Executors.newCachedThreadPool();
        // exec.execute(new UpdateJsonTask());
        initIndexMaps();
    }

    static class CarTypeAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return texts.length;
        }

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

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = LayoutInflater.from(self).inflate(
                        R.layout.brand_car_select_listview_item, null);
            }
            TextView t = (TextView) convertView.findViewById(R.id.car_brand);
            t.setText(texts[position]);
            return convertView;
        }

    }
//测试数据,实际项目中应该从Json数据读取
    private static String[] texts = { "(A) AC宝马 ", "(A) 阿斯顿马丁", "(A) 奥迪",
            "(B) 巴博斯", "(B) 保时捷", "(B) 宝骏", "(B) AC宝马 ", "(B) 阿斯顿马丁", "(B) 奥迪",
            "(C) 巴博斯", "(C) 保时捷", "(C) 宝骏", "(D) AC宝马 ", "(D) 阿斯顿马丁", "(D) 奥迪",
            "(D) 巴博斯", "(D) 保时捷", "(D) 宝骏", "(D) AC宝马 ", "(E) 阿斯顿马丁", "(E) 奥迪",
            "(E) 巴博斯", "(E) 保时捷", "(F) 宝骏" };

    private static Map indexMaps = new HashMap();

    private void initIndexMaps() {
        Map wordsCount = new TreeMap();
        for (int i = 0; i < texts.length; i++) {
            Matcher m = Pattern.compile("^\\([A-Z]\\)").matcher(texts[i]);
            String head = "";
            if (m.find()) {
                head = m.group();
                head = head.replaceAll("[()]", "");
                if (wordsCount.get(head) != null) {
                    int counts = wordsCount.get(head);
                    counts++;
                    wordsCount.put(head, counts);
                } else {
                    wordsCount.put(head, 1);
                }
            }
        }
        int index = 0;

        for (String s : wordsCount.keySet()) {
            indexMaps.put(s, index);
            index = index + wordsCount.get(s) + 1;
        }
        for (String s : indexMaps.keySet()) {
            Logs.v(s + ".pos = " + indexMaps.get(s));
        }
    }
//根据首字母返回listview.setSelection的position
    public void getSelectionForListView(String msg) {
        if (indexMaps.get(msg) != null) {
            int pos = indexMaps.get(msg);
            if (pos <= indexMaps.size() - 6)//魔数6是我自动调整的,因为加入不加或者减去这个数字6,setSelection会把该Item显示在屏幕的最底端
                listview.setSelection(pos + 6);
            else
                listview.setSelection(pos);
        }
    }
}

布局文件为:




    

        

            
            
        

        
        
    
    


自定义的CarSelectTextView,即显示导航条的TextView代码如下:

package net.liuyx.test.view;

import net.liuyx.test.product.list.BrandCarSelect;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

public class CarSlectClogTxtView extends TextView{
    private static Context context;
    private WindowManager wm;

    public CarSlectClogTxtView(Context context) {
        super(context);
        CarSlectClogTxtView.context = context;
        hint =((BrandCarSelect) context).getHintTextView();
        wm = ((BrandCarSelect) context).getManager();
    }
    
    private TextView hint;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float y = event.getY();
        String  text = textToDisplay(y);
        hint.setText("");
        hint.setText(text);
        switch (action) {
        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_DOWN:
            postTask(hint);
            ((BrandCarSelect) context).getSelectionForListView(text);//自动跳转到自动位置
            break;
        case MotionEvent.ACTION_UP:
            postDelayTask(hint,3000);
            break;
        }
        return true;
    }
    
    private String textToDisplay(float y) {
        int height = wm.getDefaultDisplay().getHeight();
        float percent = y / height;
        int len = (int) (26 * percent);
        char word = (char) ('A' + len);
        final String text = word+ "";
        return text;
    }
    
    private void postTask(final TextView t) {
        t.post(new Runnable() {

            @Override
            public void run() {
                t.setVisibility(View.VISIBLE);
            }
        });
    }


    private void postDelayTask(final TextView t, int time) {
        t.postDelayed(new Runnable() {

            @Override
            public void run() {
                t.setVisibility(View.GONE);
            }

        }, time);
    }

}

最后的效果图为:


应用场景“

因为项目需求,在产品库选择车型时,前面会出现字母,如(B)宝马,如果ListView前面没有响应的字母,可以用TextWatcher。需要当用户滑动导航条,在屏幕中间出现提示字母,并跳转到ListView指定的行中。

思路:

布局使用横向布局,或者相对布局,或者帧布局皆可。我选择的是横向布局,右边为一个垂直的LinearLayout,该容器只有一个TextView,在本例子该TextView为自定义的CarSlectClogTxtView(原谅,觉得取名一直很难,又不想太长,所以取得如此奇怪),在该TextView中覆盖了基类的OnTouch事件,然后获取事件发生的坐标,猜测用户点击的是哪个字母,并在屏幕中间显示出来,然后通过正则表达式,将ListView的内容过滤出中有字母的字符串,跳转到出现该字母的第一个行(有一个Map对象,通过setSelection跳转,注意,该Map对象并不是硬编码的,而是根据出现每个字母的次数,然后计算出出现首个该字母的位置,详情见代码)。

好了,有问题请留言,谢谢~

你可能感兴趣的:(android)