php 环状,如何实现自定义圆环状时间滚轮

本篇文章给大家分享的内容是如何实现自定义圆环状时间滚轮,有着一定的参考价值,有需要的朋友可以参考一下

先看一下最终效果:

dfa1e8074f376b2982acc62e431d0a69.gif

实现

功能简介: 一年以内的任意天和week是可以对应起来的,其他的日与月不限制年

实现原理:利用一个自定义的滚轮来写日周月相互对应的逻辑部分

自定义view的源码如下:package com.lwyy.wheel.view;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Rect;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import com.lwyy.wheel.R;

import java.util.ArrayList;

import java.util.List;

/**

* Created by ll on 2017/9/25.

*/

public class CircleWheelView extends View {

private static final String TAG = CircleWheelView.class.getSimpleName();

private int mItemAlign;

public static final int ALIGN_CENTER = 0, ALIGN_LEFT = 1, ALIGN_RIGHT = 2;

private Paint mPaint;

private OnItemSelectedListener mOnItemSelectedListener;

private int mTextMaxWidth, mTextMaxHeight;

private Rect mRectDrawn;

private int mDrawnCenterX, mDrawnCenterY; //滚轮选择器绘制中心坐标

private int mWheelCenterX, mWheelCenterY; //滚轮选择器中心坐标

private int mItemTextColor, mSelectedItemTextColor; //数据项文本颜色以及被选中的数据项文本颜色

private int mItemHeight;

private float mItemTextSize;

private int mLastPointY; //用户手指上一次触摸事件发生时事件Y坐标

private float moveY;

private List mData = new ArrayList();

private List mDataCC = new ArrayList();

private int mTurnToCenterX;

private int mCurrentItemPosition;

private int mDefaultHalfNum = 7, mDefaultVisibleNum = 13; //设置当前view默认可见的item个数

private int mVisibleHalfNum, mVisibleCount; //视图区域内的展示的item个数

private int mDataSize;

private boolean isCyclic; //数据是否循环展示 针对list的数目小于mDefaultVisibleNum

private boolean isLoopDisplay; //所有的数据是否是球形展示,即永远的头尾衔接,如果isCyclic是true,则该值也是true

private float[] rates = null;

private float[] textSizeRates = null;

public CircleWheelView(Context context) {

this(context, null);

}

public CircleWheelView(Context context, AttributeSet attrs) {

super(context, attrs);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleWheelView);

mItemTextSize = a.getDimensionPixelSize(R.styleable.CircleWheelView_wheel_item_text_size,

getResources().getDimensionPixelSize(R.dimen.WheelItemTextSize_Default));

mItemAlign = a.getInt(R.styleable.CircleWheelView_wheel_item_align, ALIGN_CENTER);

mItemTextColor = a.getColor(R.styleable.CircleWheelView_wheel_item_text_color, 0xFF888888);

mSelectedItemTextColor = a.getColor(R.styleable.CircleWheelView_wheel_selected_item_text_color, 0xFF888899);

mTurnToCenterX = a.getInt(R.styleable.CircleWheelView_wheel_turn_to_centerx, 0); //转向中间,偏移的距离

isCyclic = a.getBoolean(R.styleable.CircleWheelView_wheel_cyclic, false);

isLoopDisplay = a.getBoolean(R.styleable.CircleWheelView_wheel_loop_display, false);

a.recycle();

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.LINEAR_TEXT_FLAG);

mPaint.setStyle(Paint.Style.FILL);

mRectDrawn = new Rect();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int modeWidth = MeasureSpec.getMode(widthMeasureSpec);

int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);

int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

// 计算原始内容尺寸

int resultWidth = mTextMaxWidth;

int resultHeight = mTextMaxHeight * mVisibleCount;

// 考虑内边距对尺寸的影响

resultWidth += getPaddingLeft() + getPaddingRight();

resultHeight += getPaddingTop();

// 考虑父容器对尺寸的影响

resultWidth = measureSize(modeWidth, sizeWidth, resultWidth);

resultHeight = measureSize(modeHeight, sizeHeight, resultHeight);

setMeasuredDimension(resultWidth, resultHeight);

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

// 获取内容区域中心坐标

mRectDrawn.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),

getHeight() - getPaddingBottom());

mWheelCenterX = mRectDrawn.centerX();

mWheelCenterY = mRectDrawn.centerY();

computeDrawnCenter();

}

@Override

