Android高级索引以及搜索使用场景

来源:互联网 发布:淘宝外包客服工资 编辑:程序博客网 时间:2024/06/08 14:29

首先给大家上效果图


涉及的技术包括以下三点

绘制右侧的条目

点击某个字母定位到当前listview的position Android SearchView实现

1、涉及到的类:SectionIndexer

方法介绍

  • object[] getSections();

    获取一个数组,存的是索引的列表,其是将每一个数组单独作为一个数组元素存储的。

  • int getPositionForSection(int sectionIndex);

    根据右侧的索引字母获得position(ListView的每个条目位置)。

  • int getSectionForPosition(int postion);


2、实现步骤和方法

IndexableListViewActivity类
package com.android.xiong.gridlayoutTest;
import java.util.ArrayList;
import java.util.Collections;


import com.android.xiong.gridlayoutTest.customeview.IndexableListView;
import com.android.xiong.gridlayoutTest.util.StringMatcher;


import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import android.widget.SearchView;
import android.widget.SectionIndexer;
import android.widget.Toast;
import android.widget.SearchView.OnQueryTextListener;


public class IndexableListViewActivity extends Activity {
/**
* 1初始化item 根据selection获取position
*/
private SearchView sv;
private ArrayList<String> mItem;
private IndexableListView mListView;


@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
sv = (SearchView) findViewById(R.id.sv);
mListView = (IndexableListView) findViewById(R.id.listview);
mListView.setTextFilterEnabled(true);
// 设置文本过滤
sv.setIconifiedByDefault(false);
// 设置默认是否为缩小图标
sv.setSubmitButtonEnabled(true);
// 显示搜索按钮
sv.setQueryHint("查找");
sv.setOnQueryTextListener(new OnQueryTextListener() {


@Override
public boolean onQueryTextSubmit(String query) {
Toast.makeText(IndexableListViewActivity.this,
"你选择的是:" + query.toString(), Toast.LENGTH_SHORT).show();


return false;
}


@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
// 如果query不为零的字符串
if (TextUtils.isEmpty(newText)) {
// 清除listview的过滤
mListView.clearTextFilter();
} else {
// 使用listview输入文本进行过滤
mListView.setFilterText(newText);
}
return true;
}
});


               //添加item条目
mItem = new ArrayList<String>();
mItem.add("1234567");
mItem.add("A ASF1234567");
mItem.add("B DASD1234567");
mItem.add("C ipui1234567");
mItem.add("D qwwe1234567");
mItem.add("E ryhr1234567");
mItem.add("F asfdf1234567");
mItem.add("G hk,jmj1234567");
mItem.add("H aasff1234567");
mItem.add("L fhn1234567");
mItem.add("J asdfg1234567");


mItem.add("K asf1234567");
mItem.add("L sfaa1234567");
mItem.add("M uoou1234567");
mItem.add("N hkhk1234567");
mItem.add("O sfg1234567");
mItem.add("P ree1234567");
mItem.add("Q ere123erer4567");
mItem.add("R 123ere4567");
mItem.add("S 123er4efer567");
mItem.add("T 1234567");
mItem.add("U 123b4567");
mItem.add("V 1234567");
mItem.add("W 1ASS234567");
mItem.add("X 1234567");
mItem.add("Y 1234567");
mItem.add("Z 1234567");
// 排序
Collections.sort(mItem);
ContentAdapter adapter = new ContentAdapter(this,
android.R.layout.simple_list_item_1, mItem);


mListView.setAdapter(adapter);
//设置滚动事件
mListView.setFastScrollEnabled(true);
// mListView.set
}
private class ContentAdapter extends ArrayAdapter<String> implements
SectionIndexer {
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";


public ContentAdapter(Context context, int resource,
ArrayList<String> mItem) {
super(context, resource, mItem);
// TODO Auto-generated constructor stub
}


@Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
// 将每个Section作为单元数组放在sections中
for (int i = 0; i < sections.length; i++) {
sections[i] = String.valueOf(mSections.charAt(i));


}
return sections;
}
@Override
public int getPositionForSection(int sectionIndex) {
// Section一直往前查询,直到第一个item位置,否则继续往前不进行定位
for (int i = sectionIndex; i < 0; i--) {
for (int j = 0; j < getCount(); j++) {
// 判断是位置是否为零 查询数字
if (i == 0) {
for (int k = 0; k <= 9; k++) {
// value item的首字母
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(k))) {
// 返回当前索引
return j;
}
}
} else {
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(mSections.charAt(i)))) {
}
}
}
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}
}
}

3、创建StringMatcher类

package com.android.xiong.gridlayoutTest.util;
public class StringMatcher {
/**
     * @param value   item条目中的文字
     * @param keyword 索引列表的字符
     * @return 这两个是否进行匹配
     */
    public static boolean match(String value, String keyword) {
        if (value == null || keyword == null) {
            return false;
        }
        if (keyword.length() > value.length()) {
            return false;
        }
        int i = 0; //value的指针
        int j = 0; //keyword的指针
        do {
            if (keyword.charAt(j) == value.charAt(i)) {
                i++;
                j++;
            } else if (j > 0) {
                //
                break;
            } else {
                i++;
            }
        } while (i < value.length() && j < keyword.length());
    return (j == keyword.length()?true:false);
    }
}

4、创建IndexScroller类

package com.android.xiong.gridlayoutTest.util;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.SectionIndexer;

public class IndexScroller {
 private float mIndexbarWidth;   //索引条的宽度(索引条是直到显示完为止,所以不需要高度)
   private float mIndexbarMargin;  //索引条距离右侧边缘的距离
   private float mPreviewPadding;  //在中心显示的预览文本到四周的距离
   private float mDensity;         //320/160 = 2 ,这代表这个2,指的当前屏幕密度/160
   private float mScaledDensity;   //指的当前屏幕密度/160(设置字体的尺寸)
   private float mAlphaRate;       //透明度(用于显示和隐藏索引条)0~1
   private int mState = STATE_HIDDEN;//索引条的状态
   private int mListViewWidth;
   private int mListViewHeight;
   private int            mCurrentSection = -1;//
   private boolean        mIsIndexing     = false;
   private ListView       mListView       = null;
   private SectionIndexer mIndexer        = null;
   private String[]       mSections       = null;//索引条的文本
   private RectF mIndexbarRect;    //表示整个索引条的区域


   private static final int STATE_HIDDEN  = 0;
   private static final int STATE_SHOWING = 1;
   private static final int STATE_SHOWN   = 2;
   private static final int STATE_HIDING  = 3;


   public IndexScroller(Context context, ListView listview) {
       //获取屏幕密度的比值
       mDensity = context.getResources().getDisplayMetrics().density;
       mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
       mListView = listview;
       setAdapter(mListView.getAdapter());
       //根据屏幕密度计算索引条的高度(单位:像素)
       mIndexbarWidth = 20 * mDensity;
       mIndexbarMargin = 10 * mDensity;
       mPreviewPadding = 5 * mDensity;
   }


   public void setAdapter(Adapter adapter) {
       if (adapter instanceof SectionIndexer) {
           mIndexer = (SectionIndexer) adapter;
           mSections = (String[]) mIndexer.getSections();
       }
   }


   public void draw(Canvas canvas) {
       // 1、绘制索引条、包括索引条的背景和文本
       // 2、绘制预览文本和背景
       // 如果索引条是隐藏状态,则不进行绘制
       if (mState == STATE_HIDDEN) {
           return;
       }


       // 设置索引条背景的绘制属性
       Paint indexbarPaint = new Paint();
       indexbarPaint.setColor(Color.BLACK);
       indexbarPaint.setAlpha((int) (64 * mAlphaRate));


       //绘制索引条(4个角都是圆角的矩形区域)
       canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);


