在Android程序中如果有很多数据的话(比如通讯录)就需要能够方便地,快速地定位到用户想要的位置。正好Android中有一种能够实现这种功能的UI。想必大家都见过这种UI。
图片有点大(1920*1080),sorry.
这个UI有如下2个要点:
1、右侧的A-Z的布局实现;
2、拼音和汉字的转换;
那么,解决了这两个问题,这个UI就基本上实现了。对于第一个问题,我们使用FrameLayout布局能够比较好的解决问题;而第二个问题,我们需要使用pinyin4j-2.5.0.jar文件,可以从文章后面下载完整的pinyin4j-2.5.0压缩包。
看代码吧:
主布局main.xml文件:(自定义View指定宽高,并添加在布局的右边:android:layout_gravity="right")
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/flParent" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ListView android:id="@+id/lvShow" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <TextView android:id="@+id/tvLetter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/show_head_toast_bg" android:gravity="center" android:maxWidth="80sp" android:minWidth="70dip" android:padding="10dip" android:textColor="#32b6e7" android:textSize="50sp" > </TextView> <com.smiling.mysidebar.MySideBarView android:background="#60606060" android:id="@+id/myView" android:layout_width="30dip" android:layout_height="fill_parent" android:layout_gravity="right" > </com.smiling.mysidebar.MySideBarView> </FrameLayout> </LinearLayout>
package com.smiling.mysidebar; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.TextView; public class MySideBarView extends View{ private String str[]={"#","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 int srcChoose=-1; private boolean showBkg=false; private Paint paint=new Paint(); private OnTouchingLetterChangedListener onTouchingLetterChangedListener; private TextView tv; //private Animation anim; private Context context; public MySideBarView(Context context) { super(context); // TODO 自动生成的构造函数存根 } public MySideBarView(Context context, AttributeSet attrs) { super(context, attrs); // TODO 自动生成的构造函数存根 } public MySideBarView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO 自动生成的构造函数存根 } public void setContext(Context c){ this.context=c; } // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // setMeasuredDimension(width, height); // } protected void onDraw(Canvas c){ //最后调用onDraw(); super.onDraw(c); if(showBkg){ //c.drawColor(Color.RED); } int height=super.getHeight()-super.getHeight()/96; int width=super.getWidth(); //宽度为90pixels,在main.xml中设置的width=30dip; System.out.println("Height="+getHeight()+",Width="+getWidth()); int singleHeight=height/str.length; for(int i=0;i<str.length;i++){ paint.setColor(Color.BLACK); paint.setAntiAlias(true); paint.setTextSize(super.getHeight()/42); if(i==srcChoose){ paint.setColor(Color.RED); paint.setFakeBoldText(true); } float xPos=(width-paint.measureText(str[i]))/2 ; float yPos=singleHeight*i+singleHeight; c.drawText(str[i], xPos, yPos, paint); paint.reset(); } } //============================================================================================================ public boolean dispatchTouchEvent(MotionEvent e){ float y=e.getY(); int oldChoose=srcChoose; //srcChoose==-1; int currentPosition=(int)(y/getHeight()*str.length); System.out.println("CurrentPosition="+currentPosition); switch(e.getAction()){ case MotionEvent.ACTION_DOWN: this.showBkg=true; if(oldChoose != currentPosition && this.onTouchingLetterChangedListener != null){ if(currentPosition >= 0 && currentPosition < str.length){ this.onTouchingLetterChangedListener.onTouchingLetterChanged(str[currentPosition],currentPosition); srcChoose=currentPosition; super.invalidate(); } } break; case MotionEvent.ACTION_MOVE: if(oldChoose != currentPosition && this.onTouchingLetterChangedListener != null){ if(currentPosition >= 0&¤tPosition < str.length){ this.onTouchingLetterChangedListener.onTouchingLetterChanged(str[currentPosition],currentPosition); srcChoose=currentPosition; super.invalidate(); } } break; case MotionEvent.ACTION_UP: showBkg=false; srcChoose=-1; super.invalidate(); tv.setVisibility(View.GONE); break; } return true; } //============================================================================================================ public boolean onTouchEvent(MotionEvent e){ System.out.println("onTouchEvent()"); return super.onTouchEvent(e); } //============================================================================================================ public interface OnTouchingLetterChangedListener{ public void onTouchingLetterChanged(String string,int position); } public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener TCL){ this.onTouchingLetterChangedListener=TCL; } //============================================================================================================== public void setTextView(TextView tv){ this.tv=tv; } public String[] getAtoZAlpha(){ return str; } //================================================================================================================ }PinYin工具类:这个类的主要作用是根据给出的汉字串返回第一个字的首字母的大写。
可以先看看这里的教程:http://blog.csdn.net/hfhwfw/article/details/6030816
package com.smiling.mysidebar; 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 PinYinUtil { public static String getPinYinFirst_u(String inputString) { HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.LOWERCASE); format.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER); format.setVCharType(HanyuPinyinVCharType.WITH_U_UNICODE); char[] input = inputString.trim().toCharArray(); StringBuffer output = new StringBuffer(""); try { for (int i = 0; i < input.length; i++) { if (Character.toString(input[i]).matches("[\u4E00-\u9FA5]+")) { String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format); output.append(temp[0]); output.append(" "); } else output.append(Character.toString(input[i])); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } return output.toString().substring(0, 1).toUpperCase(); }
package com.smiling.mysidebar; import java.util.HashMap; import java.util.Map; import com.smiling.mysidebar.MySideBarView.OnTouchingLetterChangedListener; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.widget.ListView; import android.widget.TextView; public class Main extends Activity implements OnTouchingLetterChangedListener{ private TextView tv; private ListView lv; private MySideBarView myView; private MyBaseAdapter adapter; private Map<String,Integer> map=new HashMap<String,Integer>(); private String str[]={ /*这里自己添加数据吧,我的是通讯录的数据,就不好意思放在这里啦 =.= */ }; private String alphaArray[]; private int touchPosition=-1; public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); super.setContentView(R.layout.main); tv=(TextView)super.findViewById(R.id.tvLetter); lv=(ListView)super.findViewById(R.id.lvShow); myView=(MySideBarView)super.findViewById(R.id.myView); myView.setTextView(tv); myView.setContext(this); myView.setOnTouchingLetterChangedListener(this); lv.setTextFilterEnabled(true); tv.setVisibility(View.INVISIBLE); adapter=new MyBaseAdapter(this,str); lv.setAdapter(adapter); lv.setVerticalScrollBarEnabled(false); alphaArray=myView.getAtoZAlpha(); initMap(); } public void initMap(){ String tempAlpha=""; for(int i=0;i<str.length;i++){ String alpha=PinYinUtil.getPinYinFirst_u(str[i]); if(!alpha.equals(tempAlpha)){ map.put(alpha,i); tempAlpha=alpha; } } System.out.println(map.toString()); } //========================================================================================================== private boolean hasFound=false; public void onTouchingLetterChanged(String string,int position){ this.touchPosition=position; tv.setText(string); tv.startAnimation(AnimationUtils.loadAnimation(this,R.anim.toast_fade_out)); tv.setVisibility(View.VISIBLE); //注意如果使用动画的话,动画结束时控件存在与否和动画开始前存在与否一致// while(this.touchPosition != -1 && hasFound==false) { if(map.containsKey(string)){ lv.setSelection(map.get(string)); hasFound=true; }else{ touchPosition+=1; //这里注意要加1,避免陷入死循环! if(touchPosition < alphaArray.length){ string=alphaArray[touchPosition]; } } } this.hasFound=false; } }
我在实现每个UI项目的时候都能学到好多细节的东西,希望大家也能这样做,会有很多收获的。比如通过这个UI,我们能看到接口内部从注册到回调的这个细节实现。还有,控件如果使用动画的话,动画结束时控件存在与否和动画开始前存在与否一致,等等吧~