package com.cellasoft.univrapp.widget;
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;
import com.cellasoft.univrapp.adapter.ContactListAdapter;
import java.lang.ref.WeakReference;
// taken from https://github.com/woozzu/indexableListView IndexScroller
// rewrite some sections to make it more customizable
public class IndexScroller {
private static final int STATE_HIDDEN = 0;
private int mState = STATE_HIDDEN;
private static final int STATE_SHOWING = 1;
private static final int STATE_SHOWN = 2;
private static final int STATE_HIDING = 3;
protected IncomingHandler mHandler = new IncomingHandler(this);
/**
* Add additional properties for customization
*/
// Hung - store the last touch down eventX and eventY so that we can detect
// where user click on list view
// indexscroller will override touch event of listview, so onclicklistener
// will not work
private float lastTouchDownEventX = -1;
private float lastTouchDownEventY = -1;
// / end additional properties
// whether to autohide the scroller
// false will always show the index bar
// true will hide the scrollbar at first, but slowly show it later
private boolean autoHide = false;
// minor optimizations
private Paint indexbarContainerPaint = new Paint(); // paint for indexbar
// container
private Paint indexPaint = new Paint();
private boolean showIndexContainer = false; // whether to show the outer
// index container
private int indexbarContainerBgColor = Color.BLACK;
private int indexPaintColor = Color.WHITE; // color for section title
private float mIndexbarWidth;
private float mIndexbarMargin;
private float mPreviewPadding;
private float mDensity;
private float mScaledDensity;
private float mAlphaRate;
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;
public IndexScroller(Context context, ListView lv) {
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = lv;
setAdapter(mListView.getAdapter());
mIndexbarWidth = 20 * mDensity;
mIndexbarMargin = 10 * mDensity;
mPreviewPadding = 5 * mDensity;
// customization of paint colors
// outer container
indexbarContainerPaint.setAntiAlias(true);
// letter in section
indexPaint.setAntiAlias(true);
indexPaint.setTextSize(12 * mScaledDensity);
}
public IndexScroller(Context context, ListView lv, SectionIndexer indexer) {
this(context, lv);
mIndexer = indexer;
}
// draw the outer rounded container
public void drawIndexBarContainer(Canvas canvas) {
indexbarContainerPaint.setColor(indexbarContainerBgColor);
indexbarContainerPaint.setAlpha((int) (64 * mAlphaRate)); // opacity
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity,
indexbarContainerPaint);
}
public void drawSections(Canvas canvas) {
indexPaint.setColor(indexPaintColor);
indexPaint.setAlpha((int) (255 * mAlphaRate));
float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin)
/ mSections.length;
float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint
.ascent())) / 2;
// draw the section letters
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 drawCurrentSection(Canvas canvas) {
if (mCurrentSection >= 0) {
// Preview is shown when mCurrentSection is set
// mCurrentSection is the letter that is being pressed
// this will draw the big preview text on top of the listview
Paint previewPaint = new Paint();
previewPaint.setColor(Color.BLACK);
previewPaint.setAlpha(96);
previewPaint.setAntiAlias(true);
previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0));
Paint previewTextPaint = new Paint();
previewTextPaint.setColor(Color.WHITE);
previewTextPaint.setAntiAlias(true);
previewTextPaint.setTextSize(50 * 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);
}
}
public void draw(Canvas canvas) {
if (mState == STATE_HIDDEN)
return;
if (showIndexContainer)
drawIndexBarContainer(canvas);
if (mSections != null && mSections.length > 0) {
drawCurrentSection(canvas);
drawSections(canvas);
}
}
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchDownEventX = ev.getX(); // Hung capture the event for later
// use in listview item click
lastTouchDownEventY = ev.getY();
// If down event occurs inside index bar region, start indexing
if (mState != STATE_HIDDEN && 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());
mListView.setSelection(mIndexer
.getPositionForSection(mCurrentSection) + 1);
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) + 1);
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mIsIndexing) {
mIsIndexing = false;
mCurrentSection = -1;
}
// only hide if state is auto hiding
if (autoHide) {
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
}
break;
}
return false;
}
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);
}
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
} else if (adapter instanceof ContactListAdapter) {
ContactListAdapter c = (ContactListAdapter) adapter;
mIndexer = c.getIndexer();
mSections = (String[]) mIndexer.getSections();
}
}
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
mHandler.removeMessages(0);
break;
case STATE_HIDING:
// Start to fade out after three seconds
mAlphaRate = 1;
fade(3000);
break;
}
}
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());
}
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));
}
private void fade(long delay) {
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
}
private void refresh() {
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
// Hung - comment out this to disable hiding
if (autoHide) {
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;
}
}
public float getLastTouchDownEventX() {
return lastTouchDownEventX;
}
public void setLastTouchDownEventX(float lastTouchDownEventX) {
this.lastTouchDownEventX = lastTouchDownEventX;
}
public float getLastTouchDownEventY() {
return lastTouchDownEventY;
}
public void setLastTouchDownEventY(float lastTouchDownEventY) {
this.lastTouchDownEventY = lastTouchDownEventY;
}
public boolean isAutoHide() {
return autoHide;
}
public void setAutoHide(boolean autoHide) {
this.autoHide = autoHide;
}
public boolean isShowIndexContainer() {
return showIndexContainer;
}
public void setShowIndexContainer(boolean showIndexContainer) {
this.showIndexContainer = showIndexContainer;
}
public int getIndexbarContainerBgColor() {
return indexbarContainerBgColor;
}
public void setIndexbarContainerBgColor(int indexbarContainerBgColor) {
this.indexbarContainerBgColor = indexbarContainerBgColor;
}
public int getIndexPaintColor() {
return indexPaintColor;
}
public void setIndexPaintColor(int indexPaintColor) {
this.indexPaintColor = indexPaintColor;
}
static class IncomingHandler extends Handler {
private final WeakReference<IndexScroller> mScroller;
IncomingHandler(IndexScroller scroller) {
mScroller = new WeakReference<IndexScroller>(scroller);
}
@Override
public void handleMessage(Message msg) {
IndexScroller scroller = mScroller.get();
if (scroller != null) {
scroller.refresh();
}
}
}
}