自定义类似微信联系人列表姓名首字母边栏索引列表效果

声明:部分代码和资源并非原创,而是从互联网上下载。资源和代码经过本人的一些优化,逻辑更清晰易读,加强了使用性,嵌入项目时更容易。

效果类似于



先来看一下右边的SideBar字母索引列表效果如何实现:

package org.initialltouchlist;

import com.example.listsildedel.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.HeaderViewListAdapter;
import android.widget.ListView;
import android.widget.SectionIndexer;
import android.widget.TextView;

public class SideBar extends View {
	private char[] l;
	private SectionIndexer sectionIndexter = null;
	private ListView list;
	private TextView mDialogText;
	Bitmap mbitmap;
	private int type = 1;
	private int color = 0xff858c94;

	public SideBar(Context context) {
		super(context);
		init();
	}

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

	private void init() {

		l = new char[] { '!', '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', '#' };
		mbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.scroll_bar_search_icon);
	}

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

	public void setListView(ListView _list) {
		list = _list;
		HeaderViewListAdapter ha = (HeaderViewListAdapter) _list.getAdapter();
		NameAdapter ad = (NameAdapter) ha.getWrappedAdapter();
		sectionIndexter = (SectionIndexer) ad;

	}

	public void setTextView(TextView mDialogText) {
		this.mDialogText = mDialogText;
	}

	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		int i = (int) event.getY();
		int idx = i / (getMeasuredHeight() / l.length);
		if (idx >= l.length) {
			idx = l.length - 1;
		} else if (idx < 0) {
			idx = 0;
		}
		if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
			setBackgroundResource(R.drawable.scrollbar_bg);
			mDialogText.setVisibility(View.VISIBLE);
			/**
			 * 中央显示的提示首字符
			 */
			if (idx == 0) {
				mDialogText.setText("Search");
				mDialogText.setTextSize(16);
			} else {
				mDialogText.setText(String.valueOf(l[idx]));
				mDialogText.setTextSize(34);
			}
			if (sectionIndexter == null) {
				sectionIndexter = (SectionIndexer) list.getAdapter();
			}
			int position = sectionIndexter.getPositionForSection(l[idx]);

			if (position == -1) {
				return true;
			}
			list.setSelection(position);
		} else {
			mDialogText.setVisibility(View.INVISIBLE);
		}
		if (event.getAction() == MotionEvent.ACTION_UP) {
			setBackgroundDrawable(new ColorDrawable(0x00000000));
		}
		return true;
	}

	Paint paint = new Paint();

	protected void onDraw(Canvas canvas) {
		paint.setColor(color);
		paint.setTextSize(12);
		paint.setStyle(Style.FILL);
		paint.setAntiAlias(true);
		paint.setTextAlign(Paint.Align.CENTER);
		float widthCenter = getMeasuredWidth() / 2;
		if (l.length > 0) {
			float height = getMeasuredHeight() / l.length;
			for (int i = 0; i < l.length; i++) {
				if (i == 0 && type != 2) {
					canvas.drawBitmap(mbitmap, widthCenter - 7, (i + 1) * height - height / 2, paint);
				} else
					canvas.drawText(String.valueOf(l[i]), widthCenter, (i + 1) * height, paint);
			}
		}
		this.invalidate();
		super.onDraw(canvas);
	}
}



onTouchEvent方法里面,实现了让屏幕中央的TextView显示正在触摸到的字母索引,并让ListView对象找到选定字母的分段位置,直接弹到列表的该字母的位置。

onDraw不用说了,绘制右边栏条。

再看下适配器类实现:NameAdapter.java

package org.initialltouchlist;

import java.util.List;

import com.example.listsildedel.R;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;

public class NameAdapter extends BaseAdapter implements SectionIndexer {

	private List<Content> list = null;
	private Context mContext;

	public NameAdapter(Context mContext, List<Content> list) {
		this.mContext = mContext;
		this.list = list;

	}

	public int getCount() {
		return this.list.size();
	}

	public Object getItem(int position) {
		return null;
	}

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

	public View getView(final int position, View view, ViewGroup arg2) {
		ViewHolder viewHolder = null;
		if (view == null) {
			viewHolder = new ViewHolder();
			view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
			viewHolder.tvTitle = (TextView) view.findViewById(R.id.title);
			viewHolder.tvLetter = (TextView) view.findViewById(R.id.catalog);
			view.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) view.getTag();
		}
		final Content mContent = list.get(position);
		if (position == 0) {
			viewHolder.tvLetter.setVisibility(View.VISIBLE);
			viewHolder.tvLetter.setText(mContent.getLetter());
		} else {
			String lastCatalog = list.get(position - 1).getLetter();
			if (mContent.getLetter().equals(lastCatalog)) {
				viewHolder.tvLetter.setVisibility(View.GONE);
			} else {
				viewHolder.tvLetter.setVisibility(View.VISIBLE);
				viewHolder.tvLetter.setText(mContent.getLetter());
			}
		}