protected void onDraw(Canvas canvas) {

if (mDataCC.size() > 0) {

mPaint.setStrokeWidth(1);

mPaint.setSubpixelText(true); //设置该项为true,将有助于文本在LCD屏幕上的显示效果

canvas.save();

int indexBottom = 0;

float distanceX = 0, mDrawnItemCenterY = 0;

int endIndex = isCyclic ? 13 : Math.min(mDefaultVisibleNum, mDataCC.size());

if (endIndex > mData.size())

endIndex = mData.size();

for (int i = 0; i < endIndex; i++) {

if (i == 0) mPaint.setColor(mSelectedItemTextColor);

else mPaint.setColor(mItemTextColor);

if (isCyclic) {

if (i < mVisibleHalfNum)

indexBottom = i;

else

indexBottom = i - mVisibleHalfNum + 1;

if (i < mVisibleHalfNum)

mDrawnItemCenterY = (int) (mDrawnCenterY - (mItemHeight * rates[indexBottom]));

else

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]));

if (i == endIndex - 1)

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]) - 10);

} else if (mData.size() < 13) { if (i < mVisibleHalfNum + 1)

indexBottom = i; else

indexBottom = i - mVisibleHalfNum; if (i < mVisibleHalfNum + 1)

mDrawnItemCenterY = (int) (mDrawnCenterY - (mItemHeight * rates[indexBottom])); else

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom])); if (i == endIndex - 1)

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]) - 10);// if (i < mVisibleHalfNum)// indexBottom = i;// else// indexBottom = i - mVisibleHalfNum + 1; if (i < mVisibleHalfNum)// mDrawnItemCenterY = (int) (mDrawnCenterY - (mItemHeight * rates[indexBottom]));// else// mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]));// if (i == endIndex - 1)// mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]) - 10);

} else {

if (i < mVisibleHalfNum)

indexBottom = i;

else

indexBottom = i - mVisibleHalfNum + 1;

// LogUtil.e(TAG, "indexBottom:" + indexBottom + ",mVisibleHalfNum:" + mVisibleHalfNum + ",i:" + i);

if (indexBottom >= mDefaultHalfNum)

continue;

if (i < mVisibleHalfNum)

mDrawnItemCenterY = (int) (mDrawnCenterY - (mItemHeight * rates[indexBottom])); else

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom])); if (i == endIndex - 1)

mDrawnItemCenterY = (int) (mDrawnCenterY + (mItemHeight * rates[indexBottom]) - 10);

}

mPaint.setTextSize(mItemTextSize * textSizeRates[indexBottom]);

paintText(canvas, distanceX, rates, mDrawnItemCenterY, indexBottom, i);

}

}

}

private void paintText(Canvas canvas, float distanceX, float[] rates, float drawnCenterY, int indexBottom, int i) {

float mDistanceSubX = mDrawnCenterX - mTurnToCenterX * rates[indexBottom] * rates[indexBottom];

float mDistanceAddX = mDrawnCenterX + mTurnToCenterX * rates[indexBottom] * rates[indexBottom];

switch (mItemAlign) {

case ALIGN_CENTER:

distanceX = mDrawnCenterX;

break;

case ALIGN_LEFT:

distanceX = mData.get(i).toString().length() <= 1 ?

mDistanceSubX + 4 * (mVisibleHalfNum - indexBottom) : mDistanceSubX;

break;

case ALIGN_RIGHT:

distanceX = mData.get(i).toString().length() <= 1 ?

mDistanceAddX - 4 * (mVisibleHalfNum - indexBottom) : mDistanceAddX;

break;

}

String text = "";

if (!isLoopDisplay && !isCyclic && mDataSize > mDefaultHalfNum) {

if (mCurrentItemPosition < mDefaultHalfNum - 1 && (i >= mDefaultHalfNum || i <= mCurrentItemPosition)) {

text = String.valueOf(mData.get(i));

} else if (mCurrentItemPosition > mDefaultHalfNum - 1 && i < mDefaultHalfNum + (mDataSize - mCurrentItemPosition) - 1)

text = String.valueOf(mData.get(i));

else if (mCurrentItemPosition == mDefaultHalfNum - 1)

text = String.valueOf(mData.get(i));

} else if (isCyclic && !isLoopDisplay) {

if (mCurrentItemPosition < mDefaultHalfNum - 1 && ((i >= mDefaultHalfNum && i < mDataCC.size() - mCurrentItemPosition - 1 + mDefaultHalfNum) || i <= mCurrentItemPosition)) {

text = String.valueOf(mData.get(i));

} else if (mCurrentItemPosition > mDefaultHalfNum - 1 && i < mDefaultHalfNum + (mDataCC.size() - mCurrentItemPosition) - 1)

text = String.valueOf(mData.get(i)); else if (mCurrentItemPosition == mDefaultHalfNum - 1 && i < mDataCC.size())

text = String.valueOf(mData.get(i));

} else {

text = String.valueOf(mData.get(i));

// LogUtil.i(TAG, "text:" + mData.get(i) + ",i:" + i);

}

canvas.drawText(text, distanceX, drawnCenterY, mPaint);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

mLastPointY = (int) event.getY();

break;

case MotionEvent.ACTION_MOVE:

moveY = event.getY() - mLastPointY;

if (Math.abs(moveY) < 1) break;

if (mDataCC.size() < mDefaultHalfNum)

break;

if (!isLoopDisplay) {

if (mCurrentItemPosition == 0 && moveY > 0)

break;

if (isCyclic && mDataCC.size() < mDefaultVisibleNum && mCurrentItemPosition == mDataCC.size() - 1 && moveY < 0)

break;

else if (mCurrentItemPosition == mDataSize - 1 && moveY < 0)

break;

}

changeMoveItemPosition(event, mItemHeight * 3 / 2);

break;

case MotionEvent.ACTION_UP:

if (mDataCC.size() < mDefaultHalfNum) {

changeMoveItemPosition(event, mItemHeight / 2);

}

if (null != mOnItemSelectedListener && mCurrentItemPosition < mDataCC.size() && mDataCC.size() > 0 && mCurrentItemPosition >= 0)

mOnItemSelectedListener.onItemSelected(this, mDataCC.get(mCurrentItemPosition), mCurrentItemPosition); break;

}

