Android ListView A-Z侧边栏字母排序,点击处扭曲成半圆

最近在做通讯录看到360通讯录和索尼系统内置通讯录有这个效果,就是点击联系人列表的A-Z侧边栏时,A-Z栏的被触摸处会扭曲成半圆,半圆可以随着手指在A-Z移动。索尼手机的比较高级,扭曲和恢复的过程有阻尼效果,360的没有任何效果就一伸一缩。所本人实现了个360效果一摸一样的,A-Z侧边栏。

    一.实现原理讲解:

我们知道正常的A-Z栏每个字母的坐标的Y值应该是一样的(起码差不多一样),所以绘制出来的时候三竖直的。因此我只要按照如下步骤,就可以实现图中的效果:

1.当A-Z栏被触摸时,通过setLayoutParams()方法改变他的宽度(本列为100dp)使得它有足够的空间显示绘制的半圆效果(本例正常状态的宽度为30dp)。这里需要注意的是,当setLayoutParams()调用后,我们要保持A-Z栏在屏幕的位置不变,就要根据情况重新计算每个字母的坐标。

2.在宽度调好之后,要获取当前被触摸的字母的在A-Z在竖直线上的坐标(既途中M水平线和A-Z竖直线的交点)作为扭曲半圆的圆心,如图当前被触摸的为M,那么圆心在A-Z栏没被扭曲时M的位置。

3.在确定好圆心之后,我们要求半径。如图,我们把7个字母绘制在了半圆上,那么A-Z栏竖直线的空白处长度就为,半圆的直径。可以轻易的看出直径是7个字母的长度,我们可以很容易的计算出每个字母的高度。再次我们获得了半径的值

4.求这七个字母的坐标。首先, 来理一理Android的角度问题,图中Q所在角度为90,M为180,I为270;得出每个字母的偏移角度分别为:J -- 247.5, K -- 225, L --202.5,

M -- 180, N -- 157.5, O -- 135, P -- 112.5。

在获得半径radius, 角度arc ,和圆心坐标O(centerX, centerY)后通过如下公式就可以球道这七个字母的坐标

x = (float) (centerX + radius * Math.cos(arc * Math.PI / 180));

