扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
如何在Android中自定义半圆形圆盘滚动选择器?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
成都创新互联-专业网站定制、快速模板网站建设、高性价比青州网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式青州网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖青州地区。费用合理售后完善,十多年实体公司更值得信赖。
attrs.xml文件代码:
自定义控件的类代码:
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.wj.R; import com.wj.utils.DensityUtil; import com.wj.utils.ScreenUtils; import java.util.ArrayList; import java.util.List; /** * @time 2018/6/8 * @author JunJieW * @since 1376881525@qq.com * @description 自定义半圆形展示效果转盘选择器控件 */ public class RingViewHalf extends ViewGroup { /** * 上一次滑动的坐标 */ private float mLastX; private float mLastY; /** * 检测按下到抬起时使用的时间 */ private long mDownTime; /** * 自动滚动线程 */ private ScrollResetRunnable mScrollResetRunnable; /** * 检测按下到抬起时旋转的角度 */ private float mTmpAngle; /** * 每秒最大移动角度 */ private int mMax_Speed; /** * 如果移动角度达到该值,则屏蔽点击 */ private int mMin_Speed; /** * 圆的直径 */ private int mRadius; /** * 判断是否正在自动滚动 */ private boolean isMove; /** * 布局滚动角度 */ private int mStartAngle = 0; /** * 中间条的宽度 */ private int mCircleLineStrokeWidth; /** * 图片内容偏移角度 */ private int mImageAngle; /** * 是否初始化布局 */ private boolean isChekc = false; /** * 布局view */ private ListmImageList = new ArrayList<>(); /** * 是否可点击 */ private boolean isCanClick = true; /** * 图片与环之间的padding */ private int mPadding; /** * 是否是右边居中的图标为选中图标 */ private boolean is_right_select_icon = true; /** * 是否是右边居中的图标为选中图标 */ private Rect select_icon_rect = new Rect(); //是否能转动 private boolean mCanScrool; public RingViewHalf(Context context) { this(context, null, 0); } public RingViewHalf(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RingViewHalf(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义控件设置的值 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ringview_half, 0, 0); mMax_Speed = array.getInteger(R.styleable.ringview_half_max_speed_rh, 300); mMin_Speed = array.getInteger(R.styleable.ringview_half_min_speed_rh, 3); mImageAngle = array.getInteger(R.styleable.ringview_half_image_angle_rh, 0); mPadding = array.getInteger(R.styleable.ringview_half_image_padding_rh, 0); mCanScrool = array.getBoolean(R.styleable.ringview_half_can_scroll_rh, true); is_right_select_icon = array.getBoolean(R.styleable.ringview_half_is_right_select_icon_rh, true); //获取xml定义的资源文件 TypedArray mList = context.getResources().obtainTypedArray(array.getResourceId(R.styleable.ringview_half_list_rh, 0)); int len = mList.length(); if (len > 0) { for (int i = 0; i < len; i++) mImageList.add(mList.getResourceId(i, 0)); } else { mImageList.add(R.mipmap.icon); mImageList.add(R.mipmap.icon); mImageList.add(R.mipmap.icon); } mList.recycle(); array.recycle(); int [] location =new int [2]; getLocationInWindow(location); Log.d("locationInWindow",">>>>X=="+location[0]+"y=="+location[1]); addImgIcon(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (!isChekc) { initView(); mRadius = getWidth(); isChekc = true; } } /** * 测量 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); int childCount = this.getChildCount(); for (int i = 0; i < childCount; i++) { View child = this.getChildAt(i); this.measureChild(child, widthMeasureSpec, heightMeasureSpec); child.getMeasuredWidth(); } } /** * 排版布局 */ private void initView() { int width = this.getWidth(); int height = this.getHeight(); if (width != height) { int min = Math.min(width, height); width = min; height = min; } //不同屏幕分辨率下做不同的处理 float instPadding = 70f; if (ScreenUtils.getScreenWidth(getContext())<=720){ instPadding = 55f; } //图片摆放的圆弧半径 mCircleLineStrokeWidth = getChildAt(0).getMeasuredHeight() + DensityUtil.dip2px(getContext(),instPadding) + mPadding; //计算图片圆的半径 final int mContent = width / 2 - mCircleLineStrokeWidth / 2; for (int i = 0; i < getChildCount(); i++) { View child = this.getChildAt(i); //计算每个图片摆放的角度 int mAnGle = 360 / mImageList.size() * (i + 1) + mImageAngle; //获取每个图片摆放的左上角的x和y坐标 float left = (float) (width / 2 + mContent * Math.cos(mAnGle * Math.PI / 180)) - child.getMeasuredWidth() / 2; float top = (float) (height / 2 + mContent * Math.sin(mAnGle * Math.PI / 180)) - child.getMeasuredHeight() / 2; /** * 一四象限 */ if (getQuadrantByAngle(mAnGle) == 1 || getQuadrantByAngle(mAnGle) == 4) { // child.setRotation(mAnGle - 270); /** * 二三象限 */ } else { // child.setRotation(mAnGle + 90); } child.layout((int) left, (int) top, (int) left + child.getMeasuredWidth(), (int) top + child.getMeasuredHeight()); } } /** * 添加子控件 */ private void addImgIcon() { for (int i = 1; i < mImageList.size() + 1; i++) { //新建imageview final ImageView mImageView = new ImageView(getContext()); mImageView.setImageResource(mImageList.get(i - 1)); LayoutParams layoutParams = null; mImageView.setScaleType(ImageView.ScaleType.FIT_XY); if (is_right_select_icon){ //右侧icon为选中状态 if (i==mImageList.size()){ mImageView.setAlpha(1f); layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f)); }else { mImageView.setAlpha(0.5f); layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f)); } }else { // 左侧icon为选中状态 if (i==5){ mImageView.setAlpha(1f); layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f)); }else { mImageView.setAlpha(0.5f); layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f)); } } mImageView.setLayoutParams(layoutParams); final int finalI = i; //添加点击事件 mImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (isCanClick) { // Toast.makeText(getContext(),finalI + " ---", Toast.LENGTH_SHORT).show(); if (mOnLogoItemClick != null) mOnLogoItemClick.onItemClick(view, finalI - 1); } } }); //添加view addView(mImageView); } //添加view点击事件 setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (isCanClick) { } } }); } /** * 触摸监听 */ @Override public boolean dispatchTouchEvent(MotionEvent event) { if (mCanScrool) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; mDownTime = System.currentTimeMillis(); mTmpAngle = 0; // 如果当前已经在快速滚动 if (isMove) { // 移除快速滚动的回调 removeCallbacks(mScrollResetRunnable); isMove = false; return true; } break; case MotionEvent.ACTION_MOVE: /** * 获得开始的角度 */ float start = getAngle(mLastX, mLastY); /** * 获得当前的角度 */ float end = getAngle(x, y); Log.e("TAG", "start = " + start + " , end =" + end); // 一四象限 if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4) { mStartAngle += end - start; mTmpAngle += end - start; //二三象限 } else { mStartAngle += start - end; mTmpAngle += start - end; } // 重新布局 getCheck(); break; case MotionEvent.ACTION_UP: // 获取每秒移动的角度 float anglePerSecond = mTmpAngle * 1000 / (System.currentTimeMillis() - mDownTime); // 如果达到最大速度 if (Math.abs(anglePerSecond) > mMax_Speed && !isMove) { // 惯性滚动 post(mScrollResetRunnable = new ScrollResetRunnable(anglePerSecond)); return true; } // 如果当前旋转角度超过minSpeed屏蔽点击 if (Math.abs(mTmpAngle) > mMin_Speed) { return true; } break; } } return super.dispatchTouchEvent(event); } /** * 获取移动的角度 */ private float getAngle(float xTouch, float yTouch) { double x = xTouch - (mRadius / 2d); double y = yTouch - (mRadius / 2d); return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI); } /** * 根据当前位置计算象限 */ private int getQuadrant(float x, float y) { int tmpX = (int) (x - mRadius / 2); int tmpY = (int) (y - mRadius / 2); if (tmpX >= 0) { return tmpY >= 0 ? 4 : 1; } else { return tmpY >= 0 ? 3 : 2; } } /** * 在activity的onCreate方法中获取当前自定义view中在屏幕中的绝对坐标始终为0, * 改成在onWindowFocusChanged函数中获取即可,这时view都已经加载完成 * 但这里特别注意一点要:如果是fragment种使用该自定义view的话,这里的方法就应该注释掉 * 因为不但获取到的矩形的值是空的,而且当你的fragment执行了跳转的逻辑后,再返回后会发 * 一种特别恶心的异常,你获取到判断选中位置的矩形的left,top,right,bottom的值会和 * 初始化的时候不一样,导致你选中时候的状态出现异常情况,本人已经被坑过,希望后面的同学 * 一定注意吸取教训 */ @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); getSelectIconReft(); } //获取选中icon位置的矩形范围 private void getSelectIconReft() { int [] location = new int [2]; getLocationOnScreen(location); //计算出右侧选中时图标的位置 if (is_right_select_icon){ //选中的icon动态设置宽高为60,没选中宽高55,这里60/2为选中按钮的宽度或者高度的一半,即中心点 select_icon_rect.left = location[0]+getWidth()-mCircleLineStrokeWidth/2-DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.top =(location[1]+getHeight()/2)-DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.right = location[0]+getWidth()-mCircleLineStrokeWidth/2+DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.bottom = (location[1]+getHeight()/2)+DensityUtil.dip2px(getContext(),40f)/2; }else { //计算出左侧选中时图标的位置 //选中的icon动态设置宽高为60,没选中宽高55,这里60/2为选中按钮的宽度或者高度的一半,即中心点 select_icon_rect.left = location[0]+mCircleLineStrokeWidth/2-DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.top = (location[1]+getHeight()/2)-DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.right = location[0]+mCircleLineStrokeWidth/2+DensityUtil.dip2px(getContext(),40f)/2; select_icon_rect.bottom = (location[1]+getHeight()/2)+DensityUtil.dip2px(getContext(),40f)/2; } Log.d("onFocusChanged","-----getHeight=="+getChildAt(0).getHeight()+";getWidth=="+getChildAt(0).getWidth()); } /** * 通过角度判断象限 */ private int getQuadrantByAngle(int angle) { if (angle <= 90) { return 4; } else if (angle <= 180) { return 3; } else if (angle <= 270) { return 2; } else { return 1; } } /** * 惯性滚动 */ private class ScrollResetRunnable implements Runnable { private float angelPerSecond; public ScrollResetRunnable(float velocity) { this.angelPerSecond = velocity; } public void run() { //小于20停止 if ((int) Math.abs(angelPerSecond) < 20) { isMove = false; return; } isMove = true; // 滚动时候不断修改滚动角度大小 // mStartAngle += (angelPerSecond / 30); mStartAngle += (angelPerSecond / 40); //逐渐减小这个值 angelPerSecond /= 1.0666F; postDelayed(this, 30); // 重新布局 getCheck(); } } /** * 点击事件接口 */ public interface OnLogoItemClick { void onItemClick(View view, int pos); } private OnLogoItemClick mOnLogoItemClick; private OnIconSelectedListener mOnIconSelectedListener; /** * 设置点击事件 * @param mOnLogoItemClick */ public void addOnItemClick(OnLogoItemClick mOnLogoItemClick) { this.mOnLogoItemClick = mOnLogoItemClick; } /** * 到选中位置后选中事件接口 */ public interface OnIconSelectedListener{ void onIconSelected( int pos); } /** * 设置点击事件 * @param mOnIconSelectedListener */ public void addOnIconSelectedListener(OnIconSelectedListener mOnIconSelectedListener) { this.mOnIconSelectedListener = mOnIconSelectedListener; } /** * 旋转圆盘 */ private void getCheck() { mStartAngle %= 360; setRotation(mStartAngle); //改变选中的icon的状态 setSelectedIcon(); } //改变选中的icon的状态 private void setSelectedIcon() { if (select_icon_rect.left==0&&select_icon_rect.top==0){ //fragment中onWindowFocusChanged会出现计算select_icon_rect.left和select_icon_rect.top等于0的情况, // 所以做下判断,如果为0则重新调用下计算方法 getSelectIconReft(); } for (int j =0;j 然后就是你在activity中根据回调方法获取选中的对象:
//左右侧方法相同,这里列出左侧圆盘获取方法:view.ringView_half_left.addOnIconSelectedListener { position -> // ToDo 根据postion从你的list中获取对应的选中的对象的bean类属性即可 }最后贴下布局文件:
//这里是放半圆形转盘选择器上显示的图片list,我这里是用的xml静态传进去的,也可以改为动态方式传递
app:list_rh="@array/zodiac_list"然后在values下面创建一个arrays.xml文件
- @drawable/zodiac_1
- @drawable/zodiac_2
- @drawable/zodiac_3
- @drawable/zodiac_4
- @drawable/zodiac_5
- @drawable/zodiac_6
- @drawable/zodiac_7
- @drawable/zodiac_8
- @drawable/zodiac_9
- @drawable/zodiac_10
- @drawable/zodiac_11
- @drawable/zodiac_12
关于如何在Android中自定义半圆形圆盘滚动选择器问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。
新闻标题:如何在Android中自定义半圆形圆盘滚动选择器
URL链接:http://kswjz.com/article/pgjchh.html
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流