温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何在Android中自定义半圆形圆盘滚动选择器

发布时间:2021-06-08 16:46:07 来源:亿速云 阅读:175 作者:Leah 栏目:移动开发

如何在Android中自定义半圆形圆盘滚动选择器?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

attrs.xml文件代码:

<!--自定义半圆形展示效果转盘选择器控件-->  <declare-styleable name="ringview_half">  <attr name="image_angle_rh" format="integer" />  <attr name="image_padding_rh" format="integer" />  <attr name="max_speed_rh" format="integer" />  <attr name="min_speed_rh" format="integer" />  <attr name="list_rh" format="integer" />  <attr name="can_scroll_rh" format="boolean" />  <attr name="is_right_select_icon_rh" format="boolean" /> </declare-styleable>

自定义控件的类代码:

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 List<Integer> mImageList = 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<getChildCount();j++){  LayoutParams layoutParams = null;  int [] location = new int [2];  getChildAt(j).getLocationOnScreen(location);  Log.d("getCheck","location[0]=="+location[0]+";select_icon_rect.left=="+select_icon_rect.left+"location[1]=="+location[1]+";select_icon_rect.top=="+select_icon_rect.top);  if (is_right_select_icon){  //右边icon是选中状态的时候  if (select_icon_rect.left-22<=location[0]&&location[0]<=select_icon_rect.right+22){   if (select_icon_rect.top-22<=location[1]&&location[1]<=select_icon_rect.bottom+22){   getChildAt(j).setAlpha(1);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));   //把选中的icon所在list中的position通过接口回传过去   if (mOnIconSelectedListener!=null){   mOnIconSelectedListener.onIconSelected(j);   }   }else {   getChildAt(j).setAlpha(0.5f);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));   }  }else {   getChildAt(j).setAlpha(0.5f);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));  }  }else {  //左边icon是选中状态的时候  if (select_icon_rect.left-22<=location[0]&&location[0]<=select_icon_rect.right+22){   if (select_icon_rect.top-22<=location[1]&&location[1]<=select_icon_rect.bottom+22){   getChildAt(j).setAlpha(1);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));   //把选中的icon所在list中的position通过接口回传过去   if (mOnIconSelectedListener!=null){   mOnIconSelectedListener.onIconSelected(j);   }   }else {   getChildAt(j).setAlpha(0.5f);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));   }  }else {   getChildAt(j).setAlpha(0.5f);   layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));  }  }  getChildAt(j).setLayoutParams(layoutParams);  getChildAt(j).invalidate();  Log.d("getChildCount","=="+j+";class=="+getChildAt(j).getClass()+";left=="+getChildAt(j).getLeft()+";top=="+getChildAt(j).getTop()+";right=="+getChildAt(j).getRight()+";bottom=="+getChildAt(j).getBottom()+";getLocationOnScreen:x="+location[0]+"y="+location[1]+";getRotationX=="+getRotationX()+";getRotationY=="+getRotationY());  }  } }

然后就是你在activity中根据回调方法获取选中的对象:
//左右侧方法相同,这里列出左侧圆盘获取方法:

view.ringView_half_left.addOnIconSelectedListener { position ->  // ToDo 根据postion从你的list中获取对应的选中的对象的bean类属性即可  }

最后贴下布局文件:

<RelativeLayout  android:layout_width="match_parent"  android:layout_height="match_parent">  <RelativeLayout  android:id="@+id/ring_left_outside_rl"  android:layout_width="@dimen/dp_350"  android:layout_height="@dimen/dp_350"  android:layout_alignParentLeft="true"  android:layout_marginLeft="-240dp">  <com.wj.views.RingViewHalf   android:id="@+id/ringView_half_left"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:background="@drawable/bg_compatibility"   app:image_angle_rh="15"   app:image_padding_rh="20"   app:is_right_select_icon_rh="true"   app:list_rh="@array/zodiac_list" />  <ImageView   android:layout_width="70dp"   android:layout_height="70dp"   android:layout_alignParentLeft="true"   android:layout_centerVertical="true"   android:layout_marginLeft="@dimen/dp_220"   android:src="@drawable/icon_match_boy" />  </RelativeLayout>  <RelativeLayout  android:id="@+id/ring_right_outside_rl"  android:layout_width="@dimen/dp_350"  android:layout_height="@dimen/dp_350"  android:layout_alignParentRight="true"  android:layout_marginRight="-240dp">  <com.wj.views.RingViewHalf   android:id="@+id/ringView_half_right"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:layout_alignParentRight="true"   android:layout_centerVertical="true"   android:background="@drawable/bg_compatibility"   app:image_angle_rh="15"   app:image_padding_rh="20"   app:is_right_select_icon_rh="false"   app:list_rh="@array/zodiac_list" />  <ImageView   android:layout_width="70dp"   android:layout_height="70dp"   android:layout_alignParentRight="true"   android:layout_centerVertical="true"   android:layout_marginRight="@dimen/dp_220"   android:src="@drawable/icon_match_girl" />  </RelativeLayout>  <Button  android:id="@+id/check_btn"  android:layout_width="@dimen/dp_265"  android:layout_height="@dimen/dp_46"  android:layout_alignParentBottom="true"  android:layout_marginBottom="@dimen/dp_25"  android:layout_marginTop="@dimen/dp_25"  android:layout_centerHorizontal="true"  android:alpha="0.5"  android:enabled="true"  android:clickable="true"  android:background="@drawable/check" /> </RelativeLayout>

//这里是放半圆形转盘选择器上显示的图片list,我这里是用的xml静态传进去的,也可以改为动态方式传递

app:list_rh="@array/zodiac_list"

然后在values下面创建一个arrays.xml文件

<?xml version="1.0" encoding="utf-8"?> <resources>  <string-array name="zodiac_list">  <item>@drawable/zodiac_1</item>  <item>@drawable/zodiac_2</item>  <item>@drawable/zodiac_3</item>  <item>@drawable/zodiac_4</item>  <item>@drawable/zodiac_5</item>  <item>@drawable/zodiac_6</item>  <item>@drawable/zodiac_7</item>  <item>@drawable/zodiac_8</item>  <item>@drawable/zodiac_9</item>  <item>@drawable/zodiac_10</item>  <item>@drawable/zodiac_11</item>  <item>@drawable/zodiac_12</item>  </string-array> </resources>

关于如何在Android中自定义半圆形圆盘滚动选择器问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI