/* * Copyright 2011 woozzu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.cgt.weixin.view.widget; import org.xbill.DNS.tests.primary; import net.cgt.weixin.R; import net.cgt.weixin.utils.L; import net.cgt.weixin.utils.LogUtil; 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; /** * 右侧的索引条 * * @author lijian-pc * @date 2014-11-26 */ public class IndexScroller { private static final String LOGTAG = LogUtil .makeLogTag(IndexScroller.class); private Context mContext; /** * 索引条宽度 **/ private float mIndexbarWidth; /** * 索引条外边距 **/ private float mIndexbarMargin; /** * 索引条内边距 **/ private float mPreviewPadding; /** * 密度 **/ private float mDensity; /** * 缩放密度 **/ private float mScaledDensity; /** * 透明度 **/ private float mAlphaRate; /** * 状态 **/ private int mState = STATE_HIDDEN; /** * ListView宽度 **/ private int mListViewWidth; /** * ListView高度 **/ private int mListViewHeight; /** * 当前部分 */ private int mCurrentSection = -1; /** * 是否正在索引 */ private boolean mIsIndexing = false; /** * 右侧索引文本色值 */ private static final String COLOR_RIGHT_TEXT = "#565656"; /** * 右侧索引背景色值 */ private static final String COLOR_RIGHT_BACKGROUND = "#808080";// "#bfbfbf"; /** * 中间预览文本色值 */ private static final String COLOR_MIDDLE_TEXT = "#ffffff"; /** * 中间预览背景色值 */ private static final String COLOR_MIDDLE_BACKGROUND = "#000000";// "#808080"; private ListView mListView = null; private SectionIndexer mIndexer = null; private String[] mSections = null; private RectF mIndexbarRect; /** 4种状态(已隐藏) **/ private static final int STATE_HIDDEN = 0; /** 4种状态(正在显示) **/ private static final int STATE_SHOWING = 1; /** 4种状态(已显示) **/ private static final int STATE_SHOWN = 2; /** 4种状态(正在隐藏) **/ private static final int STATE_HIDING = 3; public IndexScroller(Context context, ListView lv) { this.mContext = context; mDensity = context.getResources().getDisplayMetrics().density; mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity; mListView = lv; setAdapter(mListView.getAdapter()); mIndexbarWidth = 25 * mDensity; mIndexbarMargin = 0 * mDensity; mPreviewPadding = 10 * mDensity; } public void draw(Canvas canvas) { if (mSections != null && mSections.length > 0) { onDrawRightIndexText(canvas); } if (mState == STATE_HIDDEN) return; onDrawRightIndexBackground(canvas); if (mSections != null && mSections.length > 0) { // Preview is shown when mCurrentSection is set // 中间预览文本 if (mCurrentSection >= 0) { onDrawMiddlePreview(canvas); } } } /** * 绘制右侧索引条文本 * * @param canvas */ private void onDrawRightIndexText(Canvas canvas) { L.d(LOGTAG, "onDrawRightIndexText"); // 绘画右侧索引条的字母 Paint indexPaint = new Paint(); indexPaint.setColor(Color.parseColor(COLOR_RIGHT_TEXT)); // indexPaint.setAlpha((int) (255 * mAlphaRate)); indexPaint.setAntiAlias(true); indexPaint.setTextSize(14 * 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); } } /** * 绘制中间预览view * * @param canvas */ private void onDrawMiddlePreview(Canvas canvas) { L.d(LOGTAG, "onDrawMiddlePreview"); Paint previewPaint = new Paint(); // 用来绘画预览文本背景的画笔 previewPaint.setColor(Color.parseColor(COLOR_MIDDLE_BACKGROUND));// 设置画笔颜色为黑色 previewPaint.setAlpha(96); // 设置透明度 previewPaint.setAntiAlias(true);// 设置抗锯齿 previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0)); // 设置阴影层 Paint previewTextPaint = new Paint(); // 用来绘画预览字母的画笔 previewTextPaint.setColor(Color.parseColor(COLOR_MIDDLE_TEXT)); // 设置画笔为白色 previewTextPaint.setAntiAlias(true); // 设置抗锯齿 previewTextPaint.setTextSize(60 * mScaledDensity); // 设置字体大小 // 单个文本的宽度 float previewTextWidth = previewTextPaint .measureText(mSections[mCurrentSection]); float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent(); 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); } /** * 绘制右侧索引的背景 * * @param canvas */ private void onDrawRightIndexBackground(Canvas canvas) { L.d(LOGTAG, "onDrawRoundRectBackground"); // mAlphaRate determines the rate of opacity Paint indexbarPaint = new Paint(); // indexbarPaint.setColor(Color.BLACK); indexbarPaint.setColor(Color.parseColor(COLOR_RIGHT_BACKGROUND)); indexbarPaint.setAlpha((int) (64 * mAlphaRate)); indexbarPaint.setAntiAlias(true); // 画右侧字母索引的圆矩形 // canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, // indexbarPaint); canvas.drawRect(mIndexbarRect, indexbarPaint); } public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // If down event occurs inside index bar region, start indexing // 如果按下的事件发生在索引条区域内,开始索引 if (contains(ev.getX(), ev.getY())) { setState(STATE_SHOWN); // It demonstrates that the motion event started from index bar mIsIndexing = true; // Determine which section the point is in, and move the list to // that section mCurrentSection = getSectionByPoint(ev.getY()); // ListView.setSelection(int position) // ListView.setSelectionFromTop(int position, int y); // 其中 // position指的是指定的item的在ListView中的索引,注意如果有Header存在的情况下,索引是从Header就开始算的。 // y指的是到ListView可见范围内最上边边缘的距离。 mListView.setSelection(mIndexer .getPositionForSection(mCurrentSection)); return true; } break; case MotionEvent.ACTION_MOVE: if (mIsIndexing) { // If this event moves inside index bar if (contains(ev.getX(), ev.getY())) { // Determine which section the point is in, and move the // list to that section 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); return true; } break; } return false; } /** * 创建索引条大小 * * @param w * @param h * @param oldw * @param oldh */ 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); } 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); } /** * 设置右侧索引条数据 * * @param adapter */ public void setAdapter(Adapter adapter) { if (adapter instanceof SectionIndexer) { mIndexer = (SectionIndexer) adapter; mSections = (String[]) mIndexer.getSections(); } } /** * 设置显隐状态 * * @param state */ private void setState(int state) { if (state < STATE_HIDDEN || state > STATE_HIDING) return; mState = state; switch (mState) { case STATE_HIDDEN: // Cancel any fade effect mHandler.removeMessages(0); break; case STATE_SHOWING: // Start to fade in mAlphaRate = 0; fade(0); break; case STATE_SHOWN: // Cancel any fade effect mAlphaRate = 1; mHandler.removeMessages(0); break; case STATE_HIDING: // Start to fade out after three seconds mAlphaRate = 1; fade(3000); break; } } /** * 检测触摸点是否在索引条区域内 * * @param x * @param y * @return */ private 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()); } /** * 根据触摸点的y坐标获得区域 * * @param y * @return */ private int getSectionByPoint(float y) { // 如果索引字母不存在,返回0 if (mSections == null || mSections.length == 0) return 0; // 如果在最顶部的索引字母之外,返回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)); } /** * 设置多久后隐藏 * * @param delay */ 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: // Fade in effect mAlphaRate += (1 - mAlphaRate) * 0.2; if (mAlphaRate > 0.9) { mAlphaRate = 1; setState(STATE_SHOWN); } mListView.invalidate(); fade(10); break; case STATE_SHOWN: // If no action, hide automatically setState(STATE_HIDING); break; case STATE_HIDING: // Fade out effect mAlphaRate -= mAlphaRate * 0.2; if (mAlphaRate < 0.1) { mAlphaRate = 0; setState(STATE_HIDDEN); } mListView.invalidate(); fade(10); break; } } }; }