Intereting Posts
Синхронизация модели частичной базы данных с сервера на клиент Зарегистрировать идентификатор устройства непосредственно с помощью Amazon SNS Полноэкранный просмотр без растягивания видео Стратегия старта местоположения Как реализовать индикатор выполнения загрузки файлов в android Получение токена авторизации Google oauth с Android-return с invalid_scope / Неизвестная ошибка "Http: // {root_dir} / oauth / token" Файл не найден в Magento для регистрации приложения REST API Android – Как изменить толщину по умолчанию SeekBar? Как запустить эмулятор Android автоматически из Eclipse? Как показывать значки в меню переполнения ActionBar? Android Speech Recognition API не работает в Android 7 Nougat Есть ли способ настроить порог для прокрутки ViewPager? Как построить только single buildType с Android Studio и / или Gradle Custom Lint для Java / Android Report, если мы найдем вызов класса без реализации его интерфейса Android FinalizerDaemon повесил трубку

Как я могу реализовать круговое управление на основе swipe, как это?

Я работаю над Android-приложением, и у меня есть TextView, где я показываю цену (например, 50 $).

Я хотел бы иметь круговой контроль, подобный этой картине:

Введите описание изображения здесь

  • Прокручивание пальца по часовой стрелке на циферблате увеличивает сумму на 1 доллар США
  • Прокрутка пальца против часовой стрелки на циферблате уменьшает сумму на 1 доллар США

Я провел некоторое исследование, но не смог найти что-то для этого.

Как вы могли бы создать такое круговое управление, управляемое звуками?

Solutions Collecting From Web of "Как я могу реализовать круговое управление на основе swipe, как это?"

Я изменил источник roundseekbar для работы по вашему желанию.

Вы можете получить mofidied класс из измененного cirucularseekbar

Сначала Включите элемент управления в свой макет и установите свой циферблат в качестве фона

<com.yourapp.CircularSeekBar android:id="@+id/circularSeekBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/amount_wheel_bg" /> 

Затем в вашей деятельности (он должен реализовать OnCircularSeekBarChangeListener) добавить следующее:

 //This is a reference to the layout control private CircularSeekBar circularSeekBar; //This is a reference to the textbox where you want to display the amount private EditText amountEditText; private int previousProgress = -1; 

