android 仿微信demo————微信通讯录界面功能实现(移动端,服务端)

Posted 你要永远相信光z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 仿微信demo————微信通讯录界面功能实现(移动端,服务端)相关的知识,希望对你有一定的参考价值。

android 仿微信demo————微信启动界面实现

android 仿微信demo————注册功能实现(移动端)

android 仿微信demo————注册功能实现(服务端)

android 仿微信demo————登录功能实现(移动端)

android 仿微信demo————登录功能实现(服务端)

android 仿微信demo————微信主界面实现

android 仿微信demo————微信消息界面实现(移动端)

android 仿微信demo————微信消息界面实现(服务端)

android 仿微信demo————微信通讯录界面功能实现(移动端,服务端)

android 仿微信demo————微信发现界面实现

android 仿微信demo————微信顶部操作栏界面实现

android 仿微信demo————微信顶部操作栏搜索按钮实现(查询通讯录好友功能)

android 仿微信demo————微信顶部操作栏加号按钮实现(弹出子菜单)

前面我们实现了微信消息界面的实现,这篇继续完善微信功能,实现微信通讯录界面

移动端微信通讯录界面功能实现

微信通讯录,头部是四个标签(不进行分组),下面是好友信息且根据呢称首字母进行排序分组,底部还统计了好友个数,右边是一组英文字母导航,可滑动并且还可以点击跳转到相应的分组
在这里插入图片描述
微信好友和顶部的四个标签,可以用ListViw实现并指定一个item布局,分组效果只需要在代码段进行判断即可

右边的字母操作行可以自定义一个组件继承AppCompatTextView,为什么要用它呢,而不用TextView呢?因为UI设计限定了一个文本的宽度,但是文本的长度可能比较长,如果设定一个固定的textSize,就导致一部分文本无法显示,而AppCompatTextView最显著的特点是可以自适应字体宽度大小变化。这个特点很有用,可以让文本随着文本宽度的变化,限定在一个固定范围内完整显示出来:

修改微信通信录界面的fragment布局
contactlist_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<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.wxchatdemo.MainWeixin">
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@drawable/main_list_divider_line"
        android:dividerHeight="1.5px"
        android:layout_marginBottom="50dp" />
    <com.example.wxchatdemo.tools.SideBar
        android:id="@+id/side_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="100dp"
        android:layout_marginTop="100dp"
        android:paddingRight="10dp"
        android:textColor="@color/black"
        android:textSize="9sp" />
</RelativeLayout>

fragment整体布局使用相对布局,这样可以通过 android:layout_alignParentRight="true"属性指定右边的自定义字母导航(SideBar继承AppCompatTextView)在父容器右边(即在屏幕中间的右边),相对布局包括两个组件(ListView,SideBar)

创建自定义组件SideBar.java继承AppCompatTextView
SideBar.java

