标签:
在联系人界面 可以看到这种界面 手指快速滑动右边滑动条时 可以显示相应的字母图标
android里提供了android.widget.SectionIndexer这个接口去实现该效果 可是只能显示字母提示
但我们想实现右图这种效果时 则只能自定义去实现了
先上代码
MainActivity
public class MainActivity extends Activity { /* * * 1 ListView BaseAdapter DataSource 列表 适配器 数据源 * 巨坑 外面只有包上线性布局才会显示 * */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView = (ListView) findViewById(R.id.list); List<Model> models = new ArrayList<Model>(); for (int i = 0; i < 400; i++) { Model model = new Model("key" + i, "2012" + i); models.add(model); } MyAdapter1 adapter = new MyAdapter1(getApplicationContext(), models); listView.setAdapter(adapter); } }
activity_main
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:demoapp="http://schemas.android.com/apk/res/com.example.mylistview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <com.example.mylistview.CustomFastScrollView android:id="@+id/fast_scroll_view" android:layout_width="wrap_content" android:layout_height="0px" android:background="#FFFFFF" android:layout_weight="1" demoapp:overlayHeight="40dp" demoapp:overlayScrollThumbWidth="40dp" demoapp:overlayTextSize="18dp" demoapp:overlayWidth="100dp" > <ListView android:id="@+id/list" android:layout_width="wrap_content" android:layout_height="fill_parent" /> </com.example.mylistview.CustomFastScrollView> </LinearLayout>
Model
public class Model { private String key; // 列表中显示 private String value; // 提示中显示 private String other; public Model(String key, String value) { super(); this.key = key; this.value = value; } /** * @return the key */ public String getKey() { return this.key; } /** * @param key * the key to set */ public void setKey(String key) { this.key = key; } /** * @return the value */ public String getValue() { return this.value; } /** * @param value * the value to set */ public void setValue(String value) { this.value = value; } }
MyAdapter1
public class MyAdapter1 extends BaseAdapter implements SectionIndexer{ private static final String TAG = "MyAdapter"; private List<Model> countries; private Context context; private SectionIndexer sectionIndexer; public MyAdapter1(Context context, List<Model> countries) { this.context = context; this.countries = countries; } @Override public int getCount() { return countries.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // 创建一个LinearLayout,并向其中添加2个组件 LinearLayout line = new LinearLayout(context); line.setOrientation(0); ImageView image = new ImageView(context); image.setImageResource(R.drawable.ic_launcher); TextView text = new TextView(context); text.setText(countries.get(position).getKey()); text.setTextSize(20); text.setTextColor(Color.RED); line.addView(image); line.addView(text); // 返回LinearLayout实例 return line; } @Override public Object[] getSections() { return getSectionIndexer().getSections(); } @Override public int getPositionForSection(int section) { return getSectionIndexer().getPositionForSection(section); } @Override public int getSectionForPosition(int position) { return getSectionIndexer().getSectionForPosition(position); } private SectionIndexer getSectionIndexer() { if (sectionIndexer == null) { sectionIndexer = createSectionIndexer(countries); } return sectionIndexer; } private SectionIndexer createSectionIndexer(List<Model> countries) { return createSectionIndexer(countries, new Function<Model, String>() { @Override public String apply(Model input) { // show just the first letter in the title //return input.getName().substring(0, 1); return input.getValue(); } }); } private SectionIndexer createSectionIndexer(List<Model> countries, Function<Model, String> sectionFunction) { List<String> sections = new ArrayList<String>(); final List<Integer> sectionsToPositions = new ArrayList<Integer>(); final List<Integer> positionsToSections = new ArrayList<Integer>(); for (int i = 0; i < countries.size(); i++) { Model country = countries.get(i); String section = sectionFunction.apply(country); Log.i("TAG1", "section----" + section); if (sections.isEmpty() || !sections.get(sections.size() - 1).equals(section)) { sections.add(section); sectionsToPositions.add(i); } positionsToSections.add(sections.size() - 1); } final String[] sectionsArray = sections.toArray(new String[sections.size()]); return new SectionIndexer() { @Override public Object[] getSections() { return sectionsArray; } @Override public int getSectionForPosition(int position) { return positionsToSections.get(position); } @Override public int getPositionForSection(int section) { return sectionsToPositions.get(section); } }; } }
Function
public interface Function<E,T> { public T apply(E input); }
最后最关键的类
CustomFastScrollView
public class CustomFastScrollView extends FrameLayout implements OnScrollListener, OnHierarchyChangeListener { // how much transparency to use for the fast scroll thumb private static final int ALPHA_MAX = 255; // how long before the fast scroll thumb disappears private static final long FADE_DURATION = 200; private Drawable mCurrentThumb; //滑动条 private Drawable mOverlayDrawable; //提示文字背景框 private int mThumbH; private int mThumbW; private int mThumbY; private RectF mOverlayPos; // custom values I defined private int mOverlayWidth; private int mOverlayHeight; private float mOverlayTextSize; private int mOverlayScrollThumbWidth; private boolean mDragging; private ListView mList; private boolean mScrollCompleted; private boolean mThumbVisible; private int mVisibleItem; private Paint mPaint; private int mListOffset; private Object [] mSections; private String mSectionText; private boolean mDrawOverlay; private ScrollFade mScrollFade; private Handler mHandler = new Handler(); private BaseAdapter mListAdapter; private boolean mChangedBounds; public static interface SectionIndexer { Object[] getSections(); int getPositionForSection(int section); int getSectionForPosition(int position); } public CustomFastScrollView(Context context) { super(context); init(context, null); } public CustomFastScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public CustomFastScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void useThumbDrawable(Drawable drawable) { mCurrentThumb = drawable; mThumbW = mOverlayScrollThumbWidth;//mCurrentThumb.getIntrinsicWidth(); mThumbH = mCurrentThumb.getIntrinsicHeight(); mChangedBounds = true; } private void init(Context context, AttributeSet attrs) { // set all attributes from xml if (attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomFastScrollView); mOverlayHeight = typedArray.getDimensionPixelSize( R.styleable.CustomFastScrollView_overlayHeight, 0); mOverlayWidth = typedArray.getDimensionPixelSize( R.styleable.CustomFastScrollView_overlayWidth, 0); mOverlayTextSize = typedArray.getDimensionPixelSize( R.styleable.CustomFastScrollView_overlayTextSize, 0); mOverlayScrollThumbWidth = typedArray.getDimensionPixelSize( R.styleable.CustomFastScrollView_overlayScrollThumbWidth, 0); } // Get both the scrollbar states drawables final Resources res = context.getResources(); //Drawable thumbDrawable = res.getDrawable(R.drawable.scrollbar_handle_accelerated_anim2); Drawable thumbDrawable = res.getDrawable(R.drawable.bar1); useThumbDrawable(thumbDrawable); //mOverlayDrawable = res.getDrawable(android.R.drawable.alert_dark_frame); mOverlayDrawable = res.getDrawable(R.drawable.bar2); mScrollCompleted = true; setWillNotDraw(false); // Need to know when the ListView is added setOnHierarchyChangeListener(this); mOverlayPos = new RectF(); mScrollFade = new ScrollFade(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setTextSize(mOverlayTextSize); mPaint.setColor(0xFFFFFFFF); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); } private void removeThumb() { mThumbVisible = false; // Draw one last time to remove thumb invalidate(); } @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mThumbVisible) { // No need to draw the rest return; } final int y = mThumbY; final int viewWidth = getWidth(); final CustomFastScrollView.ScrollFade scrollFade = mScrollFade; int alpha = -1; if (scrollFade.mStarted) { alpha = scrollFade.getAlpha(); if (alpha < ALPHA_MAX / 2) { mCurrentThumb.setAlpha(alpha * 2); } int left = viewWidth - (mThumbW * alpha) / ALPHA_MAX; mCurrentThumb.setBounds(left, 0, viewWidth, mThumbH); mChangedBounds = true; } canvas.translate(0, y); Log.i("TAG", "left y上的变化 draw" + y); mCurrentThumb.draw(canvas); //canvas.translate(0, -y); Log.i("TAG", "left y上的变化 draw" + y); // If user is dragging the scroll bar, draw the alphabet overlay if (mDragging && mDrawOverlay) { mOverlayDrawable.draw(canvas); final Paint paint = mPaint; float descent = paint.descent(); final RectF rectF = mOverlayPos; canvas.drawText(mSectionText + "美丽" , (int) (rectF.left + rectF.right) / 2, (int) (rectF.bottom + rectF.top) / 2 + descent, paint); } else if (alpha == 0) { scrollFade.mStarted = false; removeThumb(); } else { invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mCurrentThumb != null) { mCurrentThumb.setBounds(w - mThumbW, 0, w, mThumbH); } final RectF pos = mOverlayPos; //pos.left = (w - mOverlayWidth) / 2; pos.left = w-mOverlayWidth-mThumbW; pos.right = pos.left + mOverlayWidth; //pos.top = h / 10; // 10% from top pos.top = 0 - mOverlayHeight/2 + mThumbH/2; pos.bottom = pos.top + mOverlayHeight; mOverlayDrawable.setBounds((int) pos.left, (int) pos.top, (int) pos.right, (int) pos.bottom); } public void onScrollStateChanged(AbsListView view, int scrollState) { } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (totalItemCount - visibleItemCount > 0 && !mDragging) { mThumbY = ((getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount); if (mChangedBounds) { final int viewWidth = getWidth(); mCurrentThumb.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH); mChangedBounds = false; } } mScrollCompleted = true; if (firstVisibleItem == mVisibleItem) { return; } mVisibleItem = firstVisibleItem; if (!mThumbVisible || mScrollFade.mStarted) { mThumbVisible = true; mCurrentThumb.setAlpha(ALPHA_MAX); } mHandler.removeCallbacks(mScrollFade); mScrollFade.mStarted = false; if (!mDragging) { mHandler.postDelayed(mScrollFade, 1500); } } private void getSections() { Adapter adapter = mList.getAdapter(); if (adapter instanceof HeaderViewListAdapter) { mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount(); adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter(); } if (adapter instanceof SectionIndexer) { mListAdapter = (BaseAdapter) adapter; mSections = ((SectionIndexer) mListAdapter).getSections(); } } public void onChildViewAdded(View parent, View child) { if (child instanceof ListView) { mList = (ListView)child; mList.setOnScrollListener(this); getSections(); } } public void onChildViewRemoved(View parent, View child) { if (child == mList) { mList = null; mListAdapter = null; mSections = null; } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mThumbVisible && ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getX() > getWidth() - mThumbW && ev.getY() >= mThumbY && ev.getY() <= mThumbY + mThumbH) { mDragging = true; return true; } } return false; } private void scrollTo(float position) { int count = mList.getCount(); mScrollCompleted = false; final Object[] sections = mSections; int sectionIndex; if (sections != null && sections.length > 1) { final int nSections = sections.length; int section = (int) (position * nSections); if (section >= nSections) { section = nSections - 1; } sectionIndex = section; final SectionIndexer baseAdapter = (SectionIndexer) mListAdapter; int index = baseAdapter.getPositionForSection(section); // Given the expected section and index, the following code will // try to account for missing sections (no names starting with..) // It will compute the scroll space of surrounding empty sections // and interpolate the currently visible letter‘s range across the // available space, so that there is always some list movement while // the user moves the thumb. int nextIndex = count; int prevIndex = index; int prevSection = section; int nextSection = section + 1; // Assume the next section is unique if (section < nSections - 1) { nextIndex = baseAdapter.getPositionForSection(section + 1); } // Find the previous index if we‘re slicing the previous section if (nextIndex == index) { // Non-existent letter while (section > 0) { section--; prevIndex = baseAdapter.getPositionForSection(section); if (prevIndex != index) { prevSection = section; sectionIndex = section; break; } } } // Find the next index, in case the assumed next index is not // unique. For instance, if there is no P, then request for P‘s // position actually returns Q‘s. So we need to look ahead to make // sure that there is really a Q at Q‘s position. If not, move // further down... int nextNextSection = nextSection + 1; while (nextNextSection < nSections && baseAdapter.getPositionForSection(nextNextSection) == nextIndex) { nextNextSection++; nextSection++; } // Compute the beginning and ending scroll range percentage of the // currently visible letter. This could be equal to or greater than // (1 / nSections). float fPrev = (float) prevSection / nSections; float fNext = (float) nextSection / nSections; index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev) / (fNext - fPrev)); // Don‘t overflow if (index > count - 1) index = count - 1; mList.setSelectionFromTop(index + mListOffset, 0); } else { int index = (int) (position * count); mList.setSelectionFromTop(index + mListOffset, 0); sectionIndex = -1; } if (sectionIndex >= 0) { String text = mSectionText = sections[sectionIndex].toString(); mDrawOverlay = (text.length() != 1 || text.charAt(0) != ‘ ‘) && sectionIndex < sections.length; } else { mDrawOverlay = false; } } private void cancelFling() { // Cancel the list fling MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); mList.onTouchEvent(cancelFling); cancelFling.recycle(); } @Override public boolean onTouchEvent(MotionEvent me) { if (me.getAction() == MotionEvent.ACTION_DOWN) { if (me.getX() > getWidth() - mThumbW && me.getY() >= mThumbY && me.getY() <= mThumbY + mThumbH) { mDragging = true; if (mListAdapter == null && mList != null) { getSections(); } cancelFling(); return true; } } else if (me.getAction() == MotionEvent.ACTION_UP) { if (mDragging) { mDragging = false; final Handler handler = mHandler; handler.removeCallbacks(mScrollFade); handler.postDelayed(mScrollFade, 1000); return true; } } else if (me.getAction() == MotionEvent.ACTION_MOVE) { if (mDragging) { final int viewHeight = getHeight(); mThumbY = (int) me.getY() - mThumbH + 10; if (mThumbY < 0) { mThumbY = 0; } else if (mThumbY + mThumbH > viewHeight) { mThumbY = viewHeight - mThumbH; } // If the previous scrollTo is still pending if (mScrollCompleted) { scrollTo((float) mThumbY / (viewHeight - mThumbH)); } return true; } } return super.onTouchEvent(me); } public class ScrollFade implements Runnable { long mStartTime; long mFadeDuration; boolean mStarted; void startFade() { mFadeDuration = FADE_DURATION; mStartTime = SystemClock.uptimeMillis(); mStarted = true; } int getAlpha() { if (!mStarted) { return ALPHA_MAX; } int alpha; long now = SystemClock.uptimeMillis(); if (now > mStartTime + mFadeDuration) { alpha = 0; } else { alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration); } return alpha; } public void run() { if (!mStarted) { startFade(); invalidate(); } if (getAlpha() > 0) { final int y = mThumbY; final int viewWidth = getWidth(); invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH); } else { mStarted = false; removeThumb(); } } } /** * Call when the list‘s items have changed */ public void listItemsChanged() { getSections(); } }
Demo实现的效果如下 下面来分析下代码实现的原理
标签:
原文地址:http://www.cnblogs.com/huanyi0723/p/4813508.html