首页 > 系统 > Android > 正文

基于Android实现转盘按钮代码

2020-04-11 11:10:53
字体:
来源:转载
供稿:网友

先给大家展示下效果图:

package com.lixu.circlemenu; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.lixu.circlemenu.view.CircleImageView; import com.lixu.circlemenu.view.CircleLayout; import com.lixu.circlemenu.view.CircleLayout.OnItemClickListener; import com.lixu.circlemenu.view.CircleLayout.OnItemSelectedListener; import com.szugyi.circlemenu.R; public class MainActivity extends Activity implements OnItemSelectedListener, OnItemClickListener{   private  TextView selectedTextView;   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     CircleLayout circleMenu = (CircleLayout)findViewById(R.id.main_circle_layout);     circleMenu.setOnItemSelectedListener(this);     circleMenu.setOnItemClickListener(this);     //这个TextView仅仅作为演示转盘按钮以何为默认的选中项,     //默认的最底部的那一条被选中,然后显示到该TextView中。     selectedTextView = (TextView)findViewById(R.id.main_selected_textView);     selectedTextView.setText(((CircleImageView)circleMenu.getSelectedItem()).getName());   }   //圆盘转动到底部,则认为该条目被选中   @Override   public void onItemSelected(View view, int position, long id, String name) {         selectedTextView.setText(name);   }   //选择了转盘中的某一条。   @Override   public void onItemClick(View view, int position, long id, String name) {     Toast.makeText(getApplicationContext(), getResources().getString(R.string.start_app) + " " + name, Toast.LENGTH_SHORT).show();   } }

引用两个开源类:

 package com.lixu.circlemenu.view; /*  * Copyright Csaba Szugyiczki  *  * Licensed under the Apache License, Version . (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  * *   http://www.apache.org/licenses/LICENSE-. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.widget.ImageView; import com.szugyi.circlemenu.R; /** *  * @author Szugyi * Custom ImageView for the CircleLayout class. * Makes it possible for the image to have an angle, position and a name. * Angle is used for the positioning in the circle menu. */ public class CircleImageView extends ImageView {   private float angle = ;   private int position = ;   private String name;   public float getAngle() {     return angle;   }   public void setAngle(float angle) {     this.angle = angle;   }   public int getPosition() {     return position;   }   public void setPosition(int position) {     this.position = position;   }   public String getName(){     return name;   }   public void setName(String name){     this.name = name;   }   /**   * @param context   */   public CircleImageView(Context context) {     this(context, null);   }   /**   * @param context   * @param attrs   */   public CircleImageView(Context context, AttributeSet attrs) {     this(context, attrs, );   }   /**   * @param context   * @param attrs   * @param defStyle   */   public CircleImageView(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     if (attrs != null) {       TypedArray a = getContext().obtainStyledAttributes(attrs,           R.styleable.CircleImageView);       name = a.getString(R.styleable.CircleImageView_name);     }   } }  package com.lixu.circlemenu.view;  import com.szugyi.circlemenu.R;  /*  * Copyright Csaba Szugyiczki  *  * Licensed under the Apache License, Version . (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *   http://www.apache.org/licenses/LICENSE-.  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; /**  *   * @author Szugyi  * Creates a rotatable circle menu which can be parameterized by custom attributes.  * Handles touches and gestures to make the menu rotatable, and to make the   * menu items selectable and clickable.  *   */ public class CircleLayout extends ViewGroup {   // Event listeners   private OnItemClickListener mOnItemClickListener = null;   private OnItemSelectedListener mOnItemSelectedListener = null;   private OnCenterClickListener mOnCenterClickListener = null;   // Background image   private Bitmap imageOriginal, imageScaled;   private Matrix matrix;   private int mTappedViewsPostition = -;   private View mTappedView = null;   private int selected = ;   // Child sizes   private int mMaxChildWidth = ;   private int mMaxChildHeight = ;   private int childWidth = ;   private int childHeight = ;   // Sizes of the ViewGroup   private int circleWidth, circleHeight;   private int radius = ;   // Touch detection   private GestureDetector mGestureDetector;   // needed for detecting the inversed rotations   private boolean[] quadrantTouched;   // Settings of the ViewGroup   private boolean allowRotating = true;   private float angle = ;   private float firstChildPos = ;   private boolean rotateToCenter = true;   private boolean isRotating = true;   /**    * @param context    */   public CircleLayout(Context context) {     this(context, null);   }   /**    * @param context    * @param attrs    */   public CircleLayout(Context context, AttributeSet attrs) {     this(context, attrs, );   }   /**    * @param context    * @param attrs    * @param defStyle    */   public CircleLayout(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     init(attrs);   }   /**   * Initializes the ViewGroup and modifies it's default behavior by the passed attributes   * @param attrs  the attributes used to modify default settings   */   protected void init(AttributeSet attrs) {     mGestureDetector = new GestureDetector(getContext(),         new MyGestureListener());     quadrantTouched = new boolean[] { false, false, false, false, false };     if (attrs != null) {       TypedArray a = getContext().obtainStyledAttributes(attrs,           R.styleable.Circle);       // The angle where the first menu item will be drawn       angle = a.getInt(R.styleable.Circle_firstChildPosition, );       firstChildPos = angle;       rotateToCenter = a.getBoolean(R.styleable.Circle_rotateToCenter,           true);             isRotating = a.getBoolean(R.styleable.Circle_isRotating, true);       // If the menu is not rotating then it does not have to be centered       // since it cannot be even moved       if (!isRotating) {         rotateToCenter = false;       }       if (imageOriginal == null) {         int picId = a.getResourceId(             R.styleable.Circle_circleBackground, -);         // If a background image was set as an attribute,          // retrieve the image         if (picId != -) {           imageOriginal = BitmapFactory.decodeResource(               getResources(), picId);         }       }       a.recycle();       // initialize the matrix only once       if (matrix == null) {         matrix = new Matrix();       } else {         // not needed, you can also post the matrix immediately to         // restore the old state         matrix.reset();       }       // Needed for the ViewGroup to be drawn       setWillNotDraw(false);     }   }   /**   * Returns the currently selected menu   * @return the view which is currently the closest to the start position   */   public View getSelectedItem() {     return (selected >= ) ? getChildAt(selected) : null;   }   @Override   protected void onDraw(Canvas canvas) {     // the sizes of the ViewGroup     circleHeight = getHeight();     circleWidth = getWidth();     if (imageOriginal != null) {       // Scaling the size of the background image       if (imageScaled == null) {         matrix = new Matrix();         float sx = (((radius + childWidth / ) * ) / (float) imageOriginal             .getWidth());         float sy = (((radius + childWidth / ) * ) / (float) imageOriginal             .getHeight());         matrix.postScale(sx, sy);         imageScaled = Bitmap.createBitmap(imageOriginal, , ,             imageOriginal.getWidth(), imageOriginal.getHeight(),             matrix, false);       }       if (imageScaled != null) {         // Move the background to the center         int cx = (circleWidth - imageScaled.getWidth()) / ;         int cy = (circleHeight - imageScaled.getHeight()) / ;         Canvas g = canvas;         canvas.rotate(, circleWidth / , circleHeight / );         g.drawBitmap(imageScaled, cx, cy, null);       }     }   }   @Override   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     mMaxChildWidth = ;     mMaxChildHeight = ;     // Measure once to find the maximum child size.     int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(         MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);     int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(         MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);     final int count = getChildCount();     for (int i = ; i < count; i++) {       final View child = getChildAt(i);       if (child.getVisibility() == GONE) {         continue;       }       child.measure(childWidthMeasureSpec, childHeightMeasureSpec);       mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());       mMaxChildHeight = Math.max(mMaxChildHeight,           child.getMeasuredHeight());     }     // Measure again for each child to be exactly the same size.     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth,         MeasureSpec.EXACTLY);     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight,         MeasureSpec.EXACTLY);     for (int i = ; i < count; i++) {       final View child = getChildAt(i);       if (child.getVisibility() == GONE) {         continue;       }       child.measure(childWidthMeasureSpec, childHeightMeasureSpec);     }     setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),         resolveSize(mMaxChildHeight, heightMeasureSpec));   }   @Override   protected void onLayout(boolean changed, int l, int t, int r, int b) {     int layoutWidth = r - l;     int layoutHeight = b - t;     // Laying out the child views     final int childCount = getChildCount();     int left, top;     radius = (layoutWidth <= layoutHeight) ? layoutWidth /          : layoutHeight / ;     childWidth = (int) (radius / .);     childHeight = (int) (radius / .);     float angleDelay = / getChildCount();     for (int i = ; i < childCount; i++) {       final CircleImageView child = (CircleImageView) getChildAt(i);       if (child.getVisibility() == GONE) {         continue;       }       if (angle > ) {         angle -= ;       } else {         if (angle < ) {           angle += ;         }       }       child.setAngle(angle);       child.setPosition(i);       left = Math           .round((float) (((layoutWidth / ) - childWidth / ) + radius               * Math.cos(Math.toRadians(angle))));       top = Math           .round((float) (((layoutHeight / ) - childHeight / ) + radius               * Math.sin(Math.toRadians(angle))));       child.layout(left, top, left + childWidth, top + childHeight);       angle += angleDelay;     }   }   /**   * Rotate the buttons.   *    * @param degrees The degrees, the menu items should get rotated.   */   private void rotateButtons(float degrees) {     int left, top, childCount = getChildCount();     float angleDelay = / childCount;     angle += degrees;     if (angle > ) {       angle -= ;     } else {       if (angle < ) {         angle += ;       }     }     for (int i = ; i < childCount; i++) {       if (angle > ) {         angle -= ;       } else {         if (angle < ) {           angle += ;         }       }       final CircleImageView child = (CircleImageView) getChildAt(i);       if (child.getVisibility() == GONE) {         continue;       }       left = Math           .round((float) (((circleWidth / ) - childWidth / ) + radius               * Math.cos(Math.toRadians(angle))));       top = Math           .round((float) (((circleHeight / ) - childHeight / ) + radius               * Math.sin(Math.toRadians(angle))));       child.setAngle(angle);       if (Math.abs(angle - firstChildPos) < (angleDelay / )           && selected != child.getPosition()) {         selected = child.getPosition();         if (mOnItemSelectedListener != null && rotateToCenter) {           mOnItemSelectedListener.onItemSelected(child, selected,               child.getId(), child.getName());         }       }       child.layout(left, top, left + childWidth, top + childHeight);       angle += angleDelay;     }   }   /**   * @return The angle of the unit circle with the image view's center   */   private double getAngle(double xTouch, double yTouch) {     double x = xTouch - (circleWidth / d);     double y = circleHeight - yTouch - (circleHeight / d);     switch (getQuadrant(x, y)) {     case :       return Math.asin(y / Math.hypot(x, y)) * / Math.PI;     case :     case :       return - (Math.asin(y / Math.hypot(x, y)) * / Math.PI);     case :       return + Math.asin(y / Math.hypot(x, y)) * / Math.PI;     default:       // ignore, does not happen       return ;     }   }   /**   * @return The selected quadrant.   */   private static int getQuadrant(double x, double y) {     if (x >= ) {       return y >= ? : ;     } else {       return y >= ? : ;     }   }   private double startAngle;   @Override   public boolean onTouchEvent(MotionEvent event) {     if (isEnabled()) {       if (isRotating) {         switch (event.getAction()) {         case MotionEvent.ACTION_DOWN:           // reset the touched quadrants           for (int i = ; i < quadrantTouched.length; i++) {             quadrantTouched[i] = false;           }           allowRotating = false;           startAngle = getAngle(event.getX(), event.getY());           break;         case MotionEvent.ACTION_MOVE:           double currentAngle = getAngle(event.getX(), event.getY());           rotateButtons((float) (startAngle - currentAngle));           startAngle = currentAngle;           break;         case MotionEvent.ACTION_UP:           allowRotating = true;           rotateViewToCenter((CircleImageView) getChildAt(selected),               false);           break;         }       }       // set the touched quadrant to true       quadrantTouched[getQuadrant(event.getX() - (circleWidth / ),           circleHeight - event.getY() - (circleHeight / ))] = true;       mGestureDetector.onTouchEvent(event);       return true;     }     return false;   }   private class MyGestureListener extends SimpleOnGestureListener {     @Override     public boolean onFling(MotionEvent e, MotionEvent e, float velocityX,         float velocityY) {       if (!isRotating) {         return false;       }       // get the quadrant of the start and the end of the fling       int q = getQuadrant(e.getX() - (circleWidth / ), circleHeight           - e.getY() - (circleHeight / ));       int q = getQuadrant(e.getX() - (circleWidth / ), circleHeight           - e.getY() - (circleHeight / ));       // the inversed rotations       if ((q == && q == && Math.abs(velocityX) < Math           .abs(velocityY))           || (q == && q == )           || (q == && q == )           || (q == && q == && Math.abs(velocityX) > Math               .abs(velocityY))           || ((q == && q == ) || (q == && q == ))           || ((q == && q == ) || (q == && q == ))           || (q == && q == && quadrantTouched[])           || (q == && q == && quadrantTouched[])) {         CircleLayout.this.post(new FlingRunnable(-             * (velocityX + velocityY)));       } else {         // the normal rotation         CircleLayout.this             .post(new FlingRunnable(velocityX + velocityY));       }       return true;     }     @Override     public boolean onSingleTapUp(MotionEvent e) {       mTappedViewsPostition = pointToPosition(e.getX(), e.getY());       if (mTappedViewsPostition >= ) {         mTappedView = getChildAt(mTappedViewsPostition);         mTappedView.setPressed(true);       } else {         float centerX = circleWidth / ;         float centerY = circleHeight / ;         if (e.getX() < centerX + (childWidth / )             && e.getX() > centerX - childWidth /              && e.getY() < centerY + (childHeight / )             && e.getY() > centerY - (childHeight / )) {           if (mOnCenterClickListener != null) {             mOnCenterClickListener.onCenterClick();             return true;           }         }       }       if (mTappedView != null) {         CircleImageView view = (CircleImageView) (mTappedView);         if (selected != mTappedViewsPostition) {           rotateViewToCenter(view, false);           if (!rotateToCenter) {             if (mOnItemSelectedListener != null) {               mOnItemSelectedListener.onItemSelected(mTappedView,                   mTappedViewsPostition, mTappedView.getId(), view.getName());             }             if (mOnItemClickListener != null) {               mOnItemClickListener.onItemClick(mTappedView,                   mTappedViewsPostition, mTappedView.getId(), view.getName());             }           }         } else {           rotateViewToCenter(view, false);           if (mOnItemClickListener != null) {             mOnItemClickListener.onItemClick(mTappedView,                 mTappedViewsPostition, mTappedView.getId(), view.getName());           }         }         return true;       }       return super.onSingleTapUp(e);     }   }   /**   * Rotates the given view to the center of the menu.   * @param view      the view to be rotated to the center   * @param fromRunnable  if the method is called from the runnable which animates the rotation   *             then it should be true, otherwise false    */   private void rotateViewToCenter(CircleImageView view, boolean fromRunnable) {     if (rotateToCenter) {       float velocityTemp = ;       float destAngle = (float) (firstChildPos - view.getAngle());       float startAngle = ;       int reverser = ;       if (destAngle < ) {         destAngle += ;       }       if (destAngle > ) {         reverser = -;         destAngle = - destAngle;       }       while (startAngle < destAngle) {         startAngle += velocityTemp / ;         velocityTemp *= .F;       }       CircleLayout.this.post(new FlingRunnable(reverser * velocityTemp,           !fromRunnable));     }   }   /**   * A {@link Runnable} for animating the menu rotation.   */   private class FlingRunnable implements Runnable {     private float velocity;     float angleDelay;     boolean isFirstForwarding = true;     public FlingRunnable(float velocity) {       this(velocity, true);     }     public FlingRunnable(float velocity, boolean isFirst) {       this.velocity = velocity;       this.angleDelay = / getChildCount();       this.isFirstForwarding = isFirst;     }     public void run() {       if (Math.abs(velocity) > && allowRotating) {         if (rotateToCenter) {           if (!(Math.abs(velocity) < && (Math.abs(angle               - firstChildPos)               % angleDelay < ))) {             rotateButtons(velocity / );             velocity /= .F;             CircleLayout.this.post(this);           }         } else {           rotateButtons(velocity / );           velocity /= .F;           CircleLayout.this.post(this);         }       } else {         if (isFirstForwarding) {           isFirstForwarding = false;           CircleLayout.this.rotateViewToCenter(               (CircleImageView) getChildAt(selected), true);         }       }     }   }   private int pointToPosition(float x, float y) {     for (int i = ; i < getChildCount(); i++) {       View item = (View) getChildAt(i);       if (item.getLeft() < x && item.getRight() > x & item.getTop() < y           && item.getBottom() > y) {         return i;       }     }     return -;   }   public void setOnItemClickListener(OnItemClickListener onItemClickListener) {     this.mOnItemClickListener = onItemClickListener;   }   public interface OnItemClickListener {     void onItemClick(View view, int position, long id, String name);   }   public void setOnItemSelectedListener(       OnItemSelectedListener onItemSelectedListener) {     this.mOnItemSelectedListener = onItemSelectedListener;   }   public interface OnItemSelectedListener {     void onItemSelected(View view, int position, long id, String name);   }   public interface OnCenterClickListener {     void onCenterClick();   }   public void setOnCenterClickListener(       OnCenterClickListener onCenterClickListener) {     this.mOnCenterClickListener = onCenterClickListener;   } }

xml文件:
 
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:circle="http://schemas.android.com/apk/res/com.szugyi.circlemenu"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      tools:context=".MainActivity" >
      <com.lixu.circlemenu.view.CircleLayout
          android:id="@+id/main_circle_layout"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:layout_above="@+id/main_selected_textView"
         android:layout_gravity="center_horizontal"
         circle:firstChildPosition="South"
         circle:rotateToCenter="true"
         circle:isRotating="true" >      
 <!--         circle:circleBackground="@drawable/green"  > -->
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_facebook_image"
             android:layout_width="dp"
             android:layout_height="dp"
             android:src="@drawable/icon_facebook"
             circle:name="@string/facebook" />
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_myspace_image"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/icon_myspace"
             circle:name="@string/myspace" />
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_google_image"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/icon_google"
             circle:name="@string/google" />
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_linkedin_image"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/icon_linkedin"
             circle:name="@string/linkedin" />
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_twitter_image"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/icon_twitter"
             circle:name="@string/twitter" />
         <com.lixu.circlemenu.view.CircleImageView
             android:id="@+id/main_wordpress_image"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/icon_wordpress"
             circle:name="@string/wordpress" />
     </com.lixu.circlemenu.view.CircleLayout>
     <TextView
         android:id="@+id/main_selected_textView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_centerHorizontal="true"
         android:layout_marginBottom="dp"
         android:textAppearance="?android:attr/textAppearanceLarge" />
 </RelativeLayout>

基于Android实现转盘按钮代码的全部内容就到此结束了,希望能够帮助到大家。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表