我创造了一个弧形。我想在点击弧时在不同的弧上做某些事情。我怎么知道弧是否被触摸?有人可以提供一些onTouch方法的代码来进行这样的计算。还请稍微解释一下。
package com.example.android.customviews;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
/**
* Limit Indicator is used to show any kind of limits such as Balance and Actual
* Amount of wallet present in an account. In order to use this in the XML
* Layout, please include the following: <br />
* <br />
*
* xmlns:custom="http://schemas.android.com/apk/res/com.example.android.customviews"
*
* <br /> <br />
*
* Following custom attributes are provided: <br />
* <br />
*
* custom:borderColor <br />
* custom:borderRadius <br />
* custom:outerCircleRadius <br />
* custom:text <br />
* custom:textSize <br />
* custom:innerCircleColor <br />
*
* @author Syed Ahmed Hussain
*/
public class LimitIndicator extends ViewGroup {
// ============================================================================================
// Variables Declaration
private int mInnerCircleColor;
private int mBorderColor;
private int mTextColor;
private float mTextSize;
private String mTitleText = "";
private float mHalfOfBorderWidth = 0.0f;
private float mOuterCircleRadius = 2.0f;
private float mBorderWidth = 30.0f;
private Paint mDialPaint, mTextPaint, mBorderPaint, mInnerCirclePaint;
private float mCenterX = 100.0f;
private float mCenterY = 100.0f;
private int mTotalProgressInDegrees;
private int mTotalProgress = -1;
// Start Angle should be 90 degrees to create a clockwise illusion.
private int mStartAngle = 270;
// This should be the one which provides us a percentage wise drawing
private int mSweepAngle = 1;
private RectF mBorderBounds = null;
// ============================================================================================
// Constructors
public LimitIndicator(Context pContext) {
super(pContext);
Log.d("LimitIndicator", "LimitIndicator(Context pContext) called");
initialize();
}
public LimitIndicator(Context pContext, AttributeSet pAttrs) {
super(pContext, pAttrs);
Log.d("LimitIndicator", "LimitIndicator(Context pContext, AttributeSet pAttrs) called");
TypedArray typedArray = pContext.obtainStyledAttributes(pAttrs, R.styleable.LimitIndicator, 0, 0);
try {
mOuterCircleRadius = typedArray.getDimension(R.styleable.LimitIndicator_outerCircleRadius, mOuterCircleRadius);
mTextColor = typedArray.getColor(R.styleable.LimitIndicator_textColor, Color.WHITE);
mTitleText = typedArray.getString(R.styleable.LimitIndicator_text);
mTextSize = typedArray.getDimension(R.styleable.LimitIndicator_textSize, 25);
mTotalProgress = typedArray.getInteger(R.styleable.LimitIndicator_numerator, mTotalProgress);
mBorderColor = typedArray.getColor(R.styleable.LimitIndicator_borderColor, Color.BLACK);
mBorderWidth = typedArray.getDimension(R.styleable.LimitIndicator_borderRadius, mBorderWidth);
mInnerCircleColor = typedArray.getColor(R.styleable.LimitIndicator_innerCircleColor, Color.GREEN);
} finally {
typedArray.recycle();
}
initialize();
}
// ============================================================================================
// Initialization
/**
* Initialize all elements
*/
private void initialize() {
// Set up the paint for the dial
mDialPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDialPaint.setStyle(Paint.Style.FILL);
mDialPaint.setColor(Color.GRAY);
// Set up the paint for the label text
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setColor(mTextColor);
mTextPaint.setTextSize(mTextSize);
// Set up the paint for the border
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setDither(true);
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setColor(mInnerCircleColor);
// mBorderPaint.setStrokeJoin(Paint.Join.ROUND);
// mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
mBorderBounds = new RectF(getLeft(), getTop(), getRight(), getBottom());
}
// ============================================================================================
// Drawing on surface
@Override
protected void onDraw(Canvas pCanvas) {
super.onDraw(pCanvas);
Log.d("LimitIndicator", "OnDraw called");
Log.d("Measured Spec Width", mCenterX + "");
Log.d("Measured Spec Height", mCenterY + "");
pCanvas.drawCircle(mCenterX, mCenterY, mOuterCircleRadius, mDialPaint);
pCanvas.drawCircle(mCenterX, mCenterY, (float) (mOuterCircleRadius - mBorderWidth + 1) , mInnerCirclePaint);
pCanvas.drawText(mTitleText, mCenterX, mCenterY + 5, mTextPaint);
pCanvas.drawArc(mBorderBounds, mStartAngle, mSweepAngle, false, mBorderPaint);
if (mSweepAngle < mTotalProgressInDegrees) {
mSweepAngle+=3;
mBorderPaint.setStrokeWidth(mBorderWidth++);
invalidate();
}
}
@Override
protected void onLayout(boolean pChanged, int pLeft, int pTop, int pRight, int pBottom) {
Log.d("LimitIndicator", "OnLayout called");
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).layout(0, 0, pRight, pBottom);
}
}
@Override
protected void onSizeChanged(int pW, int pH, int pOldw, int pOldh) {
super.onSizeChanged(pW, pH, pOldw, pOldh);
Log.d("LimitIndicator", "OnSizeChanged called");
float xPad = (getPaddingLeft() + getPaddingRight());
float yPad = (getPaddingTop() + getPaddingBottom());
// To draw Circle in the middle
mCenterX = (float) ((pW - xPad) * 0.5);
mCenterY = (float) ((pH - yPad) * 0.5);
// This (mBorderBounds.bottom needs to be fixed. Width &
// Height should be equal in order
// to create a perfect circle. Otherwise an
// Oval will be created! :P
// Bounds for creating an arc
mHalfOfBorderWidth = (float) (mBorderWidth * 0.5);
mBorderBounds.right = mCenterX + mOuterCircleRadius - mHalfOfBorderWidth;
mBorderBounds.left = mCenterX - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.top = mCenterY - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.bottom = mCenterY + mOuterCircleRadius - mHalfOfBorderWidth;
}
// =========================================================================================================
/**
* Start the progress/animation. Use this method to start the animated view.
*/
public void startProgress() {
if (mTotalProgress >= 0) {
float progressInDegrees = mTotalProgress;
mTotalProgressInDegrees = (int) (progressInDegrees/100 * 360);
invalidate();
}
}
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent pEvent) {
return super.dispatchPopulateAccessibilityEvent(pEvent);
}
// =========================================================================================================
// Getters && Setters!
/**
* @return the dialRadius
*/
public float getDialRadius() {
return mOuterCircleRadius;
}
/**
* @param pDialRadius
* the dialRadius to set
*/
public void setDialRadius(float pDialRadius) {
mOuterCircleRadius = pDialRadius;
}
/**
* @return the textSize
*/
public float getTextSize() {
return mTextSize;
}
/**
* @param pTextSize the textSize to set
*/
public void setTextSize(float pTextSize) {
mTextSize = pTextSize;
}
/**
* @return the textColor
*/
public int getTextColor() {
return mTextColor;
}
/**
* @param pTextColor the textColor to set
*/
public void setTextColor(int pTextColor) {
mTextColor = pTextColor;
}
/**
* @return the borderColor
*/
public int getBorderColor() {
return mBorderColor;
}
/**
* @param pBorderColor the borderColor to set
*/
public void setBorderColor(int pBorderColor) {
mBorderColor = pBorderColor;
}
/**
* @return the innerCircleColor
*/
public int getInnerCircleColor() {
return mInnerCircleColor;
}
/**
* @param pInnerCircleColor the innerCircleColor to set
*/
public void setInnerCircleColor(int pInnerCircleColor) {
mInnerCircleColor = pInnerCircleColor;
}
/**
* @return the titleText
*/
public String getTitleText() {
return mTitleText;
}
/**
* @param pTitleText the titleText to set
*/
public void setTitleText(String pTitleText) {
mTitleText = pTitleText;
}
/**
* @return the outerCircleRadius
*/
public float getOuterCircleRadius() {
return mOuterCircleRadius;
}
/**
* @param pOuterCircleRadius the outerCircleRadius to set
*/
public void setOuterCircleRadius(float pOuterCircleRadius) {
mOuterCircleRadius = pOuterCircleRadius;
}
/**
* @return the borderWidth
*/
public float getBorderWidth() {
return mBorderWidth;
}
/**
* @param pBorderWidth the borderWidth to set
*/
public void setBorderWidth(float pBorderWidth) {
mBorderWidth = pBorderWidth;
}
/**
* @return the totalProgress
*/
public int getTotalProgress() {
return mTotalProgress;
}
/**
* @param pTotalProgress the totalProgress to set
*/
public void setTotalProgress(int pTotalProgress) {
mTotalProgress = pTotalProgress;
}
}
修改
@Override
public boolean onTouchEvent(MotionEvent pEvent) {
double x = pEvent.getX();
double y = pEvent.getY();
double x1 = x - mCenterX;
double y1 = y - mCenterY;
double distance = Math.sqrt(x1*x1 + y1*y1);
RectF topBoundingRect = new RectF(mCenterX - mOuterCircleRadius, mCenterY - mOuterCircleRadius, mCenterX + mOuterCircleRadius, mCenterY);
if (Math.abs(distance - mOuterCircleRadius) <= MAX_TOUCH_TOLERANCE && topBoundingRect.contains((float) x, (float) y)) {
// the user is touching the arc. Which arc is tapped? How do I know that?
}
return true;
}
答案 0 :(得分:3)
我不会为此发布代码,因为我不太习惯使用Java UI,但是你所描述的背后的数学应该不会太难。
为了确保我理解你在做什么:你有一个由某个中心点(x 0 ,y 0 )定义的圆弧,半径为r ,起始角θ 0 ,并且结束角θ 1 。然后,您需要测试点(x,y)并确定用户是否单击了圆圈。
如果我们将所有内容都转换回原点,这个问题就更容易解决,因为三角函数相对于原点总是更容易。那么让我们来吧
x'= x - x 0
y'= y - y 0
现在你有了x'和y',我们可以通过计算来判断它离圆心的距离
dist =√(x' 2 + y' 2 )
如果此值不接近半径r,则点击的点无法靠近弧的任何位置。由于圆弧在数学上是无限小的,因此您可能希望在用户单击圆弧时设置一些“公差”。例如,您可以定义一些常量TOLERANCE
,然后假设用户点击圆周长
| dist - r | ≤容差
现在,假设弧只是圆的边界。如果您正在绘制填充圆弧,则可以只检查是否
dist≤r+ TOLERANCE
这会检查该点是否在圆圈内。
现在,此时您可以检查该点是否在圆圈内/内。接下来的问题是他们是否点击了圆弧的一部分。为此,您可以使用Math.atan2
和计算Math.atan2(y', x')
来计算该点相对于圆心的角度θ。这会让你回到一个角度θ(以弧度为单位)。然后你可以检查θ 0 ≤θ≤θ 1 ,然后它会告诉你他们是否点击了你关心的圆圈部分。
简而言之:
Math.atan2
获取角度θ希望这有帮助!