       //绘制Section
       if (mSections != null && mSections.length > 0) {
           //绘制预览文本和背景
           if (mCurrentSection >= 0) {
               //预览的背景
               Paint previewPaint = new Paint();
               previewPaint.setColor(Color.BLACK);
               previewPaint.setAlpha(96);
               //预览的文本
               Paint previewTextPaint = new Paint();
               previewTextPaint.setColor(Color.WHITE);
               previewTextPaint.setTextSize(50 * mScaledDensity);
               //测量文本的宽度
               float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
               //预览文本高度=文本上边距+文本下边距+文字高度
               //TODO:注1
               float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
               //预览文本背景区域 (预览背景左侧到listview左侧的距离)
               RectF previewRect = new RectF((mListViewWidth - previewSize) / 2, (mListViewHeight - previewSize) / 2, (mListViewWidth - previewSize) / 2 + previewSize, (mListViewHeight - previewSize) / 2 + previewSize);


               //绘制背景
               canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
               //绘制预览文本
               canvas.drawText(mSections[mCurrentSection], previewRect.left + (previewSize - previewTextWidth) / 2 - 1, previewRect.top + mPreviewPadding - previewTextPaint.ascent() + 1, previewTextPaint);


           }
       }


       //设置索引的绘制属性
       Paint indexPaint = new Paint();
       indexbarPaint.setColor(Color.WHITE);
       indexbarPaint.setAlpha((int) (255 * mAlphaRate));
       indexbarPaint.setTextSize(12 * mScaledDensity);
       float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length;
       float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;


       for (int i = 0; i < mSections.length; i++) {
           float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2;
           //绘制索引条上的文字
           canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft, mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
       }
   }


   public void onSizeChanged(int w, int h, int oldw, int oldh) {
       mListViewWidth = w;
       mListViewHeight = h;
       mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w - mIndexbarMargin, h - mIndexbarMargin);
   }


   private void fade(long delay) {
       mHandler.removeMessages(0);
       mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
   }


   private Handler mHandler = new Handler() {
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (mState) {
               case STATE_SHOWING:
                   mAlphaRate += (1 - mAlphaRate) * 0.2;
                   if (mAlphaRate > 0.9) {
                       mAlphaRate = 1;
                       setState(STATE_SHOWN);
                   }
                   mListView.invalidate();
                   fade(10);
                   break;
               case STATE_SHOWN:
                   setState(STATE_HIDING);
                   break;
               case STATE_HIDING:
                   mAlphaRate -= mAlphaRate * 0.2;
                   if (mAlphaRate < 0.1) {
                       mAlphaRate = 0;
                       setState(STATE_HIDDEN);
                   }
                   mListView.invalidate();
                   fade(10);
                   break;
           }
       }
   };


   private void setState(int state) {
       if (state < STATE_HIDDEN || state > STATE_HIDING) {
           return;
       }
       mState = state;
       switch (mState) {
           case STATE_HIDDEN:
               mHandler.removeMessages(0);
               break;
           case STATE_SHOWING:
               mAlphaRate = 0;
               fade(0);
               break;
           case STATE_SHOWN:
               mHandler.removeMessages(0);
               break;
           case STATE_HIDING:
               mAlphaRate = 1;
               fade(3000);
               break;
       }
   }


   public boolean onTouchEvent(MotionEvent ev) {
       switch (ev.getAction()) {
           case MotionEvent.ACTION_DOWN:
               if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) {
                   setState(STATE_SHOWN);


                   mIsIndexing = true;
                   //通过触摸点获取当前的section的索引
                   mCurrentSection = getSectionByPoint(ev.getY());
                   //将listview定位到指定的item
                   mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
                   return true;
               }
               break;
           case MotionEvent.ACTION_MOVE:
               if (mIsIndexing) {
                   if (contains(ev.getX(), ev.getY())) {
                       mCurrentSection = getSectionByPoint(ev.getY());
                       mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
                   }
                   return true;
               }
               break;
           case MotionEvent.ACTION_UP:
               if (mIsIndexing) {
                   mIsIndexing = false;
                   mCurrentSection = -1;
               }
               if (mState == STATE_SHOWN) {
                   setState(STATE_HIDING);
               }
               break;
       }
       return false;
   }


   public boolean contains(float x, float y) {
       // Determine if the point is in index bar region, which includes the right margin of the bar
       return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height());
   }


   private int getSectionByPoint(float y) {
       if (mSections == null || mSections.length == 0)
           return 0;
       if (y < mIndexbarRect.top + mIndexbarMargin)
           return 0;
       if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin)
           return mSections.length - 1;
       return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length));
   }



          //索引条目的显示
   public void show() {
       if (mState == STATE_HIDDEN)
           setState(STATE_SHOWING);
       else if (mState == STATE_HIDING)
           setState(STATE_HIDING);
   }

         //索引条目的隐藏
   public void hide() {
       if (mState == STATE_SHOWN)
           setState(STATE_HIDING);
   }
}