И добавьте следующие методы обратного вызова:

 @Override public void onProgressChanged(CircularSeekBar circularSeekBar, int progress, boolean fromUser) { if(previousProgress == -1) { //This is the first user touch we take it as a reference previousProgress = progress; } else { //The user is holding his finger down if(progress == previousProgress) { //he is still in the same position, we don't do anything } else { //The user is moving his finger we need to get the differences int difference = progress - previousProgress; if(Math.abs(difference) > CircularSeekBar.DEFAULT_MAX/2) { //The user is at the top of the wheel he is either moving from the 0 -> MAx or Max -> 0 //We have to consider this as 1 step //to force it to be 1 unit and reverse sign; difference /= Math.abs(difference); difference -= difference; } //update the amount selectedAmount += difference; previousProgress= progress; updateAmountText(); } } } @Override public void onStopTrackingTouch(CircularSeekBar seekBar) { //reset the tracking progress previousProgress = -1; } @Override public void onStartTrackingTouch(CircularSeekBar seekBar) { } private void updateAmountText() { amountEditText.setText(String.format("%.2f", selectedAmount)); } 

SelectedAmount – двойное свойство для хранения выбранной суммы.

Я надеюсь это тебе поможет.

Класс DialView:

 public abstract class DialView extends View { private float centerX; private float centerY; private float minCircle; private float maxCircle; private float stepAngle; public DialView(Context context) { super(context); stepAngle = 1; setOnTouchListener(new OnTouchListener() { private float startAngle; private boolean isDragging; @Override public boolean onTouch(View v, MotionEvent event) { float touchX = event.getX(); float touchY = event.getY(); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: startAngle = touchAngle(touchX, touchY); isDragging = isInDiscArea(touchX, touchY); break; case MotionEvent.ACTION_MOVE: if (isDragging) { float touchAngle = touchAngle(touchX, touchY); float deltaAngle = (360 + touchAngle - startAngle + 180) % 360 - 180; if (Math.abs(deltaAngle) > stepAngle) { int offset = (int) deltaAngle / (int) stepAngle; startAngle = touchAngle; onRotate(offset); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isDragging = false; break; } return true; } }); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { centerX = getMeasuredWidth() / 2f; centerY = getMeasuredHeight() / 2f; super.onLayout(changed, l, t, r, b); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { float radius = Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2f; Paint paint = new Paint(); paint.setDither(true); paint.setAntiAlias(true); paint.setStyle(Style.FILL); paint.setColor(0xFFFFFFFF); paint.setXfermode(null); LinearGradient linearGradient = new LinearGradient( radius, 0, radius, radius, 0xFFFFFFFF, 0xFFEAEAEA, Shader.TileMode.CLAMP); paint.setShader(linearGradient); canvas.drawCircle(centerX, centerY, maxCircle * radius, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawCircle(centerX, centerY, minCircle * radius, paint); paint.setXfermode(null); paint.setShader(null); paint.setColor(0x15000000); for (int i = 0, n = 360 / (int) stepAngle; i < n; i++) { double rad = Math.toRadians((int) stepAngle * i); int startX = (int) (centerX + minCircle * radius * Math.cos(rad)); int startY = (int) (centerY + minCircle * radius * Math.sin(rad)); int stopX = (int) (centerX + maxCircle * radius * Math.cos(rad)); int stopY = (int) (centerY + maxCircle * radius * Math.sin(rad)); canvas.drawLine(startX, startY, stopX, stopY, paint); } super.onDraw(canvas); } /** * Define the step angle in degrees for which the * dial will call {@link #onRotate(int)} event * @param angle : angle between each position */ public void setStepAngle(float angle) { stepAngle = Math.abs(angle % 360); } /** * Define the draggable disc area with relative circle radius * based on min(width, height) dimension (0 = center, 1 = border) * @param radius1 : internal or external circle radius * @param radius2 : internal or external circle radius */ public void setDiscArea(float radius1, float radius2) { radius1 = Math.max(0, Math.min(1, radius1)); radius2 = Math.max(0, Math.min(1, radius2)); minCircle = Math.min(radius1, radius2); maxCircle = Math.max(radius1, radius2); } /** * Check if touch event is located in disc area * @param touchX : X position of the finger in this view * @param touchY : Y position of the finger in this view */ private boolean isInDiscArea(float touchX, float touchY) { float dX2 = (float) Math.pow(centerX - touchX, 2); float dY2 = (float) Math.pow(centerY - touchY, 2); float distToCenter = (float) Math.sqrt(dX2 + dY2); float baseDist = Math.min(centerX, centerY); float minDistToCenter = minCircle * baseDist; float maxDistToCenter = maxCircle * baseDist; return distToCenter >= minDistToCenter && distToCenter <= maxDistToCenter; } /** * Compute a touch angle in degrees from center * North = 0, East = 90, West = -90, South = +/-180 * @param touchX : X position of the finger in this view * @param touchY : Y position of the finger in this view * @return angle */ private float touchAngle(float touchX, float touchY) { float dX = touchX - centerX; float dY = centerY - touchY; return (float) (270 - Math.toDegrees(Math.atan2(dY, dX))) % 360 - 180; } protected abstract void onRotate(int offset); } 

Используй это :

 public class DialActivity extends Activity { @Override protected void onCreate(Bundle state) { setContentView(new RelativeLayout(this) { private int value = 0; private TextView textView; { addView(new DialView(getContext()) { { // a step every 20° setStepAngle(20f); // area from 30% to 90% setDiscArea(.30f, .90f); } @Override protected void onRotate(int offset) { textView.setText(String.valueOf(value += offset)); } }, new RelativeLayout.LayoutParams(0, 0) { { width = MATCH_PARENT; height = MATCH_PARENT; addRule(RelativeLayout.CENTER_IN_PARENT); } }); addView(textView = new TextView(getContext()) { { setText(Integer.toString(value)); setTextColor(Color.WHITE); setTextSize(30); } }, new RelativeLayout.LayoutParams(0, 0) { { width = WRAP_CONTENT; height = WRAP_CONTENT; addRule(RelativeLayout.CENTER_IN_PARENT); } }); } }); super.onCreate(state); } } 

Результат:

результат

Я только что написал следующий код и только теоретически протестировал его.

 private final double stepSizeAngle = Math.PI / 10f; //Angle diff to increase/decrease dial by 1$ private final double dialStartValue = 50.0; //Center of your dial private float dialCenterX = 500; private float dialCenterY = 500; private float fingerStartDiffX; private float fingerStartDiffY; private double currentDialValueExact = dialStartValue; public boolean onTouchEvent(MotionEvent event) { int eventaction = event.getAction(); switch (eventaction) { case MotionEvent.ACTION_DOWN: //Vector between startpoint and center fingerStartDiffX = event.getX() - dialCenterX; fingerStartDiffY = event.getY() - dialCenterY; break; case MotionEvent.ACTION_MOVE: //Vector between current point and center float xDiff = event.getX() - dialCenterX; float yDiff = event.getY() - dialCenterY; //Range from -PI to +PI double alpha = Math.atan2(fingerStartDiffY, yDiff) - Math.atan2(fingerStartDiffX, xDiff); //calculate exact difference between last move and current move. //This will take positive and negative direction into account. double dialIncrease = alpha / stepSizeAngle; currentDialValueExact += dialIncrease; //Round down if we're above the start value and up if we are below setDialValue((int)(currentDialValueExact > dialStartValue ? Math.floor(currentDialValueExact) : Math.ceil(currentDialValueExact)); //set fingerStartDiff to the current position to allow multiple rounds on the dial fingerStartDiffX = xDiff; fingerStartDiffY = yDiff; break; } // tell the system that we handled the event and no further processing is required return true; } private void setDialValue(int value) { //assign value } 

Если вы хотите изменить направление, просто выполните alpha = -alpha .

Возможно, вы можете посмотреть в onTouchEvent(MotionEvent) представления. Следите за координатами x и y при перемещении пальца. Обратите внимание, что шаблон изменения координат изменяется при перемещении пальца. Вы можете использовать это для достижения увеличения / уменьшения цены. См. Эту link .

Вы можете использовать Android Gesture Builder из образцов Android SDK.

Я не могу проверить это прямо сейчас, но вы должны иметь возможность создавать приложение из образца, запускать его, создавать собственные жесты ( круговые по часовой стрелке и круговые против часовой стрелки ), а затем получать жесты с необработанного файла с устройства / Emulator (создается приложением после того, как вы делаете жесты).

При этом вы можете импортировать его в свой проект и использовать Gesture Library для перехвата, регистрации и распознавания конкретных жестов. Вы в основном добавляете оверлейный макет, где хотите, чтобы жест был захвачен, а затем вы решаете, что с ним делать.

Более подробно, пошаговое руководство по следующей ссылке: http://www.techotopia.com/index.php/Implementing_Android_Custom_Gesture_and_Pinch_Recognition

OvalSeekbar lib делает что-то подобное, я предлагаю вам посмотреть, как в нем происходят события движения. Вот ссылка на его git https://github.com/kshoji/AndroidCustomViews

Я написал этот пользовательский FrameLayout, чтобы обнаружить круговое движение вокруг его центральной точки. Я использую ориентацию трех точек на плоскости и угол между ними, чтобы определить, когда пользователь сделал половину круга в одном направлении, а затем завершает его в том же самом.

 public class CircularDialView extends FrameLayout implements OnTouchListener { private TextView counter; private int count = 50; private PointF startTouch; private PointF currentTouch; private PointF center; private boolean turning; private boolean switched = false; public enum RotationOrientation { CW, CCW, LINEAR; } private RotationOrientation lastRotatationDirection; public CircularDialView(Context context) { super(context); init(); } public CircularDialView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CircularDialView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { this.startTouch = new PointF(); this.currentTouch = new PointF(); this.center = new PointF(); this.turning = false; this.setBackgroundResource(R.drawable.dial); this.counter = new TextView(getContext()); this.counter.setTextSize(20); FrameLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER; addView(this.counter, params); updateCounter(); this.setOnTouchListener(this); } private void updateCounter() { this.counter.setText(Integer.toString(count)); } // need to keep the view square @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, widthMeasureSpec); center.set(getWidth()/2, getWidth()/2); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { startTouch.set(event.getX(), event.getY()); turning = true; return true; } case MotionEvent.ACTION_MOVE: { if(turning) { currentTouch.set(event.getX(), event.getY()); RotationOrientation turningDirection = getOrientation(center, startTouch, currentTouch); if (lastRotatationDirection != turningDirection) { double angle = getRotationAngle(center, startTouch, currentTouch); Log.d ("Angle", Double.toString(angle)); // the touch event has switched its orientation // and the current touch point is close to the start point // a full cycle has been made if (switched && angle < 10) { if (turningDirection == RotationOrientation.CCW) { count--; updateCounter(); switched = false; } else if (turningDirection == RotationOrientation.CW) { count++; updateCounter(); switched = false; } } // checking if the angle is big enough is needed to prevent // the user from switching from the start point only else if (!switched && angle > 170) { switched = true; } } lastRotatationDirection = turningDirection; return true; } } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { turning = false; return true; } } return false; } // checks the orientation of three points on a plane private RotationOrientation getOrientation(PointF a, PointF b, PointF c){ double face = ax * by + bx * cy + cx * ay - (cx * by + bx * ay + ax * cy); if (face > 0) return RotationOrientation.CW; else if (face < 0) return RotationOrientation.CCW; else return RotationOrientation.LINEAR; } // using dot product to calculate the angle between the vectors ab and ac public double getRotationAngle(PointF a, PointF b, PointF c){ double len1 = dist (a, b); double len2 = dist (a, c); double product = (bx - ax) * (cx - ax) + (by - ay) * (cy - ay); return Math.toDegrees(Math.acos(product / (len1 * len2))); } // calculates the distance between two points on a plane public double dist (PointF a, PointF b) { return Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)); } } 

У меня был друг, которому нужно было внедрить нечто похожее, как то, что вы хотите.

Он фактически использовал обнаружение жестов – GestureOverlayView и MotionEvent .

Создав свои собственные жесты, он сумел реализовать это.

Мой друг в основном ссылался на этот сайт. Там есть пример кода.

Надеюсь, вы найдете это полезным!

Вам нужно использовать Circular SeekBar, вы можете найти образец и библиотеку отсюда

Здесь может быть полезен и другой.

Спасибо, я надеюсь, что это поможет.

Используйте NumberPicker , который проще, а затем настройте его!

NumberPicker