自定义view之微信通讯录导航

本篇博客将讲述搜索通讯录,就是微信常见的通讯录

通过点击右边的字母案件实现和左边条目联动:

首先要自定义一个控件,看代码:

public class IndexSiderBar extends View {
    private ChoceInterFace choceInterFace;

    public static String[] b = {"#","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 Paint textPaint;
    private ArrayList<String> singLin=new ArrayList<>();
    private Paint circlePaint;
    private int width;
    private int height;
    private int textHeight;
    private int mChoose;
    private TextView textInfo;

    public void setChoceInterFace(ChoceInterFace choceInterFace) {
        this.choceInterFace = choceInterFace;
    }

    public IndexSiderBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public IndexSiderBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        textInfo=new TextView(context);
    }

    private void init() {
        for(String line:b){
            singLin.add(line);
        }

        textPaint = new Paint();
        textPaint.setAntiAlias(true);

        textPaint.setTypeface(Typeface.DEFAULT_BOLD);
        textPaint.setStrokeWidth(22);

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setStyle(Paint.Style.FILL);

        circlePaint.setStrokeWidth(10);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getWidth();
        height = getHeight();
        textHeight = height / (singLin.size() + 1);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if(mChoose!=-1){
            circlePaint.setColor(Color.parseColor("#FF4081"));
        }else {
            circlePaint.setColor(Color.parseColor("#4a4949"));
        }

        canvas.drawCircle(textHeight/2.0f,textHeight/2.0f,textHeight/4.0f,circlePaint);

        for (int i = 0; i < singLin.size(); i++) {
            if(mChoose==i){
                textPaint.setColor(Color.parseColor("#FF4081"));
            }else {
                textPaint.setColor(Color.parseColor("#4a4949"));
            }

            //文字的x坐标轴
            float xPos = width / 2- textPaint.measureText(singLin.get(i)) / 2;

            Paint.FontMetricsInt fm = textPaint.getFontMetricsInt();
            int centerLine = textHeight * (i + 1) + textHeight/ 2;
            int baseLine = centerLine + (fm.bottom - fm.top) / 2 - fm.bottom;
            canvas.drawText(singLin.get(i),xPos,baseLine,textPaint);
            invalidate();
        }
    }
}

上面的代码绘制了右边的条目列表,接下来要实现的是onTouch事件,可以获取触摸位置:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();
        float posY = y / height * (singLin.size() + 1);
        mChoose = (int) (posY - 1);

        String text = singLin.get(mChoose);
        if(null!=textInfo){
            Log.i("TAG------------>", "是否设置了");
            textInfo.setVisibility(VISIBLE);
            textInfo.setText(text);
            choceInterFace.onChose(mChoose,text);
        }

        switch (event.getAction()){
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_UP:
                mChoose=-1;
                if(null!=textInfo){
                    textInfo.setVisibility(GONE);
                }
                break;
        }

        invalidate();

        return true;
    }
第三步,要实现触摸条目,得到放大的控件:

效果图


主布局中如下:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.longyue.mycustview.MainActivity">

    <ListView
        android:id="@+id/main_listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>

    <IndexSider.IndexSiderBar
        android:id="@+id/main_index_barview"
        android:layout_width="25dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:background="@mipmap/line_cycle_an"/>

    <Button
        android:id="@+id/index_info"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_toLeftOf="@id/main_index_barview"
        android:layout_margin="10dp"
        android:background="@mipmap/android_xuankuang"
        android:textSize="15sp"
        android:textColor="@color/colorBlue"
        android:visibility="invisible"
        android:text="文字"/>

</RelativeLayout>

mainactivity中代码:

public class MainActivity extends AppCompatActivity {
    private Button index_info;
    private IndexSiderBar siderBar;
    private ListView mListView;
    private List<String> datas=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        index_info= (Button) findViewById(R.id.index_info);
        siderBar= (IndexSiderBar) findViewById(R.id.main_index_barview);
        mListView= (ListView) findViewById(R.id.main_listView);


        for (int i = 0; i < siderBar.b.length; i++) {
            datas.add(siderBar.b[i]+((i+1)%2));
            datas.add(siderBar.b[i]+((i+2)%2));
        }

        final CustIndex custIndex = new CustIndex(IndexSiderBar.b, datas);

        mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,datas));

        siderBar.setTextInfo(index_info);
        siderBar.setChoceInterFace(new IndexSiderBar.ChoceInterFace() {
            @Override
            public void onChose(int chose, String info) {
                Log.i("TAG------------>", "chose:"+chose+",info:"+info);
                int selection = custIndex.getPositionForSection(chose);
                Log.i("TAG------------>", "获取的位置:"+selection);
                mListView.setSelection(selection);
            }
        });
    }
}

上面有一个类CustIndex实现了SectionIndexer接口,该接口提供三个方法:

    Object[] getSections();
    int getPositionForSection(int sectionIndex);
    int getSectionForPosition(int position);

第一个函数用于返回数据数组,第二个函数传入点击的位置返回listView对应的位置,第三个函数用于listView位置对应的导航栏的位置:

看代码:

public class CustIndex implements SectionIndexer {
   


    private String[] mIndexString;
    private List<String> mDatas=new ArrayList<>();
    private SparseIntArray mAlphaMap;

    public CustIndex(String[] mIndexString, List<String> data) {
        this.mIndexString = mIndexString;
        mDatas.addAll(data);
        mAlphaMap = new SparseIntArray(this.mIndexString.length);
    }

    @Override
    public String[] getSections() {
        return mIndexString;
    }

    @Override
    public int getPositionForSection(int sectionIndex) {
        final SparseIntArray alphaMap = mAlphaMap;
        final List<String> items = mDatas;

        if (items == null || mIndexString == null) {
            return 0;
        }

        if (sectionIndex <= 0) {
            return 0;
        }
        if (sectionIndex >= mIndexString.length) {
            sectionIndex = mIndexString.length - 1;
        }

        int count = items.size();
        int start = 0;
        int end = count;
        int pos;

        char letter = mIndexString[sectionIndex].charAt(0);
        String targetLetter = Character.toString(letter);
        int key = letter;
        // 检查map是否已经保存了key为letter的对应索引,如果有的话直接返回,如果没有则进行查找
        if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) {
            if (pos < 0) {
                //如果没有,则将end设为一个极大值,以保证利用二分查找时,肯定能包含listview中所有item的索引
                pos = -pos;
                end = pos;
            } else {
                // Not approximate, this is the confirmed start of section, return it
                return pos;
            }
        }

        if (sectionIndex > 0) {
            int prevLetter = mIndexString[sectionIndex-1].charAt(0);
            int prevLetterPos = alphaMap.get(prevLetter, Integer.MIN_VALUE);
            if (prevLetterPos != Integer.MIN_VALUE) {
                start = Math.abs(prevLetterPos);
            }
        }

        pos = (end + start) / 2;

        while (pos < end) {
            // Get letter at pos
            String item = items.get(pos);
            String curName = "";
            if (item != null) {
                curName = item.charAt(0)+"";
            }
            if (curName == null) {
                if (pos == 0) {
                    break;
                } else {
                    pos--;
                    continue;
                }
            }
            int diff = compare(curName, targetLetter);
            if (diff != 0) {
                if (diff < 0) {
                    //如果已经到了Listview的末尾,但仍没有索引字符对应的item,就将最末Item的索引返回.
                    start = pos + 1;
                    if (start >= count) {
                        pos = count;
                        break;
                    }
                } else {
                    end = pos;
                }
            } else {
                // They're the same, but that doesn't mean it's the start
                if (start == pos) {
                    // This is it
                    break;
                } else {
                    // Need to go further lower to find the starting row
                    end = pos;
                }
            }
            pos = (start + end) / 2;
        }
        alphaMap.put(key, pos);
        return pos;
    }

    @Override
    public int getSectionForPosition(int position) {
        return 0;
    }


    protected int compare(String firstLetter, String secondLetter) {
        if (firstLetter == null||firstLetter.length() == 0) {
            firstLetter = " ";
        } else {
            firstLetter = firstLetter.substring(0, 1);
        }
        if (secondLetter == null||secondLetter.length() == 0) {
            secondLetter = " ";
        } else {
            secondLetter = secondLetter.substring(0, 1);
        }

        return firstLetter.compareTo(secondLetter);
    }
}
上面方法用到了二分法来查询数据,有兴趣的童鞋可以下载源码查看具体信息,奉上: github地址

欢迎关注我的博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值