5、最后一步IndexableList集成ListView

package com.android.xiong.gridlayoutTest.customeview;

import com.android.xiong.gridlayoutTest.util.IndexScroller;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ListAdapter;
import android.widget.ListView;

public class IndexableListView extends ListView {
   private boolean         mIsFastScrollEnable = false;
   //负责绘制索引条
   private IndexScroller   mScroller           = null;
   private GestureDetector mGestureDetector    = null;//手势检测器
   public IndexableListView(Context context) {
       super(context);
   }
   public IndexableListView(Context context, AttributeSet attrs) {
       super(context, attrs);
   }
   public boolean isFastScrollEnabled() {
       return mIsFastScrollEnable;
   }
          @Override
   public void setFastScrollEnabled(boolean enabled) {
       mIsFastScrollEnable = enabled;
       //如果允许FastScroll
       if (mIsFastScrollEnable) {
           if (mScroller == null) {
               mScroller = new IndexScroller(getContext(), this);
           }
       } else {
           if (mScroller != null) {
               //如果页数不多,就不显示快速滑动的索引条
               mScroller.hide();
               mScroller = null;
           }
       }
   }
   @Override
   public void draw(Canvas canvas) {
       super.draw(canvas); //绘制listview原本的item
       if (mScroller != null) {
           //绘制右侧的索引条
           mScroller.draw(canvas);
       }
   }


   @Override
   public boolean onTouchEvent(MotionEvent ev) {
       //如果mScroller自己来处理触摸事件,该方法返回true。
       //如果用户手指点到了右侧索引条,就由它自己来处理
       if (mScroller != null && mScroller.onTouchEvent(ev)) {
           return true;
       }
       if (mGestureDetector == null) {
           //使用手势处理触摸事件
           mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
               @Override
               public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                   //直接显示右侧的索引条
                   mScroller.show();
                   return super.onFling(e1, e2, velocityX, velocityY);
               }
           });
       }
       mGestureDetector.onTouchEvent(ev);
       return super.onTouchEvent(ev);
   }


   @Override
   public void setAdapter(ListAdapter adapter) {
       super.setAdapter(adapter);
       if (mScroller != null) {
           mScroller.setAdapter(adapter);
       }
   }


   @Override
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
       super.onSizeChanged(w, h, oldw, oldh);
       if (mScroller != null) {
           //当屏幕方向发生改变,索引条也要相应改变
           mScroller.onSizeChanged(w, h, oldw, oldh);
       }
   }
}
注解:
画布上绘制一个文本,有一条基准线:baseline,往上是负,往下是正。 
()就是baseline到descender line的距离(正值)。 
ascent()就是baseline到ascender line的距离(负值)。 
所以一个文本的高度就是:paint.descent()-paint.ascent()。










0 0
原创粉丝点击