		viewHolder.tvTitle.setText(this.list.get(position).getName());

		return view;

	}

	final static class ViewHolder {
		TextView tvTitle;
		TextView tvLetter;
	}

	public Object[] getSections() {
		return null;
	}

	public int getSectionForPosition(int position) {

		return 0;
	}

	public int getPositionForSection(int section) {
		Content mContent;
		String l;
		if (section == '!') {
			return 0;
		} else {
			for (int i = 0; i < getCount(); i++) {
				mContent = (Content) list.get(i);
				l = mContent.getLetter();
				char firstChar = l.toUpperCase().charAt(0);
				if (firstChar == section) {
					return i + 1;
				}
			}
		}
		mContent = null;
		l = null;
		return -1;
	}
}

请关注一下上述代码的getPositionForSection方法的实现。

上述代码需要实体类Content.java:


package org.initialltouchlist;

public class Content {

	private String letter;//存放首字母属性,我们需要使用这个属性进行对比来对列表进行排序
	private String name;

	public Content(String letter, String name) {
		super();
		this.letter = letter;
		this.name = name;
	}

	public String getLetter() {
		return letter;
	}

	public void setLetter(String letter) {
		this.letter = letter;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

现在我们要实现Comparator接口来进行首字母对比

package org.initialltouchlist;

import java.util.Comparator;

public class PinyinComparator implements Comparator<Content> {

	public int compare(Content o1, Content o2) {
		if (o1.getLetter().equals("@")
				|| o2.getLetter().equals("#")) {
			return -1;
		} else if (o1.getLetter().equals("#")
				|| o2.getLetter().equals("@")) {
			return 1;
		} else {
			return o1.getLetter().compareTo(o2.getLetter());
		}
	}

}

如果需要排序的数组当中都是汉字怎么办?我们需要取首个汉字的拼音首字母来进行比较。那我们可以通过以下代码来实现获取汉字的首个汉字的拼音。

注意,这需要需要第三方类库Pinyin4j 下载

package org.initialltouchlist;

import net.sourceforge.pinyin4j.PinyinHelper;

public class PinyinLetterHelper {
	/**
	 * 获取人名的拼音首字母并且大写化
	 * 
	 * @param name
	 *            人名字符串,允许开头包含英文数字和下划线
	 * @return
	 */
	public static String getPinyinFirstLetter(String name) {
		String firstletter = "#";
		if (name != null) {
			// 转换前不管是中文还是英文直接取开头字符
			char initial = name.charAt(0);
			//System.out.print(initial + "  ---From:  " + name);
			if (!((initial >= 'A' && initial <= 'Z')
					|| (initial >= 'a' && initial <= 'z')
					|| (initial >= '0' && initial <= '9') || initial == '_')) {
				firstletter = (PinyinHelper.toHanyuPinyinStringArray(initial))[0];
			} else {
				firstletter = initial + "";
			}

			firstletter = firstletter.toUpperCase();
			//System.out.print("  -- " + firstletter);
			char[] firstChar = new char[1];
			firstChar[0] = firstletter.charAt(0);
			firstletter = new String(firstChar);
			//System.out.println("    --" + firstletter);
		}
		return firstletter;
	}
}

以下是使用Demo:


package org.initialltouchlist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.example.listsildedel.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {

	private ListView mListView;
	private SideBar indexBar;
	private WindowManager mWindowManager;
	private TextView mDialogText;
	private View head;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// 启动activity时不自动弹出软键盘
		getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mListView = (ListView) this.findViewById(R.id.list);
		indexBar = (SideBar) findViewById(R.id.sideBar);
		mDialogText = (TextView) LayoutInflater.from(this).inflate(R.layout.list_position, null);
		
		head = LayoutInflater.from(this).inflate(R.layout.head, null);
		mListView.addHeaderView(head);
		mDialogText.setVisibility(View.INVISIBLE);
		
		mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
		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);
		mWindowManager.addView(mDialogText, lp);
		
		indexBar.setTextView(mDialogText);
		// 初始化数据
		List<Content> list = new ArrayList<Content>();
		getDataIntoList(list);
		// 根据a-z进行排序
		Collections.sort(list, new PinyinComparator());
		// 实例化自定义内容适配类
		NameAdapter adapter = new NameAdapter(this, list);
		// 为listView设置适配
		mListView.setAdapter(adapter);
		// 设置SideBar的ListView内容实现点击a-z中任意一个进行定位
		indexBar.setListView(mListView);
	}

