Android 自定义seekbar【仿微信联系人】

本文介绍如何在Android中自定义Seekbar,以实现类似微信联系人界面的效果。博主分享了实现这一功能的步骤,并提到了依赖pinyin4j库进行汉子转拼音的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**

 *
 * 转载请标明出处:
http://blog.youkuaiyun.com/u013598111/article/details/50452578

 *   @author:【JunTao_sun】
 *
 *
*/

 

汉子转拼音 需要依赖 pinyin4j 架包

public class MainActivity extends Activity implements Select2String {
	private static final String TAG = null;
	//自定义 seekbar
	private MySeekBar seekbar;
	private ListView listView;
	// 联系人信息
	private ArrayList<String> Names;
	//自定义比较器
	private PinyinComparator comparator;
	// 实体类
	private List<NamesModel> namesData = new ArrayList<NamesModel>();
	
	private ListViewadapte adapat;
	private TextView text;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		comparator = new PinyinComparator();
		
		initViews();
		initData();
		sortData();

	}

	/**
	 * 将数据NamesModel排序
	 */
	private void sortData() {
		Collections.sort(namesData, comparator);
        adapat = new ListViewadapte(namesData, this);
		listView.setAdapter(adapat);

	}

	private void initViews() {
		text = (TextView) this.findViewById(R.id.text_tag);
		listView = (ListView) this.findViewById(R.id.listview);

		View head = View.inflate(MainActivity.this, R.layout.head, null);
        listView.addHeaderView(head);

		seekbar = (MySeekBar) this.findViewById(R.id.seekbar);
		seekbar.setOnSelect2StringListener(this);
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
			if(position!=0){
				Toast.makeText(MainActivity.this,
						namesData.get(position-1).getName(), Toast.LENGTH_SHORT)
						.show();
				}

			}
		});

	}

	/* 
	 * 
	 * 接口回调的     
	 * @str字母
	 */
	
	@Override
	public void getSelect(String str) {

		for (int i = 0; i < namesData.size(); i++) {

			if (str.equals("↑")) {

				listView.setSelection(0);
			}

			if (str.equals(namesData.get(i).getNameLeftPinying())) {
				int select = adapat.getSelectPosition(i);
				int point = adapat.getSelectItem(select);
                listView.setSelection(point);
			}}}

	private void initData() {
		// 先添加数据
		Names = new ArrayList<String>();
		Names.add("浅蓝色大海");
		Names.add("梦幻彩虹");
		Names.add("湛蓝天空");
		Names.add("美丽故乡");
		Names.add("离城梦");
		Names.add("梦仙境");
		Names.add("羽化尘");
		Names.add("人来疯");
		Names.add("苦恋伊");
		Names.add("淡年华");
		Names.add("追梦少年");
		Names.add("曼陀罗");
		Names.add("石头人");
		Names.add("忆往昔");
		Names.add("夜未央");
		Names.add("巴黎港");
		Names.add("温暖");
		Names.add("幻想爱");
		Names.add("笄发醒");
		Names.add("Elisc");
		Names.add("Ahiso");
		Names.add("goiso");
		Names.add("苦酒满");
		Names.add("@魅力");
		Names.add("U可爱");
		Names.add("t天空");
		Names.add("&money$");
		Names.add("*雪花*");
		Names.add("吖美呀");
		Names.add("大话水浒");
		Names.add("一沙一世界");
		Names.add("花儿朵朵");
		Names.add("韩国旅游");
		Names.add("富士山");
		Names.add("旧金山");
		Names.add("太阳");
		Names.add("月亮");
		Names.add("温暖的旧时光");
		Names.add("记忆美好的");
		Names.add("喝点小酒");
		Names.add("几碟花生");
		// 将上面的数据 添加到NameModel中 并加入部首拼音
		for (int i = 0; i < Names.size(); i++) {
            NamesModel name = new NamesModel();
			String str = Names.get(i);
			char[] ch = str.toCharArray();

			name.setName(str);
            String saveLeft=null;
			String firstWord=str.substring(0, 1);
			//检查str 第一个string是否 是字母开头
			boolean isEnglish=firstWord.matches("[a-z-A-Z]");
			Log.e(TAG, "isEnglish"+isEnglish);
			if(isEnglish){
				saveLeft=PinyingTrasfrom.getInstance().english2Left(str);
			}else if(!isEnglish){//如果不是字母开头  是汉字 则调用拼音转换工具
				//如果是中文 再调用拼音转换 不然会抛异常
				if(str.matches("^[\u4E00-\u9FFF]+$")){
				String	s=PinyingTrasfrom.getInstance().HanZid2Pinying(ch[0]);
				 s = s.substring(0, 1);
				 saveLeft = s.matches("[A-Z]") ? s : "#";
				}else {
					saveLeft="#";
				}
				 
			}
			
			// 判断是否为字母
		
			name.setNameLeftPinying(saveLeft);
			namesData.add(name);

		}

	}

	@Override
	public void onHidden() {
		text.setVisibility(View.GONE);

	}

	@Override
	public void onAppear(String info) {
		text.setVisibility(View.VISIBLE);
		text.setText(info);

	}

}
拼音转换工具类:

package com.example.pingyin.utils;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class PinyingTrasfrom {

	private static PinyingTrasfrom py;
	private PinyingTrasfrom() {
		super();
		
	}

	public static PinyingTrasfrom getInstance(){
		
		if(py==null){
		//为什么不放在方法  因为不必要每次都检查  效率低
			//为什么放在中间 里面还要判断一次 因为防止里面有多余线程 多次创建
			synchronized (PinyingTrasfrom.class) {
				
				if(py==null){
					py=	new PinyingTrasfrom();
				
				}
				
			}
		
		}
		return py;
		
		
		
		
	}
	public  String HanZid2Pinying(char ch){
		//拼音转换格式
		HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
		// 大小写
		outputFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
//		outputFormat.setVCharType(HanyuPinyinVCharType.WITH_U_AND_COLON);
		// 音调 返回有带数字
//		outputFormat.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER);

		String[] st=null;
		try {
		//得到 每一个字符的拼音 String 
		st = PinyinHelper.toHanyuPinyinStringArray(ch, outputFormat);
		} catch (BadHanyuPinyinOutputFormatCombination e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return st[0];
		
	};
	
	/**
	 * 
	 * 得到是英语名字的时候 第一个大写字母
	 * @param english
	 * @return
	 */
	public String english2Left(String english){
		
		
		
		String en=english.substring(0, 1);
		return en.toUpperCase();
		
		
	}
	
}
ListView适配器:

package com.example.pingyin;

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

import com.example.pingyin.utils.PinyingTrasfrom;
import com.hp.hpl.sparta.xpath.PositionEqualsExpr;

import android.R.array;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class ListViewadapte extends BaseAdapter {
	private static final String TAG = null;
	private List<NamesModel> data;
	private Context c;

	public ListViewadapte() {
		super();

	}

	public ListViewadapte(List<NamesModel> t, Context c) {
		super();
		data = (List<NamesModel>) t;
		this.c = c;
		
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return data.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return data.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	static class ViewHold {

		TextView textLeft;
		TextView text;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHold hold;
	
		if (convertView == null) {
			convertView = View.inflate(c, R.layout.list_item, null);
			hold = new ViewHold();
			hold.textLeft = (TextView) convertView.findViewById(R.id.left);
			hold.text = (TextView) convertView.findViewById(R.id.text);
			convertView.setTag(hold);
		} else {
			hold = (ViewHold) convertView.getTag();
		}
	
		int selection = getSelectPosition(position);
	
		
		//判断是否是首字母的位置
		if (position == getSelectItem(selection)) {
         hold.textLeft.setVisibility(View.VISIBLE);
			hold.textLeft.setTextSize(20);
			hold.textLeft.setText(data.get(position).getNameLeftPinying());
		
		} else {
			hold.textLeft.setVisibility(View.GONE);
		}
		hold.text.setText(data.get(position).getName());
		return convertView;

	}

	/*
	 * 根据ListView的当前位置获取分类的首字母的Char ascii值
	 * 
	 * @param position
	 * 
	 * @return  该position 对应的数据  拼音对应的ascii值
	 */
	public int getSelectPosition(int position) {
		NamesModel  namesModel = data.get(position);

		String s = namesModel.getNameLeftPinying();

		char[] charResult = s.toCharArray();
		char charFirst = charResult[0];
		int selection = 0;
		return selection = charFirst;

	}

	/**
	 * @param postion
	 * 数据position 是否是第一次出现 归类   @selection Char ascii值
	 * @return
	 */
	public int getSelectItem(int selection) {
		NamesModel str = null;
		for (int i = 0; i < data.size(); i++) {

			str = data.get(i);

			char[] cs = str.getNameLeftPinying().toCharArray();
			//得到汉子 第一个字的拼音字符
			char c = cs[0];
		
			if (c == selection) {
				return i;
			}
		}

		return -1;
	}

}
排序比较器
package com.example.pingyin.utils;

import java.util.Comparator;
import java.util.List;

import com.example.pingyin.NamesModel;


public class PinyinComparator implements Comparator<NamesModel> {

	@Override
	public int compare(NamesModel o1, NamesModel o2) {

           //# >@
		if (o1.getNameLeftPinying().equals("@")
				|| o2.getNameLeftPinying().equals("#")) {
			return -1;
		} else if (o1.getNameLeftPinying().equals("#")
				|| o2.getNameLeftPinying().equals("@")) {
			return 1;
		} else {
			return o1.getNameLeftPinying().compareTo(o2.getNameLeftPinying());
		}


}}
自定义seekbar:

package com.example.pingyin;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Rect;
import android.graphics.pdf.PdfDocument;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

public class MySeekBar extends View {
	private static final String TAG = null;
	private String[] pinyin = 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", "#" };

	public MySeekBar(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		rect = new Rect();
		paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(0xff0000ff);
		paint.setDither(true);
		paint.setTextSize(sp2px(45));
	}

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

	}

	public MySeekBar(Context context) {
		this(context, null);

	}

	private Paint paint;
	private Rect rect;
	private int textWidth;
	private int textHheiht;
	private int padding = 16;
	//字母中  最大的宽度
	private int maxWdith;
	private float choose;
	//是否画背景
	private boolean showBkg;
	//间隙 可以忽略
	private float gap;
	private int shengxia;

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		maxWdith = 0;
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		paint.setTextSize(sp2px(16));
		for (int i = 0; i < pinyin.length; i++) {
			textMeasure(pinyin[i]);
			maxWdith = Math.max(maxWdith, rect.width());
		}
		// 得到最宽的字母长度
		int w = maxWdith + 2 * padding;
		setMeasuredDimension(w, getMeasuredHeight());

	}

	private void textMeasure(String str) {

		paint.getTextBounds(str, 0, str.length(), rect);

	}

	private int getTextHeight() {
		FontMetrics fm = paint.getFontMetrics();
		return (int) (fm.ascent + fm.descent);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		
		if (showBkg) {
			canvas.drawColor(Color.parseColor("#40000000"));
		}
		int height = 0;
		//得到控件宽度
		int width = getWidth();

		for (int i = 0; i < pinyin.length; i++) {
			//在循环设置大小 因为下边paint设置重置了
			paint.setTextSize(sp2px(16));
			
			textMeasure(pinyin[i]);
            //开始画字体的起始
			int startLeft = (width) / 2 - maxWdith / 2;

			if (choose == i) {

				paint.setColor(0xffff0000);
				paint.setFakeBoldText(true);

			}
			if (i == 1)
				
				startLeft = startLeft - maxWdith / 4;
			// 为了让最后个字符串 与末端有间隔 第一个字母开始的Y不变 所以把总体向上移动 一半个字体高度
			canvas.drawText(pinyin[i], startLeft, (mEachHeight + gap) * i
					+ mEachHeight - getTextHeight() + getTextHeight() / 2,
					paint);

			paint.reset();

		}

	}

	private float mEachHeight;
	private float downY;
	private boolean isChoose = false;
	// 选择区域的 位置
	private int c;
	private Select2String callback;

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		switch (event.getAction()) {
        
		case MotionEvent.ACTION_DOWN:
			
			downY = (float) event.getY();
            //得到点击 字母的位置
			c = (int) (downY / getMeasuredHeight() * pinyin.length);
            //刷新 那个字母变色
			choose = c;
			//控制是否画背景
			showBkg = true;

			if (callback != null) {

				callback.onAppear(pinyin[c]);
			}
			invalidate();

			break;
		case MotionEvent.ACTION_MOVE:

			break;
		case MotionEvent.ACTION_UP:
			if (callback != null) {

				callback.getSelect(pinyin[c]);
				callback.onHidden();
			}
		    showBkg = false;
			invalidate();
			break;

		}

		return true;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {

		super.onSizeChanged(w, h, oldw, oldh);
		mEachHeight = (h - getPaddingBottom() - getPaddingTop()) / 32;
		// 除了字母占据的空间大小
		shengxia = (int) (h - 27 * mEachHeight) + (-getTextHeight() * 2);
		// 字母间的间隙
		gap = shengxia / 54;
	}

	// 回调接口 当点击那个字母的时候进行回调
	interface Select2String {

		void getSelect(String str);

		void onHidden();

		void onAppear(String pinyin);
	}

	public void setOnSelect2StringListener(Select2String select) {
		this.callback = select;
	}

	/**
	 * sp 2 px
	 * 
	 * @param spVal
	 * @return
	 */
	protected int sp2px(int spVal) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
				spVal, getResources().getDisplayMetrics());

	}

}
实体类NamesModel:
package com.example.pingyin;

