Android(安卓)仿拼多多可水平滚动RecyclerView,自定义滚动条滚动距离
16lz
2021-01-25
Android 仿拼多多可水平滚动RecyclerView,自定义滚动条滚动距离
2020年,希望大家一切平安如意,毕竟这是个出人意料的多事之秋。
一.效果图:
二.快速实现:
1.主函数代码:
import android.content.Context;import android.graphics.Color;import android.os.Bundle;import android.os.Handler;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.DisplayMetrics;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import me.samlss.broccoli.Broccoli;import me.samlss.broccoli.PlaceholderParameter;import me.samlss.utils.ScreenUtils;import me.samlss.utils.WheelView;/** * 可参考 * https://blog.csdn.net/shenggaofei/article/details/78186177#comments_12759724 * https://blog.csdn.net/Blog_Sun/article/details/95338124 * * https://blog.csdn.net/hhw332704304/article/details/88971381 * https://blog.csdn.net/u010731746/article/details/83303190 * https://blog.csdn.net/qq_36347817/article/details/103529540?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase */public class WheelActivity extends AppCompatActivity { private Broccoli mBroccoli; private Handler mHandler = new Handler(); private WheelView mWheelView; private TextView tvAge; private PersonAgeAdapter mAgeAdapter; private MyAdapter mAdapter; private RecyclerView mRvAgeList,RvScroll; private int age_num= 0; private int mLastValue= 0; private int START_NUM= 12; private int END_NUM= 99;// private int [] endX; float endX = 0; private View view_slip_front,view_slip_front2,view_slip_front3,view_slip_front4; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wheel); view_slip_front = findViewById(R.id.view_slip_front); view_slip_front2 = findViewById(R.id.view_slip_front02); view_slip_front3 = findViewById(R.id.view_slip_front03); view_slip_front4 = findViewById(R.id.view_slip_front04); mRvAgeList = findViewById(R.id.RvAgeList); RvScroll = findViewById(R.id.RvScroll); mWheelView = findViewById(R.id.rsv_ruler); tvAge = findViewById(R.id.tv_age); confitAgeWheelView(); initAgeList();// initDatas(); initView(); } /** * 仿拼多多可水平滚动RecyclerView,自定义滚动条滚动距离 */ private void initView() { LinearLayoutManager manager=new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false); mRvAgeList.setLayoutManager(manager); mAdapter=new MyAdapter(this,START_NUM,22); mRvAgeList.setAdapter(mAdapter); // 这里的mRvHx是需要绑定滚动条的RecyclerView mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy);// float endX; // 整体的总宽度,注意是整体,包括在显示区域之外的。// int range = mRvAgeList.computeHorizontalScrollRange();// float density = getScreenDensity();// // 计算出溢出部分的宽度,即屏幕外剩下的宽度// float maxEndX = range - ScreenUtils.getScreenWidth(WheelActivity.this) + (25 * density) + 5;// // 滑动的距离//// endX[0] = endX[0] + dx;//// // 计算比例//// float proportion = endX[0] / maxEndX;//// //滑动的距离// endX += dx;// //计算比例// float proportion = endX / maxEndX;//// // 计算滚动条宽度// int transMaxRange = ((ViewGroup) view_slip_front.getParent()).getWidth() - view_slip_front.getWidth();// // 设置滚动条移动// view_slip_front.setTranslationX(transMaxRange * proportion); //整体的总宽度,注意是整体,包括在显示区域之外的。 int range = mRvAgeList.computeHorizontalScrollRange(); float density = getScreenDensity(); //计算出溢出部分的宽度,即屏幕外剩下的宽度 float maxEndX = range + (10 * density) + 5 - ScreenUtils.getScreenWidth(WheelActivity.this); //滑动的距离 endX += dx; //计算比例 float proportion = endX / maxEndX; //计算滚动条宽度 int transMaxRange = ((ViewGroup) view_slip_front.getParent()).getWidth() - view_slip_front.getWidth(); //设置滚动条移动 view_slip_front.setTranslationX(transMaxRange * proportion); //02 //计算滚动条宽度 int transMaxRange2 = ((ViewGroup) view_slip_front2.getParent()).getWidth() - view_slip_front2.getWidth(); //设置滚动条移动 view_slip_front2.setTranslationX(transMaxRange2 * proportion); //03 //计算滚动条宽度 int transMaxRange3 = ((ViewGroup) view_slip_front3.getParent()).getWidth() - view_slip_front3.getWidth(); //设置滚动条移动 view_slip_front3.setTranslationX(transMaxRange3 * proportion); //04 //计算滚动条宽度 int transMaxRange4 = ((ViewGroup) view_slip_front4.getParent()).getWidth() - view_slip_front4.getWidth(); //设置滚动条移动 view_slip_front4.setTranslationX(transMaxRange4 * proportion); } }); } public float getScreenDensity() { WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); if (wm != null) { wm.getDefaultDisplay().getMetrics(dm); } int width = dm.widthPixels;// 屏幕宽度(像素) int height = dm.heightPixels; // 屏幕高度(像素) float density = dm.density;//屏幕密度(0.75 / 1.0 / 1.5) int densityDpi = dm.densityDpi;//屏幕密度dpi(120 / 160 / 240) return density; } /** * Android WheelView横向选择器 */ private void confitAgeWheelView() { ArrayList localArrayList = new ArrayList(); int i = 18; while (i <= 60) { StringBuilder localStringBuilder = new StringBuilder(); localStringBuilder.append(String.valueOf(i)); localStringBuilder.append("岁"); localArrayList.add(localStringBuilder.toString()); i += 1; } mWheelView.setItems(localArrayList); mWheelView.selectIndex(0);//设置默认选择的年龄 tvAge.setText(mWheelView.getItems().get(0));//设置默认显示年龄// mWheelView.selectIndex(6); //监听 mWheelView.setOnWheelItemSelectedListener(new WheelView.OnWheelItemSelectedListener() { @Override public void onWheelItemChanged(WheelView wheelView, int position) { List items = wheelView.getItems(); String num = items.get(position); tvAge.setText(num+"");//根据改变的位置设置年龄 } @Override public void onWheelItemSelected(WheelView wheelView, int position) { age_num = position + 18;//选中数字因为position是从0开始的,所以要加上你初始化起始的数字大小// List items = wheelView.getItems();// String num = items.get(position);// String nums = items.get(position);// tvAge.setText(num+""); } }); } /** * 初始化年龄滑动条 */ private void initAgeList() { LinearLayoutManager mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); RvScroll.setLayoutManager(mLayoutManager); mAgeAdapter = new PersonAgeAdapter(START_NUM, END_NUM,WheelActivity.this); RvScroll.setAdapter(mAgeAdapter); RvScroll.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState);// mBDownStep.setEnabled(false); // 效果在暂停时显示, 否则会导致重绘异常 if (newState == RecyclerView.SCROLL_STATE_IDLE) { mAgeAdapter.highlightItem(getMiddlePosition()); RvScroll.scrollToPosition(getScrollPosition()); mLastValue = getMiddlePosition();// UserInfoManager.setAge(getMiddlePosition() + START_NUM);// mBDownStep.setEnabled(true); // 滑动时不可用, 停止时才可以 } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // 值是实时增加 tvAge.setText(String.valueOf(getMiddlePosition() + START_NUM)); } }); mAgeAdapter.highlightItem(getMiddlePosition()); } /** * 获取中间位置 * * @return 当前值 */ private int getMiddlePosition() { return getScrollPosition() + (PersonAgeAdapter.ITEM_NUM / 2); } /** * 获取滑动值, 滑动偏移 / 每个格子宽度 * * @return 当前值 */ private int getScrollPosition() { return (int) ((double) RvScroll.computeHorizontalScrollOffset() / (double) PersonAgeAdapter.getItemStdWidth()); } /** * 初始化数据 */ private void initDatas() { LinearLayoutManager manager=new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false); mRvAgeList.setLayoutManager(manager); mAdapter=new MyAdapter(this,START_NUM,END_NUM); mRvAgeList.setAdapter(mAdapter); mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if(newState==RecyclerView.SCROLL_STATE_IDLE){ mAdapter.highlightItem(getMiddlePositions()); //将位置移动到中间位置 ((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(getScrollPositions(),0); System.out.println(getScrollPositions()+""); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { tvAge.setText(String.valueOf(getMiddlePositions() + START_NUM)); } }); mAdapter.highlightItem(getMiddlePositions()); } /** * 获取中间位置的position * @return */ private int getMiddlePositions() { return getScrollPositions()+(mAdapter.ITEM_NUM/2); } /** * 获取滑动值, 滑动偏移 / 每个格子宽度 * * @return 当前值 */ private int getScrollPositions() { return (int) (((double) mRvAgeList.computeHorizontalScrollOffset() / (double) mAdapter.getItemWidth())+0.5f); }}
2.布局:
<?xml version="1.0" encoding="utf-8"?>
3.shape绘制圆角背景、渐变色背景
shape_bg_slip_behind.xml:
<?xml version="1.0" encoding="utf-8"?>
shape_bg_slip_front.xml:
<?xml version="1.0" encoding="utf-8"?>
shape_bg_slip_behind2.xml:
<?xml version="1.0" encoding="utf-8"?>
shape_bg_slip_front2.xml:
<?xml version="1.0" encoding="utf-8"?>
shape_bg_slip_front3.xml:
<?xml version="1.0" encoding="utf-8"?>
shape_bg_slip_front4.xml:
<?xml version="1.0" encoding="utf-8"?>
4.ScreenUtils获得屏幕相关的辅助类
package me.samlss.utils;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Rect;import android.util.DisplayMetrics;import android.view.View;import android.view.WindowManager;//获得屏幕相关的辅助类public class ScreenUtils{ private ScreenUtils() { /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } /** * 获得屏幕高度 * * @param context * @return */ public static int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; } /** * 获得屏幕宽度 * * @param context * @return */ public static int getScreenHeight(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.heightPixels; } /** * 获得状态栏的高度 * * @param context * @return */ public static int getStatusHeight(Context context) { int statusHeight = -1; try { Class<?> clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); int height = Integer.parseInt(clazz.getField("status_bar_height") .get(object).toString()); statusHeight = context.getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } return statusHeight; } /** * 获取当前屏幕截图,包含状态栏 * * @param activity * @return */ public static Bitmap snapShotWithStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, 0, width, height); view.destroyDrawingCache(); return bp; } /** * 获取当前屏幕截图,不包含状态栏 * * @param activity * @return */ public static Bitmap snapShotWithoutStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); Rect frame = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight); view.destroyDrawingCache(); return bp; }}
5.自定义类:(以下是Android RecyclerView 实现横向滚动效果)
Android RecyclerView 实现横向滚动效果
package me.samlss.utils;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;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;import android.text.TextUtils;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.SoundEffectConstants;import android.view.View;import android.widget.OverScroller;import java.util.ArrayList;import java.util.List;import me.samlss.broccoli_demo.R;/** * */public class WheelView extends View implements GestureDetector.OnGestureListener { public static final float DEFAULT_INTERVAL_FACTOR = 1.2f; public static final float DEFAULT_MARK_RATIO = 0.7f; private Paint mMarkPaint; private TextPaint mMarkTextPaint; private int mCenterIndex = -1; private int mHighlightColor, mMarkTextColor; private int mMarkColor, mFadeMarkColor; private int mHeight; private List mItems; private String mAdditionCenterMark; private OnWheelItemSelectedListener mOnWheelItemSelectedListener; private float mIntervalFactor = DEFAULT_INTERVAL_FACTOR; private float mMarkRatio = DEFAULT_MARK_RATIO; private int mMarkCount; private float mAdditionCenterMarkWidth; private Path mCenterIndicatorPath = new Path(); private float mCursorSize; private int mViewScopeSize; // scroll control args ---- start private OverScroller mScroller; private float mMaxOverScrollDistance; private RectF mContentRectF; private boolean mFling = false; private float mCenterTextSize, mNormalTextSize; private float mTopSpace, mBottomSpace; private float mIntervalDis; private float mCenterMarkWidth, mMarkWidth; 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); init(null); } public WheelView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public WheelView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } protected void init(AttributeSet attrs) { float density = getResources().getDisplayMetrics().density; mCenterMarkWidth = (int) (density * 1.5f + 0.5f); mMarkWidth = density; mHighlightColor = 0xFFF74C39; mMarkTextColor = 0xFF666666; mMarkColor = 0xFFEEEEEE; mCursorSize = density * 18; mCenterTextSize = density * 22; mNormalTextSize = density * 18; mBottomSpace = density * 6; TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.lwvWheelView); if (ta != null) { 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); mMarkRatio = Math.min(1, mMarkRatio); mTopSpace = mCursorSize + density * 2; mMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMarkTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mMarkTextPaint.setTextAlign(Paint.Align.CENTER); mMarkTextPaint.setColor(mHighlightColor); mMarkPaint.setColor(mMarkColor); mMarkPaint.setStrokeWidth(mCenterMarkWidth); mMarkTextPaint.setTextSize(mCenterTextSize); calcIntervalDis(); mScroller = new OverScroller(getContext()); mContentRectF = new RectF(); mGestureDetectorCompat = new GestureDetectorCompat(getContext(), this); selectIndex(0); } /** * calculate interval distance between items */ private void calcIntervalDis() { if (mMarkTextPaint == null) { return; } String defaultText = "888888"; Rect temp = new Rect(); int max = 0; if (mItems != null && mItems.size() > 0) { for (String i : mItems) { mMarkTextPaint.getTextBounds(i, 0, i.length(), temp); if (temp.width() > max) { max = temp.width(); } } } else { mMarkTextPaint.getTextBounds(defaultText, 0, defaultText.length(), temp); max = temp.width(); } if (!TextUtils.isEmpty(mAdditionCenterMark)) { mMarkTextPaint.setTextSize(mNormalTextSize); mMarkTextPaint.getTextBounds(mAdditionCenterMark, 0, mAdditionCenterMark.length(), temp); mAdditionCenterMarkWidth = temp.width(); max += temp.width(); } mIntervalDis = max * mIntervalFactor; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureWidth(int widthMeasureSpec) { int measureMode = MeasureSpec.getMode(widthMeasureSpec); int measureSize = MeasureSpec.getSize(widthMeasureSpec); int result = getSuggestedMinimumWidth(); switch (measureMode) { case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = measureSize; break; default: break; } return result; } private int measureHeight(int heightMeasure) { int measureMode = MeasureSpec.getMode(heightMeasure); int measureSize = MeasureSpec.getSize(heightMeasure); int result = (int) (mBottomSpace + mTopSpace * 2 + mCenterTextSize); switch (measureMode) { case MeasureSpec.EXACTLY: result = Math.max(result, measureSize); break; case MeasureSpec.AT_MOST: result = Math.min(result, measureSize); break; default: break; } return result; } public void fling(int velocityX, int velocityY) { 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); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w != oldw || h != oldh) { mHeight = h; mMaxOverScrollDistance = w / 2.f; mContentRectF.set(0, 0, (mMarkCount - 1) * mIntervalDis, h); mViewScopeSize = (int) Math.ceil(mMaxOverScrollDistance / mIntervalDis); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mCenterIndicatorPath.reset(); float sizeDiv2 = mCursorSize / 2f; float sizeDiv3 = mCursorSize / 3f; mCenterIndicatorPath.moveTo(mMaxOverScrollDistance - sizeDiv2 + getScrollX(), 0); mCenterIndicatorPath.rLineTo(0, sizeDiv3); mCenterIndicatorPath.rLineTo(sizeDiv2, sizeDiv2); mCenterIndicatorPath.rLineTo(sizeDiv2, -sizeDiv2); mCenterIndicatorPath.rLineTo(0, -sizeDiv3); mCenterIndicatorPath.close(); mMarkPaint.setColor(mHighlightColor); canvas.drawPath(mCenterIndicatorPath, mMarkPaint); int start = mCenterIndex - mViewScopeSize; int end = mCenterIndex + mViewScopeSize + 1; start = Math.max(start, -mViewScopeSize * 2); end = Math.min(end, mMarkCount + mViewScopeSize * 2); // extends both ends if (mCenterIndex == mMaxSelectableIndex) { end += mViewScopeSize; } else if (mCenterIndex == mMinSelectableIndex) { start -= mViewScopeSize; } float x = start * mIntervalDis; float markHeight = mHeight - mBottomSpace - mCenterTextSize - mTopSpace; // small scale Y offset float smallMarkShrinkY = markHeight * (1 - mMarkRatio) / 2f; smallMarkShrinkY = Math.min((markHeight - mMarkWidth) / 2f, smallMarkShrinkY); for (int i = start; i < end; i++) { float tempDis = mIntervalDis / 5f; // offset: Small mark offset Big mark for (int offset = -2; offset < 3; offset++) { float ox = x + offset * tempDis; if (i >= 0 && i <= mMarkCount && mCenterIndex == i) { int tempOffset = Math.abs(offset); if (tempOffset == 0) { mMarkPaint.setColor(mHighlightColor); } else if (tempOffset == 1) { mMarkPaint.setColor(mFadeMarkColor); } else { mMarkPaint.setColor(mMarkColor); } } else { mMarkPaint.setColor(mMarkColor); } if (offset == 0) { // center mark mMarkPaint.setStrokeWidth(mCenterMarkWidth); canvas.drawLine(ox, mTopSpace, ox, mTopSpace + markHeight, mMarkPaint); } else { // other small mark mMarkPaint.setStrokeWidth(mMarkWidth); canvas.drawLine(ox, mTopSpace + smallMarkShrinkY, ox, mTopSpace + markHeight - smallMarkShrinkY, mMarkPaint); } } // mark text if (mMarkCount > 0 && i >= 0 && i < mMarkCount) { CharSequence temp = mItems.get(i); if (mCenterIndex == i) { mMarkTextPaint.setColor(mHighlightColor); mMarkTextPaint.setTextSize(mCenterTextSize); if (!TextUtils.isEmpty(mAdditionCenterMark)) { float off = mAdditionCenterMarkWidth / 2f; float tsize = mMarkTextPaint.measureText(temp, 0, temp.length()); canvas.drawText(temp, 0, temp.length(), x - off, mHeight - mBottomSpace, mMarkTextPaint); mMarkTextPaint.setTextSize(mNormalTextSize); canvas.drawText(mAdditionCenterMark, x + tsize / 2f, mHeight - mBottomSpace, mMarkTextPaint); } else { canvas.drawText(temp, 0, temp.length(), x, mHeight - mBottomSpace, mMarkTextPaint); } } else { mMarkTextPaint.setColor(mMarkTextColor); mMarkTextPaint.setTextSize(mNormalTextSize); canvas.drawText(temp, 0, temp.length(), x, mHeight - mBottomSpace, mMarkTextPaint); } } x += mIntervalDis; } } @Override public boolean onTouchEvent(MotionEvent event) { if (mItems == null || mItems.size() == 0 || !isEnabled()) { return false; } boolean ret = mGestureDetectorCompat.onTouchEvent(event); if (!mFling && MotionEvent.ACTION_UP == event.getAction()) { autoSettle(); ret = true; } return ret || super.onTouchEvent(event); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); refreshCenter(); invalidate(); } else { if (mFling) { mFling = false; autoSettle(); } } } public void setAdditionCenterMark(String additionCenterMark) { mAdditionCenterMark = additionCenterMark; calcIntervalDis(); invalidate(); } private void autoSettle() { int sx = getScrollX(); float dx = mCenterIndex * mIntervalDis - sx - mMaxOverScrollDistance; mScroller.startScroll(sx, 0, (int) dx, 0); 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); int tempIndex = Math.round(offset / mIntervalDis); tempIndex = safeCenter(tempIndex); if (mCenterIndex == tempIndex) { return; } mCenterIndex = tempIndex; if (null != mOnWheelItemSelectedListener) { mOnWheelItemSelectedListener.onWheelItemChanged(this, mCenterIndex); } } private void refreshCenter() { refreshCenter(getScrollX()); } public void selectIndex(int index) { mCenterIndex = index; post(new Runnable() { @Override public void run() { scrollTo((int) (mCenterIndex * mIntervalDis - mMaxOverScrollDistance), 0); invalidate(); refreshCenter(); } }); } public void smoothSelectIndex(int index) { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } int deltaIndex = index - mCenterIndex; mScroller.startScroll(getScrollX(), 0, (int) (deltaIndex * mIntervalDis), 0); 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) { 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); } mContentRectF.set(0, 0, (mMarkCount - 1) * mIntervalDis, getMeasuredHeight()); mCenterIndex = Math.min(mCenterIndex, mMarkCount); calcIntervalDis(); invalidate(); } public int getSelectedPosition() { return mCenterIndex; } public void setOnWheelItemSelectedListener(OnWheelItemSelectedListener onWheelItemSelectedListener) { mOnWheelItemSelectedListener = onWheelItemSelectedListener; } @Override public boolean onDown(MotionEvent e) { if (!mScroller.isFinished()) { mScroller.forceFinished(false); } mFling = false; if (null != getParent()) { getParent().requestDisallowInterceptTouchEvent(true); } return true; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { playSoundEffect(SoundEffectConstants.CLICK); refreshCenter((int) (getScrollX() + e.getX() - mMaxOverScrollDistance)); autoSettle(); return true; } @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 < mMinSelectableIndex * mIntervalDis - 2 * mMaxOverScrollDistance) { dis = 0; } else if (scrollX < mMinSelectableIndex * mIntervalDis - mMaxOverScrollDistance) { dis = distanceX / 4.f; } else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis) { dis = 0; } else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis - mMaxOverScrollDistance) { dis = distanceX / 4.f; } scrollBy((int) dis, 0); refreshCenter(); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { float scrollX = getScrollX(); if (scrollX < -mMaxOverScrollDistance + mMinSelectableIndex * mIntervalDis || scrollX > mContentRectF.width() - mMaxOverScrollDistance - (mMarkCount - 1 - mMaxSelectableIndex) * mIntervalDis) { return false; } else { mFling = true; fling((int) -velocityX, 0); return true; } } @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 onWheelItemChanged(WheelView wheelView, int position); void onWheelItemSelected(WheelView wheelView, int position); } static class SavedState extends BaseSavedState { public static final Creator CREATOR = new 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 + "}"; } }}
6.attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
7.颜色:
<?xml version="1.0" encoding="utf-8"?> #333333 #666666 #D81B60 #333 #888
更多相关文章
- 直播源码Android(安卓)屏幕常亮N秒后自动暗屏
- Android(安卓)横竖屏切换小结
- Webview setLayoutAlgorithm
- android AlertDialog 捕获返回键
- Android(安卓)自定义FloatView实现悬浮视图
- android listview滑动设置浮标半透明效果
- AndEngine 分析之八------Texture
- android PowerManager wakelock
- Android(安卓)通过FontPaint计算字符宽