return true;

}

private void changeMoveItemPosition(MotionEvent event, int moveHeight) {

if (Math.abs(moveY) > moveHeight) {

mLastPointY = (int) event.getY();

if (moveY > 0)

mCurrentItemPosition--;

else

mCurrentItemPosition++;

if (mCurrentItemPosition > mDataCC.size() - 1)

mCurrentItemPosition -= mDataCC.size();

if (mCurrentItemPosition < 0)

mCurrentItemPosition += mDataCC.size();

if (mCurrentItemPosition >= 0 && mCurrentItemPosition < mDataCC.size())

setSelectedItemPosition(mDataCC, mCurrentItemPosition);

}

}

private void computeTextSize() {

mTextMaxWidth = mTextMaxHeight = 0;

for (Object obj : mData) {

String text = String.valueOf(obj);

int width = (int) mPaint.measureText(text);

mTextMaxWidth = Math.max(mTextMaxWidth, width);

}

Paint.FontMetrics metrics = mPaint.getFontMetrics();

mTextMaxHeight = (int) (metrics.bottom - metrics.top);

}

private int measureSize(int mode, int sizeExpect, int sizeActual) {

int realSize; if (mode == MeasureSpec.EXACTLY) {

realSize = sizeExpect;

} else {

realSize = sizeActual; if (mode == MeasureSpec.AT_MOST)

realSize = Math.min(realSize, sizeExpect);

} return realSize;

}

//固定的等比高度

private void computeDrawnCenter() {

int num = 7;

mItemHeight = mRectDrawn.height() / (num * 2 - 2);

mPaint.setTextAlign(Paint.Align.CENTER);

mDrawnCenterX = mWheelCenterX;

mDrawnCenterY = mWheelCenterY + mItemHeight / num;

}

public void updateVisibleItemCount(int num) {

if (isCyclic) {

mVisibleCount = mDefaultVisibleNum = 13;

mVisibleHalfNum = mDefaultHalfNum = 7;

} else {

if (num >= mDefaultHalfNum)

mVisibleHalfNum = mDefaultHalfNum;

else {

mVisibleHalfNum = num % 2 == 1 ? num / 2 + 1 : num / 2;

}

}

// LogUtil.e(TAG, "mVisibleHalfNum:" + mVisibleHalfNum);

mVisibleCount = Math.min(mDefaultVisibleNum, num);

mDataSize = isCyclic ? mDefaultVisibleNum : num;

}

public void setData(List mData, int pos) {

setData(mData, pos, true);

}

public void setData(List mData, int pos, boolean isLoopDisplay) {

setData(mData, pos, isLoopDisplay, false, 13);

}

public void setData(List mData, int pos, boolean isLoopDisplay, boolean isCycle) {

setData(mData, pos, isLoopDisplay, isCycle, 13);

}

/**

* @param mData 传入的展示列表数据

* @param pos 当前view的中间item展示的内容

* @param isLoopDisplay 所有的数据是否是球形展示,即永远的头尾衔接,即item由mDataSize - 1下滑变为0,默认true

* @param isCycle 数据是否循环展示 针对list的数目小于mDefaultVisibleNum. 默认false

* @param visibleShowNum 当前界面展示的item个数

*/

