先看效果:
1、自定义一个索引view,就是右边红色的部分:
package com.example.contact; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by yang on 2017/7/8. */ public class IndexView extends View { private String Textarr[]; private int TextX; private int TextY; private int itemwidth; private int itemheight; private Paint paint; private Context Mycontext; private int currentindex=-1; public IndexView(Context context, AttributeSet attrs) { super(context, attrs); this.Mycontext=context; initdata(); } private void initdata() { Textarr=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"}; paint=new Paint(); paint.setColor(Color.WHITE); paint.setTextSize(20); paint.setTypeface(Typeface.DEFAULT_BOLD); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i=0;i<Textarr.length;i++){ initdata(); String word=Textarr[i]; if (i==currentindex){ paint.setColor(Color.RED); } Rect rect=new Rect(); //返回边界(由调用者分配的)最小的矩形,它包含所有的字符,在(0,0)处有一个隐含的原点。 paint.getTextBounds(word,0,1,rect); int rectwidth=rect.width(); int rectheight=rect.height(); TextX=(itemwidth-rectwidth)/2; TextY=itemheight-rectheight/2+i*itemheight; canvas.drawText(word,TextX,TextY,paint); } } private int startX; private int startY; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: startX= (int)event.getX(); startY= (int) event.getY(); int index=(int)startY/itemheight; currentindex=index; OnindexchangeListener.change(Textarr[currentindex]); invalidate(); break; case MotionEvent.ACTION_UP: currentindex=-1; invalidate(); } return true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); itemwidth=getMeasuredWidth(); itemheight=getMeasuredHeight()/Textarr.length; } public interface OnindexchangeListener{ /* 当当前索引改变的时候调用 */ void change(String text); } private OnindexchangeListener OnindexchangeListener; public void setOnindexchangeListener(IndexView.OnindexchangeListener onindexchangeListener) { OnindexchangeListener = onindexchangeListener; } }
自定义联系人条目的view,效果如下:
package com.example.contact; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; /** * Created by yang on 2017/7/8.
这里要继承relativelayout或framelayout */ public class listviewitem extends FrameLayout { private String TAG=getClass().getSimpleName(); private View contentview; private View deleteview; private int deletewidth; private int contentwidth; private int contentheight; private boolean isshow=false; public listviewitem(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { contentview=getChildAt(0); deleteview=getChildAt(1); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); contentview.layout(0,0,getWidth(),getHeight()); deleteview.layout(getWidth(),0,getWidth()+deletewidth,getHeight()); } int startX; int startY; int downx; int downy; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: downx=startX= (int) event.getX(); downy=startY= (int) event.getY(); break; case MotionEvent.ACTION_MOVE: int endx= (int) event.getX(); int endy= (int) event.getY(); int dx=endx-startX; int toscrollx=getScrollX()-dx; if (toscrollx<0){ toscrollx=0; } else if (toscrollx > deletewidth) { toscrollx=deletewidth; } // Log.d(TAG, "onTouchEvent: startX="+startX+"endx="+endx); scrollTo(toscrollx,getScrollY()); startX= (int) event.getX(); startY= (int) event.getY(); int DX=Math.abs(endx-downx); int DY=Math.abs(endy-downy); if (DX>DY&&DX>10){ getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: int upx= (int) event.getX(); int upy= (int) event.getY(); if ((upx-downx)>deletewidth/2){ //隐藏 hintdelete(); }else if ((upx-downx)<-(deletewidth/2)){ showdelete(); } else { //回弹 if (isshow){ showdelete(); } else { hintdelete(); } } break; } return true; } int StartX=0; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercep=false; super.onInterceptTouchEvent(ev); switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: if (OnitemstatechangeListener!=null){ OnitemstatechangeListener.onitemstatechange(listviewitem.this); } downx= StartX= (int) ev.getX (); break; case MotionEvent.ACTION_MOVE: int endx= (int) ev.getX(); int DX=Math.abs(endx-StartX); Log.d(TAG, "onInterceptTouchEvent: DX="+DX); if (DX>10){ intercep=true; } break; case MotionEvent.ACTION_UP: break; } return intercep; } public void showdelete() { isshow=false; scrollTo(deletewidth,0); OnitemstatechangeListener.open(listviewitem.this); invalidate(); } public void hintdelete() { isshow=false; scrollTo(0,0); OnitemstatechangeListener.close(listviewitem.this); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); deletewidth=deleteview.getMeasuredWidth(); contentwidth=contentview.getMeasuredWidth(); contentheight=getMeasuredHeight(); } interface OnitemstatechangeListener{ void onitemstatechange(View v); void close(View v); void open(View v); } private OnitemstatechangeListener OnitemstatechangeListener; public void setOnitemstatechangeListener(listviewitem.OnitemstatechangeListener onitemstatechangeListener) { OnitemstatechangeListener = onitemstatechangeListener; } }
上主类
package com.example.contact; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; public class MainActivity extends AppCompatActivity { private TextView tv_1; private IndexView indexView; private ListView listView; private ArrayListlist; private listviewitem listviewitem; private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { tv_1.setVisibility(View.GONE); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initview(); initdata(); initlistener(); } private void initlistener() { listView.setAdapter(new Myadpter()); indexView.setOnindexchangeListener(new IndexView.OnindexchangeListener() { @Override public void change(String text) { int movetoposition=movetoitem(text); listView.setSelection(movetoposition); handler.removeCallbacksAndMessages(null); tv_1.setText(text); tv_1.setVisibility(View.VISIBLE); handler.sendEmptyMessageDelayed(0,2000); } }); } private int movetoitem(String text) { int near=0; for (int i=0;i<list.size();i++){ String newtext=list.get(i).getPinyin().substring(0,1); if (newtext.compareTo(text)<=0){ near=i; if (text.equals(newtext)){ return i; } } else { return near+1; } } return near+1; } private class Myadpter extends BaseAdapter{ @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(final int position, View convertView, ViewGroup parent) { Viewholder viewhloder; View view; if (convertView==null){ view=View.inflate(MainActivity.this,R.layout.mylistview,null); viewhloder=new Viewholder(); viewhloder.content= (TextView) view.findViewById(R.id.contect_item); viewhloder.meun= (TextView) view.findViewById(R.id.tv_menu); viewhloder.delete= (TextView) view.findViewById(R.id.delete_item); viewhloder.listviewitem1= (com.example.contact.listviewitem) view.findViewById(R.id.mylistview); view.setTag(viewhloder); }else{ view=convertView; viewhloder= (Viewholder) view.getTag(); } viewhloder.content.setText(list.get(position).getname()); String code=list.get(position).getPinyin().substring(0,1); viewhloder.meun.setText(code); if (position==0) { viewhloder.meun.setVisibility(View.VISIBLE); }else { String currentmenu=list.get(position-1).getPinyin().substring(0,1); if (currentmenu.equals(code)){ viewhloder.meun.setVisibility(View.GONE); }else { viewhloder.meun.setVisibility(View.VISIBLE); } } viewhloder.delete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { listviewitem l= (com.example.contact.listviewitem) v.getParent(); l.hintdelete(); list.remove(position); notifyDataSetChanged(); } }); viewhloder.content.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,"position="+position,Toast.LENGTH_SHORT).show(); } }); listviewitem mylistviewitem=viewhloder.listviewitem1; mylistviewitem.setOnitemstatechangeListener(new listviewitem.OnitemstatechangeListener() { @Override public void onitemstatechange(View v) { if (v!=listviewitem&&listviewitem!=null){ listviewitem.hintdelete(); } } @Override public void close(View v) { listviewitem=null; } @Override public void open(View v) { listviewitem= (com.example.contact.listviewitem) v; } }); return view; } class Viewholder{ private TextView content; private TextView meun; private TextView delete; private listviewitem listviewitem1; } } private void initdata() { list=new ArrayList<>(); list.add(new people("张晓飞")); list.add(new people("杨光福")); list.add(new people("胡继群")); list.add(new people("刘畅")); list.add(new people("钟泽兴")); list.add(new people("尹革新")); list.add(new people("安传鑫")); list.add(new people("张骞壬")); list.add(new people("温松")); list.add(new people("李凤秋")); list.add(new people("刘甫")); list.add(new people("娄全超")); list.add(new people("张猛")); list.add(new people("王英杰")); list.add(new people("李振南")); list.add(new people("孙仁政")); list.add(new people("唐春雷")); list.add(new people("牛鹏伟")); list.add(new people("姜宇航")); list.add(new people("刘挺")); list.add(new people("张洪瑞")); list.add(new people("张建忠")); list.add(new people("侯亚帅")); list.add(new people("刘帅")); list.add(new people("乔竞飞")); list.add(new people("徐雨健")); list.add(new people("吴亮")); list.add(new people("王兆霖")); list.add(new people("阿三")); list.add(new people("李博俊")); //根据拼音首字母大小排序 Collections.sort(list, new Comparator () { @Override public int compare(people p1, people p2) { return (p1.getPinyin().substring(0,1)).compareTo(p2.getPinyin().substring(0,1)); } }); } private void initview() { tv_1= (TextView) findViewById(R.id.tv_1); indexView= (IndexView) findViewById(R.id.iv_1); listView = (ListView) findViewById(R.id.lv_main); tv_1.setVisibility(View.GONE); tv_1.setAlpha((float) 0.8); } }
新建个Javabean类转成拼音
package com.example.contact; /** * Created by yang on 2017/7/8. */ public class people { private String name; private String pinyin; public people(String name) { this.name = name; } public String getPinyin() { pinyin=PinYinUtils.getPinYin(name); return pinyin; } public String getname() { return name; } }
转成拼音的jar包 pinyin4j.jar
package com.example.contact; 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.exception.BadHanyuPinyinOutputFormatCombination; /** * 作者:杨光福 on 2016/4/14 13:57 * 微信:yangguangfu520 * QQ号:541433511 * 作用:把汉字转换成拼音 * 阿福 * AFU */ public class PinYinUtils { /** * 得到指定汉字的拼音 * 注意:不应该被频繁调用,它消耗一定内存 * @param hanzi * @return */ public static String getPinYin(String hanzi){ String pinyin = ""; HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制转换是否大小写,是否带音标 format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大写 format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); //由于不能直接对多个汉字转换,只能对单个汉字转换 char[] arr = hanzi.toCharArray(); for (int i = 0; i < arr.length; i++) { if(Character.isWhitespace(arr[i]))continue;//如果是空格,则不处理,进行下次遍历 //汉字是2个字节存储,肯定大于127,所以大于127就可以当为汉字转换 if(arr[i]>127){ try { //由于多音字的存在,单 dan shan String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format); if(pinyinArr!=null){ pinyin += pinyinArr[0]; }else { pinyin += arr[i]; } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); //不是正确的汉字 pinyin += arr[i]; } }else { //不是汉字, pinyin += arr[i]; } } return pinyin; } }
上布局:
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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.contact.MainActivity"> <ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="wrap_content"> ListView> <TextView android:layout_centerInParent="true" android:id="@+id/tv_1" android:layout_width="80dp" android:layout_height="80dp" android:background="#22000000" android:textColor="#000000" android:text="A" android:gravity="center" android:textSize="50sp" /> <com.example.contact.IndexView android:id="@+id/iv_1" android:layout_width="30dp" android:layout_height="wrap_content" android:background="#ee0000" android:layout_alignParentRight="true" /> RelativeLayout>
、、、、、、、、、、、、、、、、、、、、、、
xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#000000" android:paddingLeft="5dp" android:textSize="20sp" android:padding="3dp" > TextView>
、、、、、、、、、、、、、、、、、、、
xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Delete" android:textSize="20sp" android:textColor="#ff0000" android:padding="3dp" android:gravity="center" > TextView>
、、、、、、、、、、、、、、、、、、、、、、、
xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_menu" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" android:background="#44000000" android:textColor="#000000" android:text="A" /> <com.example.contact.listviewitem android:id="@+id/mylistview" android:layout_below="@id/tv_menu" android:layout_width="match_parent" android:layout_height="wrap_content"> <include android:id="@+id/contect_item" layout="@layout/content"> include> <include android:id="@+id/delete_item" layout="@layout/delte"> include> com.example.contact.listviewitem> RelativeLayout>
里面有很多自定义接口的知识,还有拦截与反拦截机制
拦截 return TRUE的时候会拦截事件 会直接把事件给ontouch事件:
public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercep=false; super.onInterceptTouchEvent(ev); switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: if (OnitemstatechangeListener!=null){ OnitemstatechangeListener.onitemstatechange(listviewitem.this); } downx= StartX= (int) ev.getX (); break; case MotionEvent.ACTION_MOVE: int endx= (int) ev.getX(); int DX=Math.abs(endx-StartX); Log.d(TAG, "onInterceptTouchEvent: DX="+DX); if (DX>10){ intercep=true; } break; case MotionEvent.ACTION_UP: break; } return intercep; }
反拦截--反拦截大于拦截
getParent().requestDisallowInterceptTouchEvent(true);
一般会拦截下来给自己处理,如果被父级拦截了要用反拦截
或者自己处理拦截事件,在特定的情况下给自己的孩子处理