import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * Created by JillFung on 2016/8/18. */ public class LoopView extends View { private float scaleX = 1.05F; public enum ACTION { // 点击,滑翔(滑到尽头),拖拽事件 CLICK, FLING, DAGGLE } Context context; Handler handler; private GestureDetector gestureDetector; OnItemSelectedListener onItemSelectedListener; // Timer mTimer; ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture> mFuture; Paint paintOuterText; Paint paintCenterText; Paint paintIndicator; Listitems; int textSize; int maxTextWidth; int maxTextHeight; int colorGray; int colorBlack; int colorLightGray; // 条目间距倍数 float lineSpacingMultiplier; boolean isLoop; // 第一条线Y坐标值 int firstLineY; int secondLineY; int totalScrollY; int initPosition; private int selectedItem; int preCurrentIndex; int change; // 显示几个条目 int itemsVisible; int measuredHeight; int measuredWidth; int paddingLeft = 0; int paddingRight = 0; // 半圆周长 int halfCircumference; // 半径 int radius; private int mOffset = 0; private float previousY; long startTime = 0; private Rect tempRect = new Rect(); public LoopView(Context context) { super(context); initLoopView(context); } public LoopView(Context context, AttributeSet attributeset) { super(context, attributeset); initLoopView(context); } public LoopView(Context context, AttributeSet attributeset, int defStyleAttr) { super(context, attributeset, defStyleAttr); initLoopView(context); } private void initLoopView(Context context) { this.context = context; handler = new MessageHandler(this); gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this)); gestureDetector.setIsLongpressEnabled(false); lineSpacingMultiplier = 2.0F; isLoop = true; itemsVisible = 9; textSize = 0; colorGray = 0xffafafaf; colorBlack = 0xff313131; colorLightGray = 0xffc5c5c5; totalScrollY = 0; initPosition = -1; initPaints(); setTextSize(16F); } private void initPaints() { paintOuterText = new Paint(); paintOuterText.setColor(colorGray); paintOuterText.setAntiAlias(true); paintOuterText.setTypeface(Typeface.MONOSPACE); paintOuterText.setTextSize(textSize); paintCenterText = new Paint(); paintCenterText.setColor(colorBlack); paintCenterText.setAntiAlias(true); paintCenterText.setTextScaleX(scaleX); paintCenterText.setTypeface(Typeface.MONOSPACE); paintCenterText.setTextSize(textSize); paintIndicator = new Paint(); paintIndicator.setColor(colorLightGray); paintIndicator.setAntiAlias(true); if (android.os.Build.VERSION.SDK_INT >= 11) { setLayerType(LAYER_TYPE_SOFTWARE, null); } } private void remeasure() { if (items == null) { return; } measureTextWidthHeight(); halfCircumference = (int) (maxTextHeight * lineSpacingMultiplier * (itemsVisible - 1)); measuredHeight = (int) ((halfCircumference * 2) / Math.PI); radius = (int) (halfCircumference / Math.PI); measuredWidth = maxTextWidth + paddingLeft + paddingRight; firstLineY = (int) ((measuredHeight - lineSpacingMultiplier * maxTextHeight) / 2.0F); secondLineY = (int) ((measuredHeight + lineSpacingMultiplier * maxTextHeight) / 2.0F); if (initPosition == -1) { if (isLoop) { initPosition = (items.size() + 1) / 2; } else { initPosition = 0; } } preCurrentIndex = initPosition; } private void measureTextWidthHeight() { for (int i = 0; i < items.size(); i++) { String s1 = items.get(i); paintCenterText.getTextBounds(s1, 0, s1.length(), tempRect); int textWidth = tempRect.width(); if (textWidth > maxTextWidth) { maxTextWidth = (int) (textWidth * scaleX); } paintCenterText.getTextBounds("\u661F\u671F", 0, 2, tempRect); // 星期 int textHeight = tempRect.height(); if (textHeight > maxTextHeight) { maxTextHeight = textHeight; } } } void smoothScroll(ACTION action) { cancelFuture(); if (action == ACTION.FLING || action == ACTION.DAGGLE) { float itemHeight = lineSpacingMultiplier * maxTextHeight; mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight); if ((float) mOffset > itemHeight / 2.0F) { mOffset = (int) (itemHeight - (float) mOffset); } else { mOffset = -mOffset; } } mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS); } // void smoothScroll() { // int offset = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight)); // cancelFuture(); // mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, offset), 0, 10, TimeUnit.MILLISECONDS); // } protected final void scrollBy(float velocityY) { cancelFuture(); // 修改这个值可以改变滑行速度 int velocityFling = 10; mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, velocityFling, TimeUnit.MILLISECONDS); } public void cancelFuture() { if (mFuture != null && !mFuture.isCancelled()) { mFuture.cancel(true); mFuture = null; } } public final void setNotLoop() { isLoop = false; } public final void setTextSize(float size) { if (size > 0.0F) { textSize = (int) (context.getResources().getDisplayMetrics().density * size); paintOuterText.setTextSize(textSize); paintCenterText.setTextSize(textSize); } } public final void setInitPosition(int initPosition) { if (initPosition < 0) { this.initPosition = 0; } else { if (items != null && (items.size() - 1) >= initPosition) { this.initPosition = initPosition; } } } public final void setListener(OnItemSelectedListener OnItemSelectedListener) { onItemSelectedListener = OnItemSelectedListener; } public final void setItems(List items) { this.items = items; remeasure(); invalidate(); } @Override public int getPaddingLeft() { return paddingLeft; } @Override public int getPaddingRight() { return paddingRight; } // 设置左右内边距 public void setViewPadding(int left, int top, int right, int bottom) { paddingLeft = left; paddingRight = right; } public final int getSelectedItem() { return selectedItem; } // // protected final void scrollBy(float velocityY) { // Timer timer = new Timer(); // mTimer = timer; // timer.schedule(new InertiaTimerTask(this, velocityY, timer), 0L, 20L); // } protected final void onItemSelected() { if (onItemSelectedListener != null) { postDelayed(new OnItemSelectedRunnable(this), 200L); } } @Override protected void onDraw(Canvas canvas) { if (items == null) { return; } String as[] = new String[itemsVisible]; change = (int) (totalScrollY / (lineSpacingMultiplier * maxTextHeight)); preCurrentIndex = initPosition + change % items.size(); if (!isLoop) { if (preCurrentIndex < 0) { preCurrentIndex = 0; } if (preCurrentIndex > items.size() - 1) { preCurrentIndex = items.size() - 1; } } else { if (preCurrentIndex < 0) { preCurrentIndex = items.size() + preCurrentIndex; } if (preCurrentIndex > items.size() - 1) { preCurrentIndex = preCurrentIndex - items.size(); } } int j2 = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight)); // 设置as数组中每个元素的值 int k1 = 0; while (k1 < itemsVisible) { int l1 = preCurrentIndex - (itemsVisible / 2 - k1); if (isLoop) { while (l1 < 0) { l1 = l1 + items.size(); } while (l1 > items.size() - 1) { l1 = l1 - items.size(); } as[k1] = items.get(l1); } else if (l1 < 0) { as[k1] = ""; } else if (l1 > items.size() - 1) { as[k1] = ""; } else { as[k1] = items.get(l1); } k1++; } canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator); canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator); int j1 = 0; while (j1 < itemsVisible) { canvas.save(); // L(弧长)=α(弧度)* r(半径) (弧度制) // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长) float itemHeight = maxTextHeight * lineSpacingMultiplier; double radian = ((itemHeight * j1 - j2) * Math.PI) / halfCircumference; // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限 float angle = (float) (90D - (radian / Math.PI) * 180D); if (angle >= 90F || angle <= -90F) { canvas.restore(); } else { int translateY = (int) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D); canvas.translate(0.0F, translateY); canvas.scale(1.0F, (float) Math.sin(radian)); if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) { // 条目经过第一条线 canvas.save(); canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY); canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText); canvas.restore(); canvas.save(); canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight)); canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText); canvas.restore(); } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) { // 条目经过第二条线 canvas.save(); canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY); canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText); canvas.restore(); canvas.save(); canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight)); canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText); canvas.restore(); } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) { // 中间条目 canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText); selectedItem = items.indexOf(as[j1]); } else { // 其他条目 canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText); } canvas.restore(); } j1++; } } // 绘制文字起始位置 private int getTextX(String a, Paint paint, Rect rect) { paint.getTextBounds(a, 0, a.length(), rect); // 获取到的是实际文字宽度 int textWidth = rect.width(); // 转换成绘制文字宽度 textWidth *= scaleX; return (measuredWidth - textWidth) / 2; // return textWidth; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { remeasure(); setMeasuredDimension(measuredWidth, measuredHeight); } @Override public boolean onTouchEvent(MotionEvent event) { boolean eventConsumed = gestureDetector.onTouchEvent(event); float itemHeight = lineSpacingMultiplier * maxTextHeight; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startTime = System.currentTimeMillis(); cancelFuture(); previousY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: float dy = previousY - event.getRawY(); previousY = event.getRawY(); totalScrollY = (int) (totalScrollY + dy); // 边界处理。 if (!isLoop) { float top = -initPosition * itemHeight; float bottom = (items.size() - 1 - initPosition) * itemHeight; if (totalScrollY < top) { totalScrollY = (int) top; } else if (totalScrollY > bottom) { totalScrollY = (int) bottom; } } break; case MotionEvent.ACTION_UP: default: if (!eventConsumed) { float y = event.getY(); double l = Math.acos((radius - y) / radius) * radius; int circlePosition = (int) ((l + itemHeight / 2) / itemHeight); float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight; mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset); if ((System.currentTimeMillis() - startTime) > 120) { // 处理拖拽事件 smoothScroll(ACTION.DAGGLE); } else { // 处理条目点击事件 smoothScroll(ACTION.CLICK); } } break; } invalidate(); return true; } }
这个是之前公司大牛写的,有些地方还有些不完善,希望大家遇到问题可以共同讨论一下。
现在说怎么使用吧:
xml version="1.0" encoding="utf-8"?>这个是popwindow的布局文件。xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="260dp" android:orientation="vertical"> android:id="@+id/user_info_select_finish" android:layout_width="wrap_content" android:layout_height="44dp" android:layout_alignParentRight="true" android:layout_gravity="right" android:layout_marginRight="16dp" android:gravity="center" android:text="完成" android:textSize="22sp" android:visibility="visible" /> style="@style/ProfileSplitLine" /> android:id="@+id/user_info_choose_sex" android:layout_width="match_parent" android:layout_height="150dp" android:layout_gravity="center" android:visibility="visible" />
import android.app.Activity; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.PopupWindow; import android.widget.TextView; import com.yunbix.myutils.tool.choosebirthday.LoopView; import com.yunbix.myutils.tool.choosebirthday.OnItemSelectedListener; import com.yunbix.suyihua.R; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2017/4/26. */ public class WorkTimePopWindow extends PopupWindow { private View mView; private TextView finish; public static String sex; public static String type; private List这个是popwindow的类,使用起来也特别简单sexData; private LoopView chooseSex; private Context context; private List worktype; public WorkTimePopWindow(Activity context, View.OnClickListener clickListener) { super(context); this.context = context; sex = "一年以内"; type="1"; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mView = inflater.inflate(R.layout.choose_sex_diaolog, null); finish = (TextView) mView.findViewById(R.id.user_info_select_finish); chooseSex = (LoopView) mView.findViewById(R.id.user_info_choose_sex); initLoopView(); // 设置按钮监听 finish.setOnClickListener(clickListener); //设置PopupWindow的View this.setContentView(mView); //设置PopupWindow弹出窗体的宽 this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); //设置PopupWindow弹出窗体的高 this.setHeight(context.getResources().getDimensionPixelOffset(R.dimen.user_info_dialog_height)); //设置PopupWindow弹出窗体可点击 this.setFocusable(true); //实例化一个ColorDrawable颜色为半透明 ColorDrawable dw = new ColorDrawable(0xeef6f6f6); // 设置SelectPicPopupWindow弹出窗体的背景 this.setBackgroundDrawable(dw); this.setAnimationStyle(R.style.livePopStyle); } private void initLoopView() { sexData = new ArrayList (); sexData.add("一年以内"); sexData.add("一到三年"); sexData.add("三年到五年"); sexData.add("五年以上"); worktype=new ArrayList<>(); worktype.add("1"); worktype.add("2"); worktype.add("3"); worktype.add("4"); chooseSex.setListener(new OnItemSelectedListener() { @Override public void onItemSelected(int index) { sex = sexData.get(index); type=worktype.get(index); } }); chooseSex.setItems(sexData); chooseSex.setInitPosition(0); chooseSex.setTextSize(22); chooseSex.setNotLoop(); } }
View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { edTvWorktime.setText(WorkTimePopWindow.sex); myPopUpWindow.dismiss(); worktime=WorkTimePopWindow.type; } }; myPopUpWindow = new WorkTimePopWindow(this, listener); myPopUpWindow.showAtLocation(this.findViewById(R.id.user_info), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);