public void setData(List mData, int pos, boolean isLoopDisplay, boolean isCycle, int visibleShowNum) {

if (mData.size() <= 0 || pos < 0 || pos >= mData.size())

return;

if (visibleShowNum % 2 == 1)

visibleShowNum++;

if (mData.size() > 12 && isCycle)

isCycle = false;

else if (mData.size() < 13 && !isCycle && !isLoopDisplay) {

isCycle = true;

}

mDefaultVisibleNum = mData.size() < 13 && !isCycle ? mData.size() : 13;

mDefaultHalfNum = mDefaultVisibleNum < 13 ? mDefaultVisibleNum / 2 : 7;

this.isLoopDisplay = isLoopDisplay;

this.isCyclic = isCycle;

if (this.mDataCC.size() > 0)

this.mDataCC.clear();

this.mDataCC.addAll(mData);

this.mCurrentItemPosition = pos;

updateVisibleItemCount(mData.size());

setSelectedItemPosition(mData, pos);

updateRates();

computeTextSize();

requestLayout();

invalidate();

}

public void updateDataPos(int pos) {

if (pos < 0)

return;

if (pos >= mDataCC.size())

pos = mDataCC.size() / 2 - 1;

this.mCurrentItemPosition = pos;

setSelectedItemPosition(mDataCC, pos);

invalidate();

}

private void updateRates() {

int num = 7;

if (null == rates)

rates = new float[num];

if (null == textSizeRates)

textSizeRates = new float[num];

float rate = 0.0f;

for (int i = 0; i < num; i++) {

if (i > 0) {

rate = (3 * (11 * i - (i - 1) * (i - 1))) / 22f;

if (i == num - 1)

rate = (3 * (11 * i - (i - 1) * (i - 1)) + 4) / 22f;

}

rates[i] = rate;

textSizeRates[i] = ((18 - 7 * i / 3) / 13f);

}

}

public void setItemAlign(int align) {

this.mItemAlign = align;

invalidate();

}

public void setTurnToCenter(int turnToCenterX) {

this.mTurnToCenterX = turnToCenterX;

requestLayout();

invalidate();

}

public void setSelectedItemPosition(List mDatas, int centerPos) {

if (mData.size() > 0)

mData.clear();

mDataSize = isCyclic ? mDefaultVisibleNum : mDatas.size();

int halfIndex = isCyclic ? 7 : Math.min(mVisibleHalfNum, mDefaultHalfNum);

// LogUtil.i(TAG, "endIndex:" + endIndex + ",topPos:" + topPos + ",centerPos:" + centerPos + ",bottomPos:" + bottomPos + ",halfIndex:" + halfIndex);

// LogUtil.i(TAG, "endIndex:" + endIndex + ",mVisibleHalfNum:" + mVisibleHalfNum + ",mDefaultHalfNum:" + mDefaultHalfNum + ",mDefaultVisibleNum:" + mDefaultVisibleNum);

int bottom = 0;

mData.add(0, mDatas.get(centerPos));

if ((isCyclic && mDatas.size() < 13) || mDatas.size() >= 13) {

for (int i = 1; i < halfIndex; i++) {

bottom = centerPos - i;

bottom = chooseIndex(bottom, mDatas.size());

mData.add(mDatas.get(bottom));

}

for (int i = mDatas.size() - 1; i >= halfIndex; i--) {

bottom = centerPos + mDatas.size() - i;

bottom = chooseIndex(bottom, mDatas.size());

mData.add(mDatas.get(bottom));

}

if (isCyclic)

for (int i = mDatas.size(); i < 13; i++) {

bottom = i - halfIndex + 1 + centerPos;

bottom = chooseIndex(bottom, mDatas.size());

// LogUtil.e(TAG, "bottom:" + bottom + ",:" + mDatas.get(bottom));

mData.add(mDatas.get(bottom));

}

} else if (mDatas.size() < 13) {

//下面个数比上面多

for (int i = 1; i <= halfIndex; i++) {

bottom = centerPos - i;

bottom = chooseIndex(bottom, mDatas.size());

mData.add(mDatas.get(bottom));

}

for (int i = mDatas.size() - 1; i > halfIndex; i--) {

bottom = centerPos + mDatas.size() - i;

bottom = chooseIndex(bottom, mDatas.size());

mData.add(mDatas.get(bottom));

}

}

invalidate();

}

public void setTextSize(float textSize) {

this.mItemTextSize = textSize;

invalidate();

}

public void setTextSelectColor(int colorRes) {

this.mSelectedItemTextColor = colorRes;

requestLayout();

invalidate();

}

public int chooseIndex(int index, int dataSize) {

if (index > dataSize - 1)

index -= dataSize;

if (index < 0)

index += dataSize;

return index;

}

public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {

this.mOnItemSelectedListener = onItemSelectedListener;

}

public int getListSize() {

if (null != mDataCC)

return mDataCC.size();

return 0;

}

/**

* 滚轮选择器Item项被选中时监听接口

*/

public interface OnItemSelectedListener {

void onItemSelected(CircleWheelView view, Object data, int position);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值