diff --git a/.gitignore b/.gitignore
index ce27571..3c0fdad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
#idea
.idea/
+*.iml
# Built application files
*.apk
diff --git a/README.md b/README.md
index 24927cd..531031a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,66 @@
# WheelView-Android
Selector with wheel view, applicable to selecting money or other short length values.
+
+Use with Gradle:
+---
+
+```
+dependencies {
+ compile 'com.lantouzi.wheelview:library:1.1.1'
+}
+```
+
Screenshot of Demo:
-
\ No newline at end of file
+---
+
+
+Usage
+---
+### Style the view in xml:
+
+* **lwvHighlightColor** highlight color for selected item and the cursor.
+* **lwvMarkColor** color of mark on normal status.
+* **lwvMarkTextColor** color of mark text on normal status.
+* **lwvIntervalFactor** factor for calculate interval using text width.(larger means sparser)
+* **lwvMarkRatio** ratio that decides how short is the short mark than the long mark.
+* **lwvCursorSize** size(width) of the cursor upside.
+* **lwvMarkTextSize** text size of mark text on normal status.
+* **lwvCenterMarkTextSize** text size of the center mark text (on selected status)
+* **lwvAdditionalCenterMark** additional text used for unit of the center mark.
+
+### Listener
+
+```
+public interface OnWheelItemSelectedListener {
+ // Called each time when the center index changed.
+ void onWheelItemChanged(WheelView wheelView, int position);
+
+ // Called only when the center index selected and wheel never moving to others.
+ void onWheelItemSelected(WheelView wheelView, int position);
+}
+```
+
+
+### Limit scope of selection.
+*(Added in 1.1.1)*
+
+* **setMinSelectableIndex/setMaxSelectableIndex** limit min/max index whitch is selectable in code.
+
+Check out the demo project for more information.
+
+License
+---
+
+ 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.
+
diff --git a/build.gradle b/build.gradle
index be515a8..f3b8339 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,8 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
-
+ classpath 'com.github.dcendents:android-maven-plugin:1.2'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/demo/build.gradle b/demo/build.gradle
index 270047a..306238e 100644
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "com.lantouzi.wheelview.demo"
minSdkVersion 11
targetSdkVersion 23
- versionCode 1
- versionName "1.0"
+ versionCode 4
+ versionName "1.1.0"
}
buildTypes {
release {
@@ -22,6 +22,5 @@ android {
dependencies {
compile project(path: ':library')
compile fileTree(dir: 'libs', include: ['*.jar'])
- testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
}
diff --git a/demo/demo.iml b/demo/demo.iml
index d88a457..96de818 100644
--- a/demo/demo.iml
+++ b/demo/demo.iml
@@ -65,26 +65,19 @@
-
-
+
-
-
-
-
-
-
-
+
diff --git a/demo/src/main/java/com/lantouzi/wheelview/demo/MainActivity.java b/demo/src/main/java/com/lantouzi/wheelview/demo/MainActivity.java
index e867b41..b283375 100644
--- a/demo/src/main/java/com/lantouzi/wheelview/demo/MainActivity.java
+++ b/demo/src/main/java/com/lantouzi/wheelview/demo/MainActivity.java
@@ -13,7 +13,7 @@
public class MainActivity extends AppCompatActivity {
private WheelView mWheelView, mWheelView2, mWheelView3, mWheelView4, mWheelView5;
- private TextView mSelectedTv;
+ private TextView mSelectedTv, mChangedTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -26,6 +26,7 @@ protected void onCreate(Bundle savedInstanceState) {
mWheelView4 = (WheelView) findViewById(R.id.wheelview4);
mWheelView5 = (WheelView) findViewById(R.id.wheelview5);
mSelectedTv = (TextView) findViewById(R.id.selected_tv);
+ mChangedTv = (TextView) findViewById(R.id.changed_tv);
final List items = new ArrayList<>();
for (int i = 1; i <= 40; i++) {
@@ -70,12 +71,29 @@ protected void onCreate(Bundle savedInstanceState) {
mWheelView3.setAdditionCenterMark("m");
mWheelView4.setItems(items);
+ mWheelView4.setEnabled(false);
mWheelView5.setItems(items);
+ mWheelView5.setMinSelectableIndex(3);
+ mWheelView5.setMaxSelectableIndex(items.size() - 3);
+
+ items.remove(items.size() - 1);
+ items.remove(items.size() - 2);
+ items.remove(items.size() - 3);
+ items.remove(items.size() - 4);
+
+ mSelectedTv.setText(String.format("onWheelItemSelected:%1$s", ""));
+ mChangedTv.setText(String.format("onWheelItemChanged:%1$s", ""));
+
mWheelView5.setOnWheelItemSelectedListener(new WheelView.OnWheelItemSelectedListener() {
@Override
- public void onWheelItemSelected(int position) {
- mSelectedTv.setText("选择:" + items.get(position) + "万");
+ public void onWheelItemSelected(WheelView wheelView, int position) {
+ mSelectedTv.setText(String.format("onWheelItemSelected:%1$s", wheelView.getItems().get(position)));
+ }
+
+ @Override
+ public void onWheelItemChanged(WheelView wheelView, int position) {
+ mChangedTv.setText(String.format("onWheelItemChanged:%1$s", wheelView.getItems().get(position)));
}
});
diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml
index 4ac7b45..015a79e 100644
--- a/demo/src/main/res/layout/activity_main.xml
+++ b/demo/src/main/res/layout/activity_main.xml
@@ -74,6 +74,13 @@
app:lwvMarkTextColor="#CACACA"
app:lwvMarkTextSize="12sp"/>
+
+
-
+
@@ -65,22 +65,13 @@
+
-
-
-
-
-
-
-
-
-
-
diff --git a/library/src/main/java/com/lantouzi/wheelview/WheelView.java b/library/src/main/java/com/lantouzi/wheelview/WheelView.java
index 0bfcd84..06f1ff0 100644
--- a/library/src/main/java/com/lantouzi/wheelview/WheelView.java
+++ b/library/src/main/java/com/lantouzi/wheelview/WheelView.java
@@ -7,6 +7,8 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ViewCompat;
import android.text.TextPaint;
@@ -20,6 +22,7 @@
import com.lantouzi.wheelview.library.R;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -31,7 +34,7 @@ public class WheelView extends View implements GestureDetector.OnGestureListener
private Paint mMarkPaint;
private TextPaint mMarkTextPaint;
- private int mCenterIndex;
+ private int mCenterIndex = -1;
private int mHighlightColor, mMarkTextColor;
private int mMarkColor, mFadeMarkColor;
@@ -61,6 +64,9 @@ public class WheelView extends View implements GestureDetector.OnGestureListener
private GestureDetectorCompat mGestureDetectorCompat;
// scroll control args ---- end
+ private int mLastSelectedIndex = -1;
+ private int mMinSelectableIndex = Integer.MIN_VALUE;
+ private int mMaxSelectableIndex = Integer.MAX_VALUE;
public WheelView(Context context) {
super(context);
@@ -90,17 +96,17 @@ protected void init(AttributeSet attrs) {
mNormalTextSize = density * 18;
mBottomSpace = density * 6;
- TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.WheelView);
+ TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.lwvWheelView);
if (ta != null) {
- mHighlightColor = ta.getColor(R.styleable.WheelView_lwvHighlightColor, mHighlightColor);
- mMarkTextColor = ta.getColor(R.styleable.WheelView_lwvMarkTextColor, mMarkTextColor);
- mMarkColor = ta.getColor(R.styleable.WheelView_lwvMarkColor, mMarkColor);
- mIntervalFactor = ta.getFloat(R.styleable.WheelView_lwvIntervalFactor, mIntervalFactor);
- mMarkRatio = ta.getFloat(R.styleable.WheelView_lwvMarkRatio, mMarkRatio);
- mAdditionCenterMark = ta.getString(R.styleable.WheelView_lwvAdditionalCenterMark);
- mCenterTextSize = ta.getDimension(R.styleable.WheelView_lwvCenterMarkTextSize, mCenterTextSize);
- mNormalTextSize = ta.getDimension(R.styleable.WheelView_lwvMarkTextSize, mNormalTextSize);
- mCursorSize = ta.getDimension(R.styleable.WheelView_lwvCursorSize, mCursorSize);
+ mHighlightColor = ta.getColor(R.styleable.lwvWheelView_lwvHighlightColor, mHighlightColor);
+ mMarkTextColor = ta.getColor(R.styleable.lwvWheelView_lwvMarkTextColor, mMarkTextColor);
+ mMarkColor = ta.getColor(R.styleable.lwvWheelView_lwvMarkColor, mMarkColor);
+ mIntervalFactor = ta.getFloat(R.styleable.lwvWheelView_lwvIntervalFactor, mIntervalFactor);
+ mMarkRatio = ta.getFloat(R.styleable.lwvWheelView_lwvMarkRatio, mMarkRatio);
+ mAdditionCenterMark = ta.getString(R.styleable.lwvWheelView_lwvAdditionalCenterMark);
+ mCenterTextSize = ta.getDimension(R.styleable.lwvWheelView_lwvCenterMarkTextSize, mCenterTextSize);
+ mNormalTextSize = ta.getDimension(R.styleable.lwvWheelView_lwvMarkTextSize, mNormalTextSize);
+ mCursorSize = ta.getDimension(R.styleable.lwvWheelView_lwvCursorSize, mCursorSize);
}
mFadeMarkColor = mHighlightColor & 0xAAFFFFFF;
mIntervalFactor = Math.max(1, mIntervalFactor);
@@ -196,7 +202,11 @@ private int measureHeight(int heightMeasure) {
}
public void fling(int velocityX, int velocityY) {
- mScroller.fling(getScrollX(), getScrollY(), velocityX, velocityY, (int) -mMaxOverScrollDistance, (int) (mContentRectF.width() - mMaxOverScrollDistance), 0, 0, (int) mMaxOverScrollDistance, 0);
+ mScroller.fling(getScrollX(), getScrollY(),
+ velocityX, velocityY,
+ (int) (-mMaxOverScrollDistance + mMinSelectableIndex * mIntervalDis), (int) (mContentRectF.width() - mMaxOverScrollDistance - (mMarkCount - 1 - mMaxSelectableIndex) * mIntervalDis),
+ 0, 0,
+ (int) mMaxOverScrollDistance, 0);
ViewCompat.postInvalidateOnAnimation(this);
}
@@ -234,17 +244,17 @@ protected void onDraw(Canvas canvas) {
start = Math.max(start, -mViewScopeSize * 2);
end = Math.min(end, mMarkCount + mViewScopeSize * 2);
- // 对两端的绘制范围进行扩展
- if (mCenterIndex == mMarkCount - 1) {
+ // extends both ends
+ if (mCenterIndex == mMaxSelectableIndex) {
end += mViewScopeSize;
- } else if (mCenterIndex == 0) {
+ } else if (mCenterIndex == mMinSelectableIndex) {
start -= mViewScopeSize;
}
float x = start * mIntervalDis;
- float markHeight = mHeight - mBottomSpace - mCenterTextSize - mTopSpace;
- // 小刻度的Y方向缩小量
+ float markHeight = mHeight - mBottomSpace - mCenterTextSize - mTopSpace;
+ // small scale Y offset
float smallMarkShrinkY = markHeight * (1 - mMarkRatio) / 2f;
smallMarkShrinkY = Math.min((markHeight - mMarkWidth) / 2f, smallMarkShrinkY);
@@ -306,23 +316,13 @@ protected void onDraw(Canvas canvas) {
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mItems == null || mItems.size() == 0) {
+ if (mItems == null || mItems.size() == 0 || !isEnabled()) {
return false;
}
boolean ret = mGestureDetectorCompat.onTouchEvent(event);
if (!mFling && MotionEvent.ACTION_UP == event.getAction()) {
- if (getScrollX() < -mMaxOverScrollDistance) {
- mScroller.startScroll(getScrollX(), 0, (int) -mMaxOverScrollDistance - getScrollX(), 0);
- invalidate();
- ret = true;
- } else if (getScrollX() > mContentRectF.width() - mMaxOverScrollDistance) {
- mScroller.startScroll(getScrollX(), 0, (int) (mContentRectF.width() - mMaxOverScrollDistance) - getScrollX(), 0);
- invalidate();
- ret = true;
- } else {
- autoSettle();
- ret = true;
- }
+ autoSettle();
+ ret = true;
}
return ret || super.onTouchEvent(event);
}
@@ -352,19 +352,40 @@ private void autoSettle() {
int sx = getScrollX();
float dx = mCenterIndex * mIntervalDis - sx - mMaxOverScrollDistance;
mScroller.startScroll(sx, 0, (int) dx, 0);
- invalidate();
+ postInvalidate();
+ if (mLastSelectedIndex != mCenterIndex) {
+ mLastSelectedIndex = mCenterIndex;
+ if (null != mOnWheelItemSelectedListener) {
+ mOnWheelItemSelectedListener.onWheelItemSelected(this, mCenterIndex);
+ }
+ }
+ }
+
+ /**
+ * limit center index in bounds.
+ *
+ * @param center
+ * @return
+ */
+ private int safeCenter(int center) {
+ if (center < mMinSelectableIndex) {
+ center = mMinSelectableIndex;
+ } else if (center > mMaxSelectableIndex) {
+ center = mMaxSelectableIndex;
+ }
+ return center;
}
private void refreshCenter(int offsetX) {
int offset = (int) (offsetX + mMaxOverScrollDistance);
- mCenterIndex = Math.round(offset / mIntervalDis);
- if (mCenterIndex < 0) {
- mCenterIndex = 0;
- } else if (mCenterIndex > mMarkCount - 1) {
- mCenterIndex = mMarkCount - 1;
+ int tempIndex = Math.round(offset / mIntervalDis);
+ tempIndex = safeCenter(tempIndex);
+ if (mCenterIndex == tempIndex) {
+ return;
}
+ mCenterIndex = tempIndex;
if (null != mOnWheelItemSelectedListener) {
- mOnWheelItemSelectedListener.onWheelItemSelected(mCenterIndex);
+ mOnWheelItemSelectedListener.onWheelItemChanged(this, mCenterIndex);
}
}
@@ -393,13 +414,52 @@ public void smoothSelectIndex(int index) {
invalidate();
}
+ public int getMinSelectableIndex() {
+ return mMinSelectableIndex;
+ }
+
+ public void setMinSelectableIndex(int minSelectableIndex) {
+ if (minSelectableIndex > mMaxSelectableIndex) {
+ minSelectableIndex = mMaxSelectableIndex;
+ }
+ mMinSelectableIndex = minSelectableIndex;
+ int afterCenter = safeCenter(mCenterIndex);
+ if (afterCenter != mCenterIndex) {
+ selectIndex(afterCenter);
+ }
+ }
+
+ public int getMaxSelectableIndex() {
+ return mMaxSelectableIndex;
+ }
+
+ public void setMaxSelectableIndex(int maxSelectableIndex) {
+ if (maxSelectableIndex < mMinSelectableIndex) {
+ maxSelectableIndex = mMinSelectableIndex;
+ }
+ mMaxSelectableIndex = maxSelectableIndex;
+ int afterCenter = safeCenter(mCenterIndex);
+ if (afterCenter != mCenterIndex) {
+ selectIndex(afterCenter);
+ }
+ }
+
public List getItems() {
return mItems;
}
public void setItems(List items) {
- mItems = items;
+ if (mItems == null) {
+ mItems = new ArrayList<>();
+ } else {
+ mItems.clear();
+ }
+ mItems.addAll(items);
mMarkCount = null == mItems ? 0 : mItems.size();
+ if (mMarkCount > 0) {
+ mMinSelectableIndex = Math.max(mMinSelectableIndex, 0);
+ mMaxSelectableIndex = Math.min(mMaxSelectableIndex, mMarkCount - 1);
+ }
mCenterIndex = Math.min(mCenterIndex, mMarkCount);
calcIntervalDis();
invalidate();
@@ -440,19 +500,20 @@ public boolean onSingleTapUp(MotionEvent e) {
@Override
public void onLongPress(MotionEvent e) {
+
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float dis = distanceX;
float scrollX = getScrollX();
- if (scrollX < 2 * -mMaxOverScrollDistance) {
+ if (scrollX < mMinSelectableIndex * mIntervalDis - 2 * mMaxOverScrollDistance) {
dis = 0;
- } else if (scrollX < -mMaxOverScrollDistance) {
+ } else if (scrollX < mMinSelectableIndex * mIntervalDis - mMaxOverScrollDistance) {
dis = distanceX / 4.f;
- } else if (scrollX > mContentRectF.width()) {
+ } else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis) {
dis = 0;
- } else if (scrollX > mContentRectF.width() - mMaxOverScrollDistance) {
+ } else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis - mMaxOverScrollDistance) {
dis = distanceX / 4.f;
}
scrollBy((int) dis, 0);
@@ -463,7 +524,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float scrollX = getScrollX();
- if (scrollX < -mMaxOverScrollDistance || scrollX > mContentRectF.width() - mMaxOverScrollDistance) {
+ if (scrollX < -mMaxOverScrollDistance + mMinSelectableIndex * mIntervalDis || scrollX > mContentRectF.width() - mMaxOverScrollDistance - (mMarkCount - 1 - mMaxSelectableIndex) * mIntervalDis) {
return false;
} else {
mFling = true;
@@ -472,7 +533,71 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve
}
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.index = getSelectedPosition();
+ ss.min = mMinSelectableIndex;
+ ss.max = mMaxSelectableIndex;
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mMinSelectableIndex = ss.min;
+ mMaxSelectableIndex = ss.max;
+ selectIndex(ss.index);
+ requestLayout();
+ }
+
public interface OnWheelItemSelectedListener {
- void onWheelItemSelected(int position);
+ void onWheelItemChanged(WheelView wheelView, int position);
+
+ void onWheelItemSelected(WheelView wheelView, int position);
+ }
+
+ static class SavedState extends BaseSavedState {
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ int index;
+ int min;
+ int max;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ index = in.readInt();
+ min = in.readInt();
+ max = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(index);
+ out.writeInt(min);
+ out.writeInt(max);
+ }
+
+ @Override
+ public String toString() {
+ return "WheelView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " index=" + index + " min=" + min + " max=" + max + "}";
+ }
}
}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index e1b29d7..dd5802f 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -1,6 +1,6 @@
-
+
@@ -9,7 +9,6 @@
-
\ No newline at end of file
diff --git a/preview/demo.jpg b/preview/demo.jpg
deleted file mode 100644
index d1da9aa..0000000
Binary files a/preview/demo.jpg and /dev/null differ
diff --git a/preview/demo.png b/preview/demo.png
new file mode 100644
index 0000000..9585540
Binary files /dev/null and b/preview/demo.png differ