国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義半圓形圓盤滾動選擇器

2019-10-21 21:45:46
字體:
來源:轉載
供稿:網友

前段時間公司項目要求做一個特效的滑動選擇器,效果如下圖的樣子: 

 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>

到此就可以了。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 积石山| 义马市| 陆川县| 莱西市| 汶上县| 鹤岗市| 镇雄县| 贵南县| 南投县| 宜君县| 清新县| 山丹县| 五常市| 阜阳市| 新泰市| 开化县| 醴陵市| 乌兰察布市| 周至县| 嫩江县| 柯坪县| 红桥区| 建水县| 霞浦县| 麻江县| 彭阳县| 涞源县| 黄浦区| 北票市| 新巴尔虎左旗| 广德县| 永泰县| 赤城县| 巩留县| 昌都县| 鹤岗市| 莱芜市| 绥棱县| 通州市| 迁西县| 泸水县|