	/**
	 * 将内容填充到list中
	 * 
	 * @param list
	 */
	public void getDataIntoList(List<Content> list) {
		String[] array = getResources().getStringArray(R.array.default_smiley_texts);
		for (int i = 0; i < array.length; i++) {
			String string = PinyinLetterHelper.getPinyinFirstLetter(array[i]);
			list.add(new Content(string, array[i]));
		}
	}

}


本项目所有代码和资源zip包,可以直接导入Eclipse:

http://download.csdn.net/detail/lp8800/5872481

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以给你提供一个简单的思路来实现类似微信通讯录的界面,使用 Vue 和 CSS 来实现。 首先,你需要创建一个 Vue 组件来表示通讯录界面。在这个组件中,你可以使用 Vue 的 data 属性来存储通讯录中的联系人信息,例如姓名、头像、电话号码等。 然后,你可以使用 CSS 来布局和样式化你的通讯录界面。你可以使用 flexbox 布局来创建一个列布局,每列表示一个字母,用于分组显示联系人。你可以使用 CSS 样式来设计每个联系人的卡片,包括头像、姓名和电话号码等信息。 为了实现搜索功能,你可以在组件中添加一个搜索框,使用 Vue 的 computed 属性来过滤联系人信息,只显示与搜索关键字匹配的联系人。 最后,你可以添加一些交互效果,例如当用户点击一个联系人时,该联系人的详细信息会在侧边栏中显示出来。 以下是一个简单的代码示例,用于实现类似微信通讯录的界面: ``` <template> <div class="contacts"> <div class="search"> <input type="text" v-model="searchKeyword" placeholder="Search..."> </div> <div class="columns"> <div v-for="letter in letters" :key="letter" class="column"> <div class="letter">{{ letter }}</div> <div v-for="contact in filteredContacts" :key="contact.id" class="contact"> <img :src="contact.avatar" alt="Avatar"> <div class="info"> <div class="name">{{ contact.name }}</div> <div class="phone">{{ contact.phone }}</div> </div> </div> </div> </div> <div v-if="selectedContact" class="sidebar"> <img :src="selectedContact.avatar" alt="Avatar"> <div class="info"> <div class="name">{{ selectedContact.name }}</div> <div class="phone">{{ selectedContact.phone }}</div> </div> </div> </div> </template> <script> export default { data() { return { contacts: [ { id: 1, name: 'Alice', phone: '123-456-7890', avatar: 'https://randomuser.me/api/portraits/women/1.jpg' }, { id: 2, name: 'Bob', phone: '456-789-0123', avatar: 'https://randomuser.me/api/portraits/men/2.jpg' }, { id: 3, name: 'Charlie', phone: '789-012-3456', avatar: 'https://randomuser.me/api/portraits/men/3.jpg' }, // ... ], selectedContact: null, searchKeyword: '' } }, computed: { letters() { const letters = new Set() this.contacts.forEach(contact => { letters.add(contact.name.charAt(0).toUpperCase()) }) return Array.from(letters).sort() }, filteredContacts() { if (!this.searchKeyword) { return this.contacts } const keyword = this.searchKeyword.toLowerCase() return this.contacts.filter(contact => { return contact.name.toLowerCase().indexOf(keyword) !== -1 }) } }, methods: { selectContact(contact) { this.selectedContact = contact } } } </script> <style> .contacts { display: flex; flex-direction: row; justify-content: center; align-items: center; height: 100vh; font-size: 16px; } .search { margin-bottom: 16px; } .columns { display: flex; flex-direction: row; justify-content: center; align-items: flex-start; flex-wrap: wrap; width: 80%; } .column { display: flex; flex-direction: column; margin-right: 16px; } .letter { font-weight: bold; margin-bottom: 8px; } .contact { display: flex; flex-direction: row; align-items: center; margin-bottom: 8px; cursor: pointer; } .contact:hover { background-color: #eee; } .contact img { width: 48px; height: 48px; border-radius: 50%; margin-right: 16px; } .info { display: flex; flex-direction: column; } .name { font-weight: bold; } .phone { color: #888; } .sidebar { position: fixed; top: 0; right: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 20%; height: 100%; background-color: #f7f7f7; box-shadow: 0 0 16px rgba(0, 0, 0, 0.1); } .sidebar img { width: 128px; height: 128px; border-radius: 50%; margin-bottom: 16px; } .sidebar .info { display: flex; flex-direction: column; align-items: center; } .sidebar .name { font-size: 24px; font-weight: bold; margin-bottom: 8px; } .sidebar .phone { color: #888; } </style> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值