package com.test.canvas;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.SpinnerAdapter;
public class Switcher extends ViewGroup {
private static final int SCROLL = 0;
private static final int JUSTIFY = 1;
private static final int DISMISS_CONTROLS = 2;
// private static final int ANIMATION_DURATION = 750;
// private static final int IDLE_TIMEOUT = 3 * 1000;
private int mOrientation;
//宽度或高度,具体根据Orientation来决定
private int mSize;
//private Drawable mDecreaseButtonDrawable;
//private Drawable mIncreaseButtonDrawable;
//private ImageButton mDecreaseButton;
//private ImageButton mIncreaseButton;
//private PopupWindow mDecreasePopup;
//private PopupWindow mIncreasePopup;
private int mIndex;
private int mPosition;
private Scroller mScroller;
private Map<View, Integer> mViews;
private SpinnerAdapter mAdapter;
private int mPackedViews;
private GestureDetector mGestureDetector;
private Rect mGlobal;
private int mAnimationDuration;
public Switcher(Context context, AttributeSet attrs) {
super(context, attrs);
int[] linerarLayoutAttrs = {
android.R.attr.orientation
};
TypedArray a = context.obtainStyledAttributes(attrs, linerarLayoutAttrs);
mOrientation = a.getInteger(0, LinearLayout.HORIZONTAL);
a.recycle();
// a = context.obtainStyledAttributes(attrs, R.styleable.Switcher);
// mDecreaseButtonDrawable = a.getDrawable(R.styleable.Switcher_decreaseButton);
// mIncreaseButtonDrawable = a.getDrawable(R.styleable.Switcher_increaseButton);
// mAnimationDuration = a.getInteger(R.styleable.Switcher_animationDuration, ANIMATION_DURATION);
// mIdleTimeout = a.getInteger(R.styleable.Switcher_idleTimeout, IDLE_TIMEOUT);
// a.recycle();
// if(mDecreaseButtonDrawable == null) {
// throw new IllegalArgumentException(a.getPositionDescription() + ": decreaseButton attrubute not specified.");
// }
// if(mIncreaseButtonDrawable == null) {
// throw new IllegalArgumentException(a.getPositionDescription() + ": increaseButton attrubute not specified.");
// }
// mDecreaseButton = new ImageButton(context);
// mDecreaseButton.setEnabled(false);
// mDecreaseButton.setBackgroundDrawable(mDecreaseButtonDrawable);
// mIncreaseButton = new ImageButton(context);
// mIncreaseButton.setEnabled(false);
// mIncreaseButton.setBackgroundDrawable(mIncreaseButtonDrawable);
//
// mDecreaseButton.setOnClickListener(new OnClickListener() {
// public void onClick(View v) {
// setPreviousView();
// }
// });
// mIncreaseButton.setOnClickListener(new OnClickListener() {
// public void onClick(View v) {
// setNextView();
// }
// });
mScroller = new Scroller(context);
mIndex = -1;
mPosition = -1;
mPackedViews = -1;
mViews = new HashMap<View, Integer>();
mGestureDetector = new GestureDetector(gestureListener);
mGestureDetector.setIsLongpressEnabled(false);
setFocusable(true);
setFocusableInTouchMode(true);
// mDecreasePopup = new PopupWindow(mDecreaseButton, mDecreaseButtonDrawable.getIntrinsicWidth(), mDecreaseButtonDrawable.getIntrinsicHeight());
// mIncreasePopup = new PopupWindow(mIncreaseButton, mIncreaseButtonDrawable.getIntrinsicWidth(), mIncreaseButtonDrawable.getIntrinsicHeight());
// mDecreasePopup.setAnimationStyle(android.R.style.Animation_Toast);
// mIncreasePopup.setAnimationStyle(android.R.style.Animation_Toast);
mGlobal = new Rect();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mSize = mOrientation == LinearLayout.HORIZONTAL? getMeasuredWidth() : getMeasuredHeight();
}
private int getPackedViews(int offset) {
int size = mSize;
int start = offset / size;
int numViews = offset % size != 0? 1 : 0;
//都是位操作,从左往右依次执行,如果offset能被size整除则直接得出结果,如果不能整除,则结果再加1.
return start << 1 | numViews;
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == DISMISS_CONTROLS) {
// mDecreasePopup.dismiss();
// mIncreasePopup.dismiss();
return;
}
Log.v("", "handleMessage-what:"+msg.what);
mScroller.computeScrollOffset();
int currX = mScroller.getCurrX();
int delta = mPosition - currX;
mPosition = currX;
int packed = getPackedViews(mPosition);
manageViews(packed);
scroll(delta);
if (!mScroller.isFinished()) {
Log.v("", "mScroller.isFinished()=false");
handler.sendEmptyMessage(msg.what);
} else {
if (msg.what == SCROLL) {
justify();
} else {
mIndex = mPosition / mSize;
setupButtons();
}
}
}
};
// private long mIdleTimeout;
//最后的矫正
private void justify() {
int offset = mPosition % mSize;
if (offset != 0) {
int endPosition = mPosition - offset;
//如果移动超过中线则...
if (offset > mSize / 2) {
endPosition += mSize;
}
mScroller.startScroll(mPosition, 0, endPosition - mPosition, 0);
handler.sendEmptyMessage(JUSTIFY);
} else {
mIndex = mPosition / mSize;
setupButtons();
}
}
private void scroll(int offset) {
if (mOrientation == LinearLayout.HORIZONTAL) {
for (View view : mViews.keySet()) {
view.offsetLeftAndRight(offset);
}
} else {
for (View view : mViews.keySet()) {
view.offsetTopAndBottom(offset);
}
}
invalidate();
}
private void setupButtons() {
// if (mAdapter != null) {
// boolean enabled = mIndex > 0;
// mDecreaseButton.setEnabled(enabled);
// enabled = mIndex + 1 < mAdapter.getCount();
// mIncreaseButton.setEnabled(enabled);
// }
}
public void setSelection(int index, boolean animate) {
if (index == mIndex) {
return;
}
int endPosition = index * mSize;
int diff = Math.abs(index - mIndex);
int sign = index > mIndex? 1 : -1;
mIndex = index;
if (diff > 1) {
mPosition = endPosition - sign * mSize;
}
if (animate) {
mScroller.startScroll(mPosition, 0, endPosition - mPosition, 0, mAnimationDuration);
handler.removeMessages(JUSTIFY);
handler.removeMessages(SCROLL);
handler.sendEmptyMessage(JUSTIFY);
} else {
mPosition = endPosition;
manageViews(index << 1);
setupButtons();
invalidate();
}
}
private void manageViews(int packedViews) {
if (packedViews == mPackedViews) {
return;
}
Log.v("", "manageViews-packedViews:"+packedViews);
mPackedViews = packedViews;
int startIdx = packedViews >> 1;
int endIdx = startIdx + (packedViews & 1);//如果packedViews是奇数endIdx=startIdx,如果是偶数则endIdx=startIdx+1.
int viewIdx = startIdx;
while (viewIdx <= endIdx) {
if (!mViews.containsValue(viewIdx)) {
if (viewIdx >= 0 && viewIdx < mAdapter.getCount()) {
View view = mAdapter.getView(viewIdx, null, this);
mViews.put(view, viewIdx);
addView(view);
}
}
viewIdx++;
}
// remove not visible views
Iterator<View> iterator = mViews.keySet().iterator();
while (iterator.hasNext()) {
View view = iterator.next();
int idx = mViews.get(view);
if (idx < startIdx || idx > endIdx) {
iterator.remove();
removeView(view);
}
}
}
// public int getSelection() {
// return mIndex;
// }
//
// public void setPreviousView() {
// if (mAdapter != null && mIndex > 0) {
// setSelection(mIndex-1, true);
// setupDismiss();
// }
// }
//
// public void setNextView() {
// if (mAdapter != null && mIndex + 1 < mAdapter.getCount()) {
// setSelection(mIndex+1, true);
// setupDismiss();
// }
// }
public void setAdapter(SpinnerAdapter adapter) {
mAdapter = adapter;
if (mAdapter != null) {
setSelection(0, false);
setupButtons();
}
}
private void setupDismiss() {
// handler.removeMessages(DISMISS_CONTROLS);
// handler.sendEmptyMessageDelayed(DISMISS_CONTROLS, mIdleTimeout);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean rc = mGestureDetector.onTouchEvent(event);
//有数据时应该是没有机会执行里面的代码
if (!rc && event.getAction() == MotionEvent.ACTION_UP) {
justify();
}
return true;
}
SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
requestFocus();
popup();
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mAdapter != null) {
int distance = (int) (mOrientation == LinearLayout.HORIZONTAL? distanceX : distanceY);
int pos = mPosition + distance;
if (pos >= 0 && pos < (mAdapter.getCount() - 1) * mSize) {
mPosition = pos;
int packed = getPackedViews(mPosition);
manageViews(packed);
scroll(-distance);
return true;
}
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mAdapter != null) {
Log.v("", "onFling");
float velocity = mOrientation == LinearLayout.HORIZONTAL? velocityX : velocityY;
mScroller.fling(mPosition, 0, (int) -velocity, 0,
0, (mAdapter.getCount() - 1) * mSize,
0, 0);
handler.removeMessages(JUSTIFY);
handler.removeMessages(SCROLL);
handler.sendEmptyMessage(SCROLL);
return true;
}
return false;
}
};
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
if (gainFocus) {
popup();
} else {
handler.removeMessages(DISMISS_CONTROLS);
// mDecreasePopup.dismiss();
// mIncreasePopup.dismiss();
}
}
private void popup() {
// if (mDecreasePopup.isShowing() && mIncreasePopup.isShowing()) {
// return;
// }
getGlobalVisibleRect(mGlobal);
// if (mOrientation == LinearLayout.HORIZONTAL) {
// mDecreasePopup.showAtLocation(this,
// Gravity.NO_GRAVITY,
// mGlobal.left,
// mGlobal.centerY() - mDecreasePopup.getHeight()/2);
// mIncreasePopup.showAtLocation(this,
// Gravity.NO_GRAVITY,
// mGlobal.right - mIncreasePopup.getWidth(),
// mGlobal.centerY() - mIncreasePopup.getHeight()/2);
// } else {
// // TODO: re-position when Switcher is at the very top/bottom of screen
// mDecreasePopup.showAtLocation(this,
// Gravity.NO_GRAVITY,
// mGlobal.centerX() - mDecreasePopup.getWidth()/2,
// mGlobal.top-mDecreasePopup.getHeight());
// mIncreasePopup.showAtLocation(this,
// Gravity.NO_GRAVITY,
// mGlobal.centerX() - mIncreasePopup.getWidth()/2,
// mGlobal.bottom);
// }
setupDismiss();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (View view : mViews.keySet()) {
if (view.getWidth() == 0) {
// new View: not layout()ed
int idx = mViews.get(view);
if (mOrientation == LinearLayout.HORIZONTAL) {
int left = mSize * idx - mPosition;
view.layout(left, 0, left+r-l, b-t);
} else {
int top = mSize * idx - mPosition;
view.layout(0, top, r-l, top+b-t);
}
}
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// mDecreasePopup.dismiss();
// mIncreasePopup.dismiss();
}
public void setInterpolator(Interpolator interpolator) {
mScroller = new Scroller(getContext(), interpolator);
}
}