Android自定义半圆形圆盘滚动选择器

前段时间公司项目要求做一个特效的滑动选择器,效果如下图的样子:

功能要求:两边的半圆形转盘可以转动,转盘上的图标也一起滚动,蓝红色图标指着的小图标变成高亮选中状态。

第一眼看到这个需求就想到这个必须要用自定义控件来做才行,于是产生了这样的思路:

半圆形的滚动的转盘自定义view继承viewgroup,重写滑动事件,自定义圆盘上图片的摆放角度,至于蓝色和红色箭头图标指向的选中状态可以用坐标数组绘制一个区域来判断是否有符合条件的图标滚动到了这个位置,如果有的话就将这个图标所在的控件透明度设置为1,如果没到这个位置就设置为非选中状态0.5透明度 ,思路这样定下来了,预计可以行得通,于是开始进行实际的尝试写代码实现这个自定义的控件和功能。

下面我直接把核心代码附上,注释比较清晰:

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>

到此就可以了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义wheelview实现滚动日期选择器

    本文实例为大家分享了Android实现滚动日期选择器的具体代码,供大家参考,具体内容如下 wheelview滚动效果的View 这段时间需要用到一个时间选择器,但是不能使用日期对话框, 因为它是筛选条件框架下的,只能是View!这个WheelView改造后可以达到要求! 这个wheelview框架使用的类不多,就几个,还有一些资源文件. 我根据这个框架设计了日期的选择器. 主页面: 第一种日期选择器页面: 动态效果: 使用: 具体的实现是一个LoopView的类,这是一个继承View的类! 理解

  • Android自定义可循环的滚动选择器CycleWheelView

    最近碰到个项目要使用到滚动选择器,原生的NumberPicker可定制性太差,不大符合UI要求. 网上开源的WheelView是用ScrollView写的,不能循环滚动,而且当数据量很大时要加载的Item太多,性能非常低. 然后,还是自己写一个比较靠谱,用的是ListView实现的.写完自己体验了一下,性能不错,再大的数据也不怕了. 感觉不错,重新封装了一下,提供了一些接口可以直接按照自己的需求定制,调用方法在MainActivity中. 补个图片: 不多说了,直接上代码: CycleWheel

  • Android自定义控件实现按钮滚动选择效果

    本文实例为大家分享了Android实现按钮滚动选择效果的具体代码,供大家参考,具体内容如下 效果图 代码实现 package com.demo.ui.view; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Handler; import android.support.v4.content.ContextCompat;

  • Android自定义滚动选择器实例代码

    Android自定义滚动选择器 实现图片的效果 代码如下 package com.linzihui.widget; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graph

  • Android PickerView滚动选择器的使用方法

    手机里设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器,前几天用手机刷了MIUI,发现自带的那个时间选择器效果挺好看的,于是就自己仿写了一个,权当练手.先来看效果: 效果还行吧?实现思路就是自定义一个PickerView,单独滚动的是一个PickerView,显然上图中有分和秒的选择所以在布局里用了两个PickerView.由于这里不涉及到text的点击事件,所以只需要继承View就行了,直接把text用canvas画上去.PickerView的实现的主要难点: 难点1: 字体随距离的渐变

  • Android仿iphone自定义滚动选择器

    本文实例为大家分享了Android仿iphone自定义滚动选择器的具体代码,供大家参考,具体内容如下 一.多的不说,效果图,先走起 二.实例源码 (1)自定义控件 package com.pickerscrollview.views; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.annotation.Sup

  • Android自定义半圆形圆盘滚动选择器

    前段时间公司项目要求做一个特效的滑动选择器,效果如下图的样子: 功能要求:两边的半圆形转盘可以转动,转盘上的图标也一起滚动,蓝红色图标指着的小图标变成高亮选中状态. 第一眼看到这个需求就想到这个必须要用自定义控件来做才行,于是产生了这样的思路: 半圆形的滚动的转盘自定义view继承viewgroup,重写滑动事件,自定义圆盘上图片的摆放角度,至于蓝色和红色箭头图标指向的选中状态可以用坐标数组绘制一个区域来判断是否有符合条件的图标滚动到了这个位置,如果有的话就将这个图标所在的控件透明度设置为1,如

  • Android仿IOS10圆盘时间选择器

    介绍 这是一款仿IOS10(就寝功能)的圆盘时间选择器 项目演示 实现思路 以720度为一个周期,0~360°对应0~12小时,360°~720°对应12~24小时 这里以"开始时间设置按钮"为例来谈谈它的滑动实现: 将"开始时间设置按钮"作为点A,表盘中心作为点O,手指触摸点作为点P.通过反正切公式可以计算出∠AOP的大小,然后随着手指的位置不断变化去更新点A的位置(即点A的角度). // 坐标系的直线表达式 // 直线l1的表达式子:过钟表中心点和开始控件中心点

  • Android自定义view实现滚动选择控件详解

    目录 前言 需求 编写代码 主要问题 前言 上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec.onMeasure以及onLayout,接下来就用一个滚动选择的控件(View)来学一下onDraw的使用,并且了解下在XML自定义控件参数. 需求 这里就是一个滚动选择文字的控件,还是挺常见的,之前用别人的,现在选择手撕一个,核心思想如下: 1.有三层不同大小及透明度的选项,选中项放在中间 2.接受一个列表的数据,静态时显示三个值,滚动时显示四个

  • Android自定义ViewFlipper实现滚动效果

    本文实例为大家分享了自定义view实现了类似百度手机助手,首页评论滚动效果. 看效果: gif做的不好,其效果就是:几个viewitem不停的向上滚动,新加入item有个淡入的效果. 说下实现思路:自定义view继承至LinearLayout,控制item数量及其动画效果,实现item复用,传入数据即可,使用方便. 代码: /** * Jiantao.Yang * * @description 仿百度手机助手,评论滚动效果 * @time 2015/1/16 17:37 */ public cl

  • Android自定义View实现角度选择器

    首先来看一下Google Photos的效果 实现最终的效果: 实现思路 仔细观察这个效果,先分析构成结构,我把它分成三部分: 1.表示刻度的点 2.相应点上方的数字 3.控件中央的当前刻度与三角 可以看出,构成元素十分简单,不涉及图片,Drawable,那么只需要用Canvas画出来就好了. 接下来观察手势的操作,查看随着手指滑动,控件做出的变化,这里的变化有: 1.手指按上去时,部分区域变亮(部分区域即为可见区域) 2.随着手指滑动,相应的数字发生移动,当前角度值也发生改变 3.离中心越近,

  • Android自定义View仿IOS圆盘时间选择器

    通过自定义view实现仿iOS实现滑动两端的点选择时间的效果 效果图 自定义的view代码 public class Ring_Slide2 extends View { private static final double RADIAN = 180 / Math.PI; private int max_progress; // 设置最大进度 private int cur_progress; //设置锚点1当前进度 private int cur_progress2; //设置锚点2进度 p

随机推荐