y = (float) (centerY + radius * Math.sin(arc * Math.PI / 180));

      二.整个代码(尊重别人劳动成果,转载请注明 处(http://blog.csdn.net/u010949962/article/details/42198235)):

    

package xu.ye.view.ui;

import java.util.HashMap;

import xu.ye.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * @author  huangxin
 */
public class QuickAlphabeticBar extends ImageButton {

	public static final String TAG = "QuickAlphabeticBar";

	public static final int DEFAULT_SCREEN_HEIGHT = 800;

	public static final int DEFAULT_TEXT_SIZE = 20;

	private TextView mDialogText;
	private Handler mHandler;
	private ListView mList;
	private float mHight;

	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 HashMap<String, Integer> alphaIndexer;

	private float centerXY[] = null;

	private int singleHeight;

	private int height;

	private int width;

	private float arc = 0;

	private float radius;

	private float normalWidth;
	private float selectedWidth;
	private boolean isselectedState = false;

	private int textSize;

	private int startPos = -1;
	private int endPos = -1; 

	private Context context;

	private float measureTextSize = -1;

	private int screenWidth;

	private int screenHeight;

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

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

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

	public void setScreenWidth(int screenWidth) {
		this.screenWidth = screenWidth;
	}

	public void setScreenHeight(int screenHeight) {
		this.screenHeight = screenHeight;
	}

	private void init(Context context){
		this.context = context;
		//textSize = getTextSizeFormRatio();
		textSize = 10;
		normalWidth = getResources().getDimensionPixelSize(R.dimen.azbar_normal_width);
		selectedWidth = getResources().getDimensionPixelSize(R.dimen.azbar_selected_width);
	}

	public void init(Activity ctx) {
		mDialogText = (TextView) ctx.findViewById(R.id.fast_position);
		mDialogText.setVisibility(View.INVISIBLE);
		mHandler = new Handler();
	}

	public void setListView(ListView mList) {
		this.mList = mList;
	}

	public void setAlphaIndexer(HashMap<String, Integer> alphaIndexer) {
		this.alphaIndexer = alphaIndexer;
	}

	public void setHight(float mHight) {
		this.mHight = mHight;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) 	{
		int act = event.getAction();
		float y = event.getY();
		float x = event.getX();
		final int oldChoose = choose;

		singleHeight = height / letters.length;
		radius = 8 * singleHeight / 2;

		int selectIndex = (int) (y / (mHight / letters.length));
		if (selectIndex > -1 && selectIndex < letters.length) {
			String key = letters[selectIndex];
			if (alphaIndexer.containsKey(key)) {
				int pos = alphaIndexer.get(key);
				if (mList.getHeaderViewsCount() > 0) {
					this.mList.setSelectionFromTop(
							pos + mList.getHeaderViewsCount(), 0);
				} else {
					this.mList.setSelectionFromTop(pos, 0);
				}
			}

			mDialogText.setText(letters[selectIndex]);
			arc = 0;

			if(!isselectedState){
				isselectedState = true;
				//int selected = (int) (centerXY[0] - (radius + paint.measureText(letters[selectIndex])));
				setLayoutParams((int) selectedWidth);

			}
		}

		switch (act) {
		case MotionEvent.ACTION_DOWN:

			showBkg = true;

			if (oldChoose != selectIndex) {
				if (selectIndex >= 0 && selectIndex < letters.length) {
					choose = selectIndex;
					invalidate();
				}
			}

			if (mHandler != null) {
				mHandler.post(new Runnable() {
					@Override
					public void run() {
						if (mDialogText != null
								&& mDialogText.getVisibility() == View.INVISIBLE) {
							mDialogText.setVisibility(VISIBLE);
						}
					}
				});
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if(x < selectedWidth * 0.5 && isselectedState){
				reseAlphabeticBar();
				return super.onTouchEvent(event);
			}
			if (oldChoose != selectIndex) {
				if (selectIndex >= 0 && selectIndex < letters.length) {
					choose = selectIndex;
					invalidate();
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			reseAlphabeticBar();
			break;
		case MotionEvent.ACTION_CANCEL:
			reseAlphabeticBar();
			break;
		default:
			break;
		}

		return super.onTouchEvent(event);
	}

	private void reseAlphabeticBar(){
		centerXY = null;
		showBkg = false;
		choose = -1;
		isselectedState = false;
		arc = 0;
		setLayoutParams((int) normalWidth);
		if (mHandler != null) {
			mHandler.post(new Runnable() {
				@Override
				public void run() {
					if (mDialogText != null
							&& mDialogText.getVisibility() == View.VISIBLE) {
						mDialogText.setVisibility(INVISIBLE);
						invalidate();
					}
				}
			});
		}
	}

	private void setLayoutParams(int width){
		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(width,
				RelativeLayout.LayoutParams.MATCH_PARENT);
		lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
		lp.addRule(RelativeLayout.BELOW, R.id.acbuwa_topbar);
		setLayoutParams(lp);
	}

	//TODO:
	Paint paint = new Paint();
	boolean showBkg = false;
	int choose = -1;

	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		/*if (showBkg) {//???ñ????????
			canvas.drawColor(Color.parseColor("#b20264"));
		}*/

		height = getHeight();
		width = (int) getWidth();

		getStartAndEndPosFromArg();

		boolean flag = false;
		for (int i = 0; i < letters.length; i++) {
			paint.setColor(getResources().getColor(android.R.color.black));
			paint.setTextSize(textSize);
			Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
			paint.setTypeface(font);
			paint.setAntiAlias(true); 

			measureTextSize = paint.measureText(letters[i]);

			float xPos, yPos;
			if(isselectedState){
				xPos = selectedWidth - normalWidth / 2 -  measureTextSize / 2;
			}else{
				xPos = normalWidth / 2 - measureTextSize / 2;
			}

			yPos = singleHeight * i + singleHeight;

			if (i == choose) {
				paint.setColor(Color.parseColor("#00BFFF"));
				paint.setFakeBoldText(true);
			}

			if((i >= startPos && i <= endPos) && choose != -1 && isselectedState){

				if(centerXY == null){
					centerXY = new float[2];
					centerXY[0] = selectedWidth - normalWidth / 2 - measureTextSize / 2;
					centerXY[1] = singleHeight * choose + singleHeight;
				}

				if (!flag) {
					getStartPosFromArg(startPos);
					flag = true;
				}

				float[] arcXY = getXYFormArg();
				xPos  =  arcXY[0];
				yPos  =  arcXY[1];

				arc = (float) (arc - 22.5);
				int size = getArgLetterTextSize(i);
				paint.setTextSize(size);
			}
			canvas.drawText(letters[i], xPos, yPos, paint);
			paint.reset();
		}
		centerXY = null;
	}

	private void getStartAndEndPosFromArg(){
		if(choose != -1){
			if(choose <= 3){
				startPos = 0;
			}else{
				startPos = choose - 3;
			}

			if(choose - letters.length + 4 <= 0){
				endPos = choose + 3;
			}else{
				endPos = letters.length -1;
			}
		}
	}

	private void getStartPosFromArg(int startPos) {

		if (startPos == choose) {
			arc = 180;
		} else if (startPos + 1 == choose) {
			arc = (float) 202.5;
		} else if (startPos + 2 == choose) {
			arc = 225;
		} else if (startPos + 3 == choose) {
			arc = (float) 247.5;
		}
	}

	private int getArgLetterTextSize(int i){
		if(i == choose){
			return textSize + 8;
		}else if(i + 1 == choose || choose + 1 == i){
			return textSize + 6;
		}else if(i + 2== choose || choose + 2 == i){
			return textSize + 4;
		}else if(i + 3== choose || choose + 3 == i){
			return textSize + 4;
		}
		return textSize;
	}

	private float[] getXYFormArg(){
		float[] xy = new float[2];
		xy[0] = (float) (centerXY[0] + radius * Math.cos(arc * Math.PI / 180));
		xy[1] = (float) (centerXY[1] + radius * Math.sin(arc * Math.PI / 180));
		return xy;
	}

}
时间: 2024-10-13 04:49:52

Android ListView A-Z侧边栏字母排序,点击处扭曲成半圆的相关文章

Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音

转载请注明出处:http://blog.csdn.net/xiaanming/article/details/12684155 前段时间因为换工作的缘故又恰巧碰到国庆节,所以有段时间自己没有更新博客了,过完国庆到新公司报道,感觉还不错,就是现在住的地方离新公司有点远,地铁20站,伤不起啊,我每天早上7点多就要起床,然后屁颠屁颠的去挤地铁上班,晚上下班还要挤地铁,先不说路程远,车费一天就要10几块,我的银子啊,有坐龙华线去上班的深圳程序员不?听说那条线上班高峰期很挤?我没在上班高峰期坐过那趟车,我

Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能

Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能 本文中阐述如何自定义EditText实现搜索框自定义的样式以及挤压字母的思路等 自定义EditText 相关的drawable文件 主界面以及相关的适配器 结果展示 定义要呈现的EditText的样式 public class ClearEditText extends EditText implements OnFocusChangeListener, TextWatcher { /** * 定义删

[Android分享] 【转帖】Android ListView的A-Z字母排序和过滤搜索功能

感谢eoe社区的分享 最近看关于Android实现ListView的功能问题,一直都是小伙伴们关心探讨的Android开发问题之一,今天看到有关ListView实现A-Z字母排序和过滤搜索功能并且实现汉字转成拼音的功能,转帖来和eoe的小伙伴们一同分享下! Android 有关ListView实现A-Z字母排序和过滤搜索功能并且实现汉字转成拼音的功能 我们知道一般我们对联系人,城市列表等实现A-Z的排序,因为联系人和城市列表我们可以直接从数据库中获取他的汉字拼音,而对于一般的数据,我们怎么实现A

Android 联系人字母排序(仿微信)

现在很多APP只要涉及到联系人的界面,几乎都会采取字母排序以及导航的方式.作为程序猿,这种已经普及的需求还是需要学习的,于是小生开始了在网上默默的学习之路,网上学习的资料质量参差不齐,不过也有很不错的文章,文章后面分享给大家.这篇文章,仅是小生在学习之后,自己独立编写与总结吧.废话不多说先上效果图. 从界面上看,整个实现效果有两个重点: 实现字母分类. 实现右侧的字母导航. 我们先一个一个来了解解决方案,再上代码. 实现字母分类: 字母分类又分为三个小要点:一个是将中文转化为拼音,一个是实现按照

列表按照字母排序检索SideBar

项目中要求列表按照ABCD这种字母排序检索的功能,看了大神写的,瞬间崇拜了,接下来借大家参考参考了 首先是自定义view sidebar 1 /** 2 * @author J 3 *一个自定义view 实现a-z的竖直绘制,和监听滑动事件 4 */ 5 public class SideBar extends View { 6 private OnTouchingLetterChangedListener onTouchingLetterChangedListener; 7 public st

联系人列表字母排序索引(三)

也是忙忙碌碌好几天,今天又有时间了,继续这个文章的编写.今天是这篇文章的最后一部分.主要内容包括以下几点: 1.将中文名字转化成拼音,并提取首字母,进行排序. 2.实现分组列表Adapter模板. 3.将列表与索引结合在一起. pinyin4j是一个将中文转化成拼音的高效工具,我的源码中带了这个依赖包.通过这个工具,可以先获取一个中文的拼音. public static String getLetter(String name) { StringBuilder sb = new StringBu

用ItemDecoration实现按字母排序列表

首先看看实现的效果 可以看出要实现上面效果,有三个步骤: 1.汉字转化为拼音,并且根据首字母排序 2.用ItemDecoration实现字母行的显示 3.自定义实现右侧的按字母导航栏 当然重点讲讲ItemDecoration的实现.都知道RecyclerView本身都没有分割线,需要分割线都是在item中画一条线或者使用ItemDecoration来实现分割线.在RecyclerView中我们可以给每一个item都添加ItemDecoration,所以可以自定义ItemDecoration来实现

Android——ListView与适配器

Android--ListView与适配器 1.抽屉布局  Drawer <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.

字母排序

字母排序 对一行字母进行排序,如:读入一行小写字母,然后将这行字母从a到z进行排序. 首先申请一个字符数组a,然后用gets()进行读入. char a[101];//假设读入的字符不超过100个 gets(a); 接下来我们要知道读入的字符串的长度,可以用strlen()来获取字符串的长度.定义一个整型变量len来存储字符串的长度 int len; len = strlen(a); 说明:如果用了strlen()函数,就需要在程序的最开始引入一个头文件 #include <string.h>