package com.jwipc.nodka_alarmpower;
|
|
import java.util.List;
|
import java.util.TimerTask;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.TimeUnit;
|
|
import android.content.Context;
|
import android.graphics.Bitmap;
|
import android.graphics.BitmapFactory;
|
import android.graphics.Canvas;
|
import android.graphics.Paint;
|
import android.graphics.Rect;
|
import android.graphics.Typeface;
|
import android.os.Handler;
|
import android.os.Message;
|
import android.util.AttributeSet;
|
import android.view.GestureDetector;
|
import android.view.MotionEvent;
|
import android.view.View;
|
|
|
|
public class LoopView extends View {
|
|
public enum ACTION {
|
CLICK, FLING, DAGGLE
|
}
|
Context context;
|
|
Handler handler;
|
private GestureDetector gestureDetector;
|
OnItemSelectedListener onItemSelectedListener;
|
|
ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
|
private ScheduledFuture<?> mFuture;
|
|
Paint paintOuterText;
|
Paint paintCenterText;
|
Paint paintIndicator;
|
|
List<String> items;
|
|
int textSize;
|
int maxTextWidth = 50;
|
int maxTextHeight = 50;
|
|
int colorGray;
|
int colorBlack;
|
int colorLightGray;
|
|
float lineSpacingMultiplier;
|
boolean isLoop;
|
|
int firstLineY;
|
int secondLineY;
|
|
int totalScrollY;
|
int initPosition;
|
private int selectedItem;
|
int preCurrentIndex;
|
int change;
|
|
int itemsVisible;
|
|
int measuredHeight;
|
int measuredWidth;
|
int paddingLeft = 10;
|
int paddingRight = 0;
|
|
int halfCircumference;
|
int radius;
|
|
private int mOffset = 0;
|
private float previousY;
|
long startTime = 0;
|
|
final int WHAT_INVALIDATE_LOOP_VIEW = 1000;
|
final int WHAT_SMOOTH_SCROLL = 2000;
|
final int WHAT_ITEM_SELECTED = 3000;
|
|
|
|
|
|
public LoopView(Context context) {
|
super(context);
|
initLoopView(context);
|
}
|
|
public LoopView(Context context, AttributeSet attributeset) {
|
super(context, attributeset);
|
initLoopView(context);
|
}
|
|
public LoopView(Context context, AttributeSet attributeset, int defStyleAttr) {
|
super(context, attributeset, defStyleAttr);
|
initLoopView(context);
|
}
|
|
private void initLoopView(Context context) {
|
this.context = context;
|
|
handler();
|
gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
|
gestureDetector.setIsLongpressEnabled(false);
|
|
lineSpacingMultiplier = 1.6F;
|
isLoop = true;
|
itemsVisible = 5;
|
textSize = 0;
|
colorGray = 0xffafafaf;
|
colorBlack = 0xff313131;
|
colorLightGray = 0xff000000;
|
|
totalScrollY = 0;
|
initPosition = 0;
|
|
initPaints();
|
|
setTextSize(16F);
|
|
}
|
|
private void initPaints() {
|
paintOuterText = new Paint();
|
paintOuterText.setColor(colorGray);
|
paintOuterText.setAntiAlias(true);
|
paintOuterText.setTypeface(Typeface.MONOSPACE);
|
paintOuterText.setTextSize(textSize);
|
|
paintCenterText = new Paint();
|
paintCenterText.setColor(colorBlack);
|
paintCenterText.setAntiAlias(true);
|
paintCenterText.setTextScaleX(1.05F);
|
paintCenterText.setTypeface(Typeface.MONOSPACE);
|
paintCenterText.setTextSize(textSize);
|
|
paintIndicator = new Paint();
|
paintIndicator.setColor(colorLightGray);
|
paintIndicator.setAntiAlias(true);
|
|
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
}
|
}
|
|
private void remeasure() {
|
if (items == null) {
|
return;
|
}
|
|
measureTextWidthHeight();
|
|
halfCircumference = (int) (maxTextHeight * lineSpacingMultiplier * (itemsVisible - 1));
|
measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
|
radius = (int) (halfCircumference / Math.PI);
|
int extraRightWidth = (int) (maxTextWidth * 0.05) + 1;
|
if (paddingRight<=extraRightWidth) {
|
paddingRight = extraRightWidth;
|
}
|
measuredWidth = maxTextWidth + paddingLeft + paddingRight;
|
firstLineY = (int) ((measuredHeight - maxTextHeight * lineSpacingMultiplier) / 2.0F);
|
secondLineY = (int) ((measuredHeight + maxTextHeight * lineSpacingMultiplier) / 2.0F);
|
|
preCurrentIndex = initPosition;
|
}
|
|
private void measureTextWidthHeight() {
|
Rect rect = new Rect();
|
for (int i = 0; i < items.size(); i++) {
|
String s1 = items.get(i);
|
int textWidth = maxTextWidth;
|
if (textWidth > maxTextWidth) {
|
maxTextWidth = textWidth;
|
}
|
int textHeight = maxTextHeight;
|
if (textHeight > maxTextHeight) {
|
maxTextHeight = textHeight;
|
}
|
}
|
|
}
|
|
void smoothScroll(ACTION action) {
|
cancelFuture();
|
if (action==ACTION.FLING||action==ACTION.DAGGLE) {
|
float itemHeight = lineSpacingMultiplier * maxTextHeight;
|
mOffset = (int) ((totalScrollY%itemHeight + itemHeight) % itemHeight);
|
if ((float) mOffset > itemHeight / 2.0F) {
|
mOffset = (int) (itemHeight - (float) mOffset);
|
} else {
|
mOffset = -mOffset;
|
}
|
}
|
mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS);
|
}
|
|
protected final void scrollBy(float velocityY) {
|
cancelFuture();
|
// 修改这个值可以改变滑行速度
|
int velocityFling = 50;
|
mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, velocityFling, TimeUnit.MILLISECONDS);
|
}
|
|
public void cancelFuture() {
|
if (mFuture!=null&&!mFuture.isCancelled()) {
|
mFuture.cancel(true);
|
mFuture = null;
|
}
|
}
|
|
public final void setNotLoop() {
|
isLoop = false;
|
}
|
|
public final void setTextSize(float size) {
|
if (size > 0.0F) {
|
textSize = (int) (context.getResources().getDisplayMetrics().density * size);
|
paintOuterText.setTextSize(textSize);
|
paintCenterText.setTextSize(textSize);
|
}
|
}
|
|
public final void setInitPosition(int initPosition) {
|
this.initPosition = initPosition;
|
}
|
|
public final void setListener(OnItemSelectedListener OnItemSelectedListener) {
|
onItemSelectedListener = OnItemSelectedListener;
|
}
|
|
public final void setItems(List<String> items) {
|
this.items = items;
|
remeasure();
|
invalidate();
|
}
|
|
@Override
|
public int getPaddingLeft() {
|
return paddingLeft;
|
}
|
|
@Override
|
public int getPaddingRight() {
|
return paddingRight;
|
}
|
|
public void setViewPadding(int left, int top, int right, int bottom) {
|
paddingLeft = left;
|
paddingRight = right;
|
}
|
|
public final int getSelectedItem() {
|
return selectedItem;
|
}
|
|
protected final void onItemSelected() {
|
if (onItemSelectedListener != null) {
|
postDelayed(new OnItemSelectedRunnable(this), 200L);
|
}
|
}
|
|
@Override
|
protected void onDraw(Canvas canvas) {
|
if (items == null) {
|
return;
|
}
|
|
change = (int) (totalScrollY / (lineSpacingMultiplier * maxTextHeight));
|
|
|
preCurrentIndex = initPosition + change % items.size();
|
if (!isLoop) {
|
if (preCurrentIndex < 0) {
|
preCurrentIndex = 0;
|
}
|
if (preCurrentIndex > items.size() - 1) {
|
preCurrentIndex = items.size() - 1;
|
}
|
} else {
|
if (preCurrentIndex < 0) {
|
preCurrentIndex = items.size() + preCurrentIndex;
|
}
|
if (preCurrentIndex > items.size() - 1) {
|
preCurrentIndex = preCurrentIndex - items.size();
|
}
|
}
|
|
|
|
String as[] = new String[itemsVisible];
|
int k1 = 0;
|
while (k1 < itemsVisible) {
|
int l1 = preCurrentIndex - (itemsVisible / 2 - k1);
|
if (isLoop) {
|
if (l1 < 0) {
|
l1 = l1 + items.size();
|
}
|
if (l1 > items.size() - 1) {
|
l1 = l1 - items.size();
|
}
|
as[k1] = items.get(l1);
|
} else if (l1 < 0) {
|
as[k1] = "";
|
} else if (l1 > items.size() - 1) {
|
as[k1] = "";
|
} else {
|
as[k1] = items.get(l1);
|
}
|
k1++;
|
}
|
|
|
canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
|
canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
|
|
int m1 = paddingLeft;
|
int j1 = 0;
|
int j2 = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight));
|
while (j1 < itemsVisible) {
|
canvas.save();
|
float itemHeight = maxTextHeight * lineSpacingMultiplier;
|
double radian = ((itemHeight * j1 - j2) * Math.PI) / halfCircumference;
|
|
float angle = (float) (90D - (radian / Math.PI) * 180D);
|
if (angle >= 90F || angle <= -90F) {
|
canvas.restore();
|
} else {
|
int translateY = (int) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D);
|
|
canvas.translate(0.0F, translateY);
|
canvas.scale(1.0F, (float) Math.sin(radian));
|
if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
|
canvas.save();
|
canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
|
canvas.drawText(as[j1], m1, maxTextHeight, paintOuterText);
|
canvas.restore();
|
canvas.save();
|
canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
|
canvas.drawText(as[j1], m1, maxTextHeight, paintCenterText);
|
canvas.restore();
|
} else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
|
canvas.save();
|
canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
|
canvas.drawText(as[j1], m1, maxTextHeight, paintCenterText);
|
canvas.restore();
|
canvas.save();
|
canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
|
canvas.drawText(as[j1], m1, maxTextHeight, paintOuterText);
|
canvas.restore();
|
} else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
|
canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
|
canvas.drawText(as[j1], m1, maxTextHeight-10, paintCenterText);
|
selectedItem = items.indexOf(as[j1]);
|
} else {
|
canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
|
canvas.drawText(as[j1], m1, maxTextHeight, paintOuterText);
|
}
|
canvas.restore();
|
}
|
j1++;
|
}
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
remeasure();
|
setMeasuredDimension(measuredWidth, measuredHeight);
|
}
|
|
@Override
|
public boolean onTouchEvent(MotionEvent event) {
|
boolean eventConsumed = gestureDetector.onTouchEvent(event);
|
float itemHeight = lineSpacingMultiplier * maxTextHeight;
|
|
switch (event.getAction()) {
|
case MotionEvent.ACTION_DOWN:
|
startTime = System.currentTimeMillis();
|
cancelFuture();
|
previousY = event.getRawY();
|
break;
|
|
case MotionEvent.ACTION_MOVE:
|
float dy = previousY - event.getRawY();
|
previousY = event.getRawY();
|
|
totalScrollY = (int) (totalScrollY + dy);
|
|
if (!isLoop) {
|
float top = -initPosition * itemHeight;
|
float bottom = (items.size() - 1 - initPosition) * itemHeight;
|
|
if (totalScrollY < top) {
|
totalScrollY = (int) top;
|
} else if (totalScrollY > bottom) {
|
totalScrollY = (int) bottom;
|
}
|
}
|
break;
|
|
case MotionEvent.ACTION_UP:
|
default:
|
if (!eventConsumed) {
|
float y = event.getY();
|
double l = Math.acos((radius - y) / radius) * radius;
|
int circlePosition = (int) ((l + itemHeight / 2) / itemHeight);
|
|
float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
|
mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);
|
|
if ((System.currentTimeMillis() - startTime) > 120) {
|
smoothScroll(ACTION.DAGGLE);
|
} else {
|
smoothScroll(ACTION.CLICK);
|
}
|
}
|
break;
|
}
|
|
invalidate();
|
return true;
|
}
|
|
public void handler()
|
{
|
handler = new Handler()
|
{
|
@Override
|
public final void handleMessage(Message msg) {
|
switch (msg.what) {
|
case WHAT_INVALIDATE_LOOP_VIEW:
|
invalidate();
|
break;
|
|
case WHAT_SMOOTH_SCROLL:
|
smoothScroll(LoopView.ACTION.FLING);
|
break;
|
|
case WHAT_ITEM_SELECTED:
|
onItemSelected();
|
break;
|
}
|
}
|
};
|
}
|
|
class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
|
|
final LoopView loopView;
|
|
LoopViewGestureListener(LoopView loopview) {
|
loopView = loopview;
|
}
|
|
@Override
|
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
loopView.scrollBy(velocityY);
|
return true;
|
}
|
}
|
|
class InertiaTimerTask extends TimerTask {
|
|
float a;
|
final float velocityY;
|
final LoopView loopView;
|
|
InertiaTimerTask(LoopView loopview, float velocityY) {
|
super();
|
loopView = loopview;
|
this.velocityY = velocityY;
|
a = Integer.MAX_VALUE;
|
}
|
|
@Override
|
public final void run() {
|
if (a == Integer.MAX_VALUE) {
|
if (Math.abs(velocityY) > 2000F) {
|
if (velocityY > 0.0F) {
|
a = 2000F;
|
} else {
|
a = -2000F;
|
}
|
} else {
|
a = velocityY;
|
}
|
}
|
if (Math.abs(a) >= 0.0F && Math.abs(a) <= 20F) {
|
loopView.cancelFuture();
|
loopView.handler.sendEmptyMessage(loopView.WHAT_SMOOTH_SCROLL);
|
return;
|
}
|
int i = (int) ((a * 10F) / 1000F);
|
LoopView loopview = loopView;
|
loopview.totalScrollY = loopview.totalScrollY - i;
|
if (!loopView.isLoop) {
|
float itemHeight = loopView.lineSpacingMultiplier * loopView.maxTextHeight;
|
if (loopView.totalScrollY <= (int) ((float) (-loopView.initPosition) * itemHeight)) {
|
a = 40F;
|
loopView.totalScrollY = (int) ((float) (-loopView.initPosition) * itemHeight);
|
} else if (loopView.totalScrollY >= (int) ((float) (loopView.items.size() - 1 - loopView.initPosition) * itemHeight)) {
|
loopView.totalScrollY = (int) ((float) (loopView.items.size() - 1 - loopView.initPosition) * itemHeight);
|
a = -40F;
|
}
|
}
|
if (a < 0.0F) {
|
a = a + 20F;
|
} else {
|
a = a - 20F;
|
}
|
loopView.handler.sendEmptyMessage(loopView.WHAT_INVALIDATE_LOOP_VIEW);
|
}
|
}
|
|
public interface OnItemSelectedListener {
|
void onItemSelected(int index);
|
}
|
|
class OnItemSelectedRunnable implements Runnable {
|
final LoopView loopView;
|
|
OnItemSelectedRunnable(LoopView loopview) {
|
loopView = loopview;
|
}
|
|
@Override
|
public final void run() {
|
loopView.onItemSelectedListener.onItemSelected(loopView.getSelectedItem());
|
}
|
}
|
|
class SmoothScrollTimerTask extends TimerTask {
|
|
int realTotalOffset;
|
int realOffset;
|
int offset;
|
final LoopView loopView;
|
|
SmoothScrollTimerTask(LoopView loopview, int offset) {
|
this.loopView = loopview;
|
this.offset = offset;
|
realTotalOffset = Integer.MAX_VALUE;
|
realOffset = 0;
|
}
|
|
@Override
|
public final void run() {
|
if (realTotalOffset == Integer.MAX_VALUE) {
|
realTotalOffset = offset;
|
}
|
realOffset = (int) ((float) realTotalOffset * 0.1F);
|
|
if (realOffset == 0) {
|
if (realTotalOffset < 0) {
|
realOffset = -1;
|
} else {
|
realOffset = 1;
|
}
|
}
|
if (Math.abs(realTotalOffset) <= 0) {
|
loopView.cancelFuture();
|
loopView.handler.sendEmptyMessage(loopView.WHAT_ITEM_SELECTED);
|
} else {
|
loopView.totalScrollY = loopView.totalScrollY + realOffset;
|
loopView.handler.sendEmptyMessage(loopView.WHAT_INVALIDATE_LOOP_VIEW);
|
realTotalOffset = realTotalOffset - realOffset;
|
}
|
}
|
}
|
|
|
|
|
|
|
|
|
}
|