前段時間美工設計了一個控制項,辛辛苦苦花了2天才弄出來,結果偶然看到了網上原來有開源項目...
這個控制項可以上下滾動,通過adapter可以拿到中間選中部分的值。。
先上個先:
再上代碼。。
這個是控制項的view
注意哦:這個只是中間的白色的滾動部分和綠色部分的代碼,周圍的背景需要自己用xml布局。。圖片啥的就不發了,畢竟是在公司做的。。。而且我上的圖是用了2個這個控制項排在一起的效果
public class ScrollListView extends View {public static final String TAG = ScrollListView.class.getSimpleName();static final int UP = 0;//滑動方向,向上static final int DOWN = 1;//滑動方向,向下private Context mContext;private GestureDetector mGestureDetector;private ScrollListViewAdapter<?> mData;private Paint mPaint;private int offset = 0;// 位移量,向上遞減,向下遞增//private int offsetGaol;private int mMaxPos;//滑到最後一條元素的座標private int mItemHeight = 50;// 一行元素的高度private int mHalfItemHeight;//半行元素的高度private int mHeight;//繪製內容高度private int mHalfHeight;//內容高度的一半private int mCurrentMiddleIndex;//當前控制項正中間的元素在list的索引值private int mFlingVelocity;//fling的速度private int mTouchInitialOffset;//臨時變數儲存觸摸前的offsetprivate boolean mDragging;//是否在觸摸狀態private float mStartTouchPos;//觸摸起點座標private float mTextOffsetY;//字型置中位移量private boolean isCenter = false;// 讓字置中的微調控制,為true進行調整,為false不進行調整private int mDirect = -1;//方向private Bitmap mTop;private Bitmap mBottom;private float mTextPaddingLeft = 10;/** * 控制項觸摸事件 * @author lhxia * */public interface ScrollViewListener {/** * 觸摸開始調用 * * @param y * 觸摸第一個點的Y座標 */public void scrollViewTouchStart(float y);/** * 觸摸移動調用 * * @param y * 當前觸摸點的Y座標 */public void scrollViewTouchMove(float y);/** * 觸摸完調用 */public void scrollViewTouchEnd();/** * 當fling的時候調用 * * @param vY * Y方向座標 */public void scrollViewFling(float vY);/** * 更新控制項 */public void scrollViewDraw();/** * 速度為0時調用 */public void onStop(int index);};private ScrollViewListener mScrollViewListener = new ScrollViewListener() {@Overridepublic void scrollViewTouchStart(float y) {Log.e(TAG, "on touch start");mDragging = true;mStartTouchPos = y;mTouchInitialOffset = offset;mFlingVelocity = 0;}@Overridepublic void scrollViewTouchMove(float y) {Log.e(TAG, "on move");float dis = mStartTouchPos - y;setDirect(dis);offset = trap((int) (mTouchInitialOffset - dis));updateDisplay();}@Overridepublic void scrollViewTouchEnd() {Log.e(TAG, "on touch end");mDragging = false;//offsetGaol = offset;invalidate();isCenter = true;}@Overridepublic void scrollViewFling(float vY) {Log.e(TAG, "on fling");mDragging = false;//offsetGaol = offset;mFlingVelocity = (int) (-vY);updateDisplay();}@Overridepublic void scrollViewDraw() {Log.d(TAG, "on draw");if (isCenter) {updateDisplay();isCenter = false;} else if (mFlingVelocity != 0) {updateDisplay();}}@Overridepublic void onStop(int index) {if(mData != null){mData.onRefreshIndex(index);}}};/** * 判斷滑動方向 * @param dis 滑動距離 */private void setDirect(float dis) {if (dis > 0) {mDirect = UP;} else {mDirect = DOWN;}}/** * 更新offset,並調用invalidate重新整理介面 */private void updateDisplay() {if (!mDragging) {int offsetDelta = 0;if (mFlingVelocity != 0) {//速度不為0時,使控制項減速Log.d(TAG, "speed down, v = " + mFlingVelocity);offsetDelta = mFlingVelocity / 40;// 速度遞減if (mFlingVelocity > 90) {mFlingVelocity -= 90;} else if (mFlingVelocity < -90) {mFlingVelocity += 90;} else {mFlingVelocity = 0;Log.d(TAG, "stop, v = " + mFlingVelocity);isCenter = true;}offset -= offsetDelta;if (offset <= -(mMaxPos - (mHeight / 2) - mItemHeight)) {offset = -(mMaxPos - (mHeight / 2) - mItemHeight);mFlingVelocity = 0;isCenter = true;Log.d(TAG, "stop, v = " + mFlingVelocity);}if (offset >= mHalfHeight) {offset = mHalfHeight;mFlingVelocity = 0;isCenter = true;Log.d(TAG, "stop, v = " + mFlingVelocity);}//offsetGaol = offset;} else {//當速度為0時,元素可能並沒有置中,這裡調整置中int delta = (mHalfHeight - offset) % mItemHeight;if (delta <= mHalfItemHeight - 10 && delta != 0) {delta += 1;} else if (delta > mHalfItemHeight + 10) {delta -= 1;} else {if (delta >= mHalfItemHeight) {delta += 1;} else if (delta < mHalfItemHeight) {delta -= 1;}}if (delta != 0) {isCenter = true;}if (mDirect == DOWN) {Log.d(TAG, "direct down");offset += delta;} else if (mDirect == UP) {Log.d(TAG, "direct up");offset += delta;}if(mScrollViewListener != null){mScrollViewListener.onStop(computeCurrentMiddlePosition());}}}invalidate();}/** * 計算在資料的list中,當前顯示在控制項正中間的index * * @return 當前控制項中間的元素的索引 */private int computeCurrentMiddlePosition() {double f;if (offset <= 0) {f = ((-offset) + mHalfHeight) * 1.0 / mItemHeight;} else {f = (mHalfHeight - offset) * 1.0 / mItemHeight;}mCurrentMiddleIndex = (int) Math.round(f);if(mData != null){Log.d(TAG, "compute CurrentMiddlePosition is : "+ mCurrentMiddleIndex + " size " + mData.size() + " offset : " + offset + "\n"+ "halfheight" + mHalfHeight);}else {return -1;}return mCurrentMiddleIndex;}/** * 檢測offset的範圍 * @param pos * @return 返回檢測後的offset */private int trap(int pos) {if (pos >= mHeight / 2)return mHeight / 2;if (pos <= -(mMaxPos - (mHeight / 2) - mItemHeight))return -(mMaxPos - (mHeight / 2) - mItemHeight);return pos;}/** * 擷取當前中間的元素在list裡面的index * @return */public int getMiddleIndex(){computeCurrentMiddlePosition();return mCurrentMiddleIndex;}/** * 畫9.png圖片 * @param c canvas * @param id 圖片id * @param r1 圖片範圍 */private void drawNinepath(Canvas c, int id, Rect r1) {Bitmap bmp = BitmapFactory.decodeResource(getResources(), id);NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);patch.draw(c, r1);}public ScrollListView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, float velocityY) {if (mScrollViewListener != null) {mScrollViewListener.scrollViewFling(velocityY);}return true;}});TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.ScrollListView); int textColor = type.getColor(R.styleable.ScrollListView_textColor, 0XFF00FF00); //提供預設值,放置未指定 float textSize = type.getDimension(R.styleable.ScrollListView_textSize, 25); mTextPaddingLeft = type.getDimension(R.styleable.ScrollListView_paddingLeft, 25); type.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true);mPaint.setTextSize(textSize);mTextOffsetY = (mItemHeight - mPaint.getTextSize()) / 2;mPaint.setColor(textColor);mPaint.setTypeface(Typeface.SANS_SERIF);mTop = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg_condition_top_alpha);mBottom = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg_condition_bottom_alpha);}private void init(){if(mData != null){mMaxPos = mData.size() * mItemHeight;}offset = mHalfHeight;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {mHeight = MeasureSpec.getSize(heightMeasureSpec);mHalfHeight = mHeight / 2;mHalfItemHeight = mItemHeight / 2;offset = mHalfHeight;//offsetGaol = offset;computeCurrentMiddlePosition();super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// // 畫中間線,用於調試// canvas.drawLine(0, mHalfHeight, getMeasuredWidth(), mHalfHeight,// mPaint);// canvas.drawLine(0, mHalfHeight - mItemHeight, getMeasuredWidth(),// mHalfHeight - mItemHeight, mPaint);float y = 0;if(mData != null){int size = mData.size();String tmp;for (int i = 0; i < size; i++) {y = i* mItemHeight + offset - mTextOffsetY + 20;tmp = mData.getValue(i);if(tmp != null && y > -20 || y < mHeight + 20){canvas.drawText(tmp, mTextPaddingLeft, i* mItemHeight + offset - mTextOffsetY + 20, mPaint);}}if (mScrollViewListener != null) {mScrollViewListener.scrollViewDraw();}}// canvas.drawLine(0, offset, getMeasuredWidth(), offset, mPaint); drawNinepath(canvas, R.drawable.bg_condition_checked, new Rect(0, (int)(mHalfHeight - mHalfItemHeight + 2), getMeasuredWidth(), (int)(mHalfHeight + mHalfItemHeight - 1)));//畫2端漸層蒙版//drawNinepath(canvas, R.drawable.bg_condition_top_1,//new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight())); canvas.drawBitmap(mTop, 0, 0, mPaint); canvas.drawBitmap(mBottom, 0, mHeight - 4.3f, mPaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mGestureDetector.onTouchEvent(event)) {return true;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mScrollViewListener.scrollViewTouchStart(event.getY());break;case MotionEvent.ACTION_MOVE:mScrollViewListener.scrollViewTouchMove(event.getY());break;case MotionEvent.ACTION_UP:mScrollViewListener.scrollViewTouchEnd();break;}return true;}public void setAdapter(ScrollListViewAdapter<?> adapter){mData = adapter;init();invalidate();}public ScrollListViewAdapter<?> getAdapter(){return mData;}}
這個是adapter,用來把資料繫結到view上的:
public abstract class ScrollListViewAdapter<T> {public abstract String getValue(int index);public abstract int size();/** * 當控制項滾動的時候由控制項調用 * @param index 索引 * @return */public abstract int onRefreshIndex(int index);public abstract String getId(int index);public abstract T getItem(int index);public abstract void setList(List<T> data);public ScrollListViewAdapter(List<T> data){}}
代碼寫的不好。。如果有什麼疑問的請指教。。