public class NamesModel {

	public NamesModel() {
		
	}
	//汉子
	private String name;
	//汉子第一个字的 拼音
	private String nameLeftPinying;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getNameLeftPinying() {
		return nameLeftPinying;
	}
	public void setNameLeftPinying(String nameLeftPinying) {
		this.nameLeftPinying = nameLeftPinying;
	}
	

}

下边是布局实现:

<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=".MainActivity" >
 
   

    <ListView
        android:id="@+id/listview"
        android:listSelector="#CCCCCC"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:dividerHeight="0dp"
        android:scrollbars="none" >
    </ListView>

    <com.example.pingyin.MySeekBar
        android:id="@+id/seekbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true" />
     <TextView android:layout_width="120dp"
        android:layout_height="120dp"
        android:text="A"
        android:textColor="#ffffffff"
        android:id="@+id/text_tag"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:textSize="40sp"
        android:visibility="gone"
        android:background="@drawable/tip"/>

</RelativeLayout>

List_Item:


<LinearLayout 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"
  android:orientation="vertical" >


    <TextView
        android:id="@+id/left"
        android:layout_width="fill_parent"
        android:layout_height="45dp"
        android:gravity="center_vertical"
        android:background="@drawable/leftpy" />
     <TextView
         android:id="@+id/text"
         android:gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="35dp"
        />
    

</LinearLayout>




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值