package com.example.wxchatdemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class SideBar extends android.support.v7.widget.AppCompatTextView {
    private String[] letters = new String[]{"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 Paint bigTextPaint;
    private Paint scaleTextPaint;
    private Canvas canvas;
    private int itemH;
    private int w;
    private int h;
    /**
     * 普通情况下字体大小
     */
    float singleTextH;
    /**
     * 缩放离原始的宽度
     */
    private float scaleWidth;
    /**
     * 滑动的Y
     */
    private float eventY = 0;
    /**
     * 缩放的倍数
     */
    private int scaleSize = 1;
    /**
     * 缩放个数item,即开口大小
     */
    private int scaleItemCount = 6;
    private ISideBarSelectCallBack callBack;

    public SideBar(Context context) {
        this(context, null);
    }

    public SideBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
            scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);
            scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);
            scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));
            ta.recycle();
        }
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(getCurrentTextColor());
        textPaint.setTextSize(getTextSize());
        textPaint.setTextAlign(Paint.Align.CENTER);
        bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bigTextPaint.setColor(getCurrentTextColor());
        bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));
        bigTextPaint.setTextAlign(Paint.Align.CENTER);
        scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scaleTextPaint.setColor(getCurrentTextColor());
        scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));
        scaleTextPaint.setTextAlign(Paint.Align.CENTER);
    }

    public void setDataResource(String[] data) {
        letters = data;
        invalidate();
    }

    public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {
        this.callBack = callBack;
    }

    /**
     * 设置字体缩放比例     *     * @param scale
     */
    public void setScaleSize(int scale) {
        scaleSize = scale;
        invalidate();
    }

    /**
     * 设置缩放字体的个数,即开口大小     *     * @param scaleItemCount
     */
    public void setScaleItemCount(int scaleItemCount) {
        this.scaleItemCount = scaleItemCount;
        invalidate();
    }

    private int dp(int px) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (px * scale + 0.5f);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
                    eventY = event.getY();
                    invalidate();
                    return true;
                } else {
                    eventY = 0;
                    invalidate();
                    break;
                }
            case MotionEvent.ACTION_CANCEL:
                eventY = 0;
                invalidate();
                return true;
            case MotionEvent.ACTION_UP:
                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
                    eventY = 0;
                    invalidate();
                    return true;
                } else
                    break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        this.canvas = canvas;
        DrawView(eventY);
    }

    private void DrawView(float y) {
        int currentSelectIndex = -1;
        if (y != 0) {
            for (int i = 0; i < letters.length; i++) {
                float currentItemY = itemH * i;
                float nextItemY = itemH * (i + 1);
                if (y >= currentItemY && y < nextItemY) {
                    currentSelectIndex = i;
                    if (callBack != null) {
                        callBack.onSelectStr(currentSelectIndex, letters[i]);
                    }                    //画大的字母
                    Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();
                    float bigTextSize = fontMetrics.descent - fontMetrics.ascent;
                    canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize,
                            singleTextH + itemH * i, bigTextPaint);
                }
            }
        }
        drawLetters(y, currentSelectIndex);
    }

    private void drawLetters(float y, int index) {        //第一次进来没有缩放情况,默认画原图
        if (index == -1) {
            w = getMeasuredWidth();
            h = getMeasuredHeight();
            itemH = h / letters.length;
            Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
            singleTextH = fontMetrics.descent - fontMetrics.ascent;
            for (int i = 0; i < letters.length; i++) {
                canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);
            }            //触摸的时候画缩放图
        } else {            //遍历所有字母
            for (int i = 0; i < letters.length; i++) {                //要画的字母的起始Y坐标
                float currentItemToDrawY = singleTextH + itemH * i;
                float centerItemToDrawY;
                if (index < i)
                    centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount);
                else
                    centerItemToDrawY = singleTextH + itemH * (index - scaleItemCount);
                float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));
                float maxRightX = w - getPaddingRight();                //如果大于0,表明在y坐标上方
                scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);
                float drawX = maxRightX - scaleWidth * delta;                //超出边界直接花在边界上
                if (drawX > maxRightX)
                    canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i, textPaint);
                else
                    canvas.drawText(letters[i], drawX, singleTextH + itemH * i, scaleTextPaint);
            }
        }
    }

    public interface ISideBarSelectCallBack {
        void onSelectStr(int index, String selectStr);
    }
}

右侧字母导航条,包括3个自定义的属性,下面将给出

在colors.xml文件添加如下代码
colors.xml

    <declare-styleable name="SideBar">
        <attr name="scaleSize" format="integer"/>
        <attr name="scaleItemCount" format="integer"/>
        <attr name="scaleWidth" format="dimension"/>
    </declare-styleable>

要在字母导航中点击每一个字母可跳转相应分组,需要借助汉字转拼音工具类和自定义字母排序类,我们知道,java中是没有提供接口和方法让我们直接将汉字转成拼音的。在此我选择了使用第三方jar包的方式,因为它体积不大而且更加准确。

创建汉字转拼音工具类Cn2Spell.java
Cn2Spell.java

package com.example.wxchatdemo.tools;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;

/** * 汉字转换位汉语拼音,英文字符不变 */
public class Cn2Spell {
    public static StringBuffer sb = new StringBuffer();
    /**     * 获取汉字字符串的首字母,英文字符不变     * 例如:阿飞→af     */
    public static String getPinYinHeadChar(String chines) {
        sb.setLength(0);
        char[] chars = chines.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] > 128) {
                try {
                    sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));                }
                catch (Exception e) {
                    e.printStackTrace();                }
            }
            else {
                sb.append(chars[i]);            }
        }
        return sb.toString();    }
        /**     * 获取汉字字符串的第一个字母     */
        public static String getPinYinFirstLetter(String str) {
            sb.setLength(0);
            char c = str.charAt(0);
            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
            if (pinyinArray != null) {
                sb.append(pinyinArray[0].charAtandroid 仿微信demo————微信顶部操作栏搜索按钮实现(查询通讯录好友功能)

android 仿微信demo————微信顶部操作栏搜索按钮实现(查询通讯录好友功能)

Android 仿微信通讯录 导航分组列表-上使用ItemDecoration为RecyclerView打造带悬停头部的分组列表

android 仿微信demo————微信发现界面实现

android 仿微信demo————微信启动界面实现

android 仿微信demo————微信主界面实现