What i need to do is the bitmap to be drawn at the edge outside the circle.
I'm getting it right sometimes, but it's getting wrong when I rotate the device.
I also tried the rotation Animation, but i didn't get the result I want.
Please help.
That's what's expected:
What I'm getting: for this image it's working fine
it's getting wrong here
and here:
The Code(google tutorial for compass):
CompassFragment Class:
public class CompassFragment extends Fragment implements SensorEventListener {
SensorManager sensorManager;
private Sensor sensorAccelerometer;
private Sensor sensorMagneticField;
private float[] valuesAccelerometer;
private float[] valuesMagneticField;
private float[] matrixR;
private float[] matrixI;
private float[] matrixValues;
TextView readingAzimuth, readingPitch, readingRoll;
Compass myCompass;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_compass, container, false);
readingAzimuth = (TextView) view.findViewById(R.id.azimuth);
readingPitch = (TextView) view.findViewById(R.id.pitch);
readingRoll = (TextView) view.findViewById(R.id.roll);
myCompass = (Compass) view.findViewById(R.id.mycompass);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sensorManager = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE);
sensorAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMagneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
valuesAccelerometer = new float[3];
valuesMagneticField = new float[3];
matrixR = new float[9];
matrixI = new float[9];
matrixValues = new float[3];
}
#Override
public void onResume() {
sensorManager.registerListener(this,
sensorAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(this,
sensorMagneticField,
SensorManager.SENSOR_DELAY_NORMAL);
super.onResume();
}
#Override
public void onPause() {
sensorManager.unregisterListener(this,
sensorAccelerometer);
sensorManager.unregisterListener(this,
sensorMagneticField);
super.onPause();
}
#Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
for (int i = 0; i < 3; i++) {
valuesAccelerometer[i] = event.values[i];
}
break;
case Sensor.TYPE_MAGNETIC_FIELD:
for (int i = 0; i < 3; i++) {
valuesMagneticField[i] = event.values[i];
}
break;
}
boolean success = SensorManager.getRotationMatrix(
matrixR,
matrixI,
valuesAccelerometer,
valuesMagneticField);
if (success) {
SensorManager.getOrientation(matrixR, matrixValues);
double azimuth = Math.toDegrees(matrixValues[0]);
double pitch = Math.toDegrees(matrixValues[1]);
double roll = Math.toDegrees(matrixValues[2]);
readingAzimuth.setText("Azimuth: " + String.valueOf(azimuth));
readingPitch.setText("Pitch: " + String.valueOf(pitch));
readingRoll.setText("Roll: " + String.valueOf(roll));
myCompass.update(matrixValues[0]);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Compass Class:
public class Compass extends View {
private Bitmap bitmap;
private float direction;
private HashMap<String, Bitmap> mStore = new HashMap<String, Bitmap>();
private Context context;
private int h, w, r;
private Paint paint;
private Canvas canvas;
public Compass(Context context) {
super(context);
// TODO Auto-generated constructor stub
this.context = context;
}
public Compass(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public Compass(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-ge
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
w = getMeasuredWidth();
h = getMeasuredHeight();
r = 0;
if (w > h) {
r = h / 2;
} else {
r = w / 2;
}
paint = new Paint();
// Paint.ANTI_ALIAS_FLAG
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
canvas.drawCircle(w / 2, h / 2, r, paint);
paint.setColor(Color.RED);
drawImage1(canvas);
}
private void drawImage1(Canvas canvas) {
BitmapFactory.Options myOptions = new BitmapFactory.Options();
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// important
myOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, myOptions);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
Bitmap workingBitmap = Bitmap.createBitmap(bitmap);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
float left = (float) ((w / 2 + r * Math.sin(-direction)));
float top = (float) (h / 2 - r * Math.cos(-direction));
Log.e("cords", left + " " + top);
canvas.drawBitmap(mutableBitmap, left, top, paint);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/azimuth"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/mainColor" />
<TextView
android:id="#+id/pitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/mainColor" />
<TextView
android:id="#+id/roll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/mainColor" />
</LinearLayout>
<com.example.compass.Compass
android:id="#+id/mycompass"
class="com.nilecode.Compass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
</com.example.compass.Compass>
</LinearLayout>
Related
I know android.graphics is old, but i am having trouble doing a simple stuff.
I want to draw a line animation where one View points an arrow/line into another View
First Button-------------------------------->Second Button
I have tried creating a custom View class and overriding the onDraw(Canvas c) method and then using the drawLine(startX, startY, stopX, stopY, paint) method from the Canvas Object. But i don't know which coordinates to get in order to point one View to the other View
I don't want to create a static View in the XML layout with a slim height because the View can be added dynamically by the user, which i think drawing the line dynamically is the best way.
Please help me out. Thank you!
For drawing lines between views better if all of it lays on same parent layout. For the conditions of the question (Second Button is exactly to the right of First Button) you can use custom layout like that:
public class ArrowLayout extends RelativeLayout {
public static final String PROPERTY_X = "PROPERTY_X";
public static final String PROPERTY_Y = "PROPERTY_Y";
private final static double ARROW_ANGLE = Math.PI / 6;
private final static double ARROW_SIZE = 50;
private Paint mPaint;
private boolean mDrawArrow = false;
private Point mPointFrom = new Point(); // current (during animation) arrow start point
private Point mPointTo = new Point(); // current (during animation) arrow end point
public ArrowLayout(Context context) {
super(context);
init();
}
public ArrowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
}
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
if (mDrawArrow) {
drawArrowLines(mPointFrom, mPointTo, canvas);
}
canvas.restore();
}
private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();
pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
return pointFrom;
}
private Point calcPointTo(Rect fromViewBounds, Rect toViewBounds) {
Point pointTo = new Point();
pointTo.x = toViewBounds.left;
pointTo.y = toViewBounds.top + (toViewBounds.bottom - toViewBounds.top) / 2;
return pointTo;
}
private void drawArrowLines(Point pointFrom, Point pointTo, Canvas canvas) {
canvas.drawLine(pointFrom.x, pointFrom.y, pointTo.x, pointTo.y, mPaint);
double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);
int arrowX, arrowY;
arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle + ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle + ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);
arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle - ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle - ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);
}
public void animateArrows(int duration) {
mDrawArrow = true;
View fromView = getChildAt(0);
View toView = getChildAt(1);
// find from and to views bounds
Rect fromViewBounds = new Rect();
fromView.getDrawingRect(fromViewBounds);
offsetDescendantRectToMyCoords(fromView, fromViewBounds);
Rect toViewBounds = new Rect();
toView.getDrawingRect(toViewBounds);
offsetDescendantRectToMyCoords(toView, toViewBounds);
// calculate arrow sbegin and end points
Point pointFrom = calcPointFrom(fromViewBounds, toViewBounds);
Point pointTo = calcPointTo(fromViewBounds, toViewBounds);
ValueAnimator arrowAnimator = createArrowAnimator(pointFrom, pointTo, duration);
arrowAnimator.start();
}
private ValueAnimator createArrowAnimator(Point pointFrom, Point pointTo, int duration) {
final double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);
mPointFrom.x = pointFrom.x;
mPointFrom.y = pointFrom.y;
int firstX = (int) (pointFrom.x + ARROW_SIZE * Math.cos(angle));
int firstY = (int) (pointFrom.y + ARROW_SIZE * Math.sin(angle));
PropertyValuesHolder propertyX = PropertyValuesHolder.ofInt(PROPERTY_X, firstX, pointTo.x);
PropertyValuesHolder propertyY = PropertyValuesHolder.ofInt(PROPERTY_Y, firstY, pointTo.y);
ValueAnimator animator = new ValueAnimator();
animator.setValues(propertyX, propertyY);
animator.setDuration(duration);
// set other interpolator (if needed) here:
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mPointTo.x = (int) valueAnimator.getAnimatedValue(PROPERTY_X);
mPointTo.y = (int) valueAnimator.getAnimatedValue(PROPERTY_Y);
invalidate();
}
});
return animator;
}
}
with .xml layout like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_main"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<{YOUR_PACKAGE_NAME}.ArrowLayout
android:id="#+id/arrow_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/first_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="First Button"/>
<Button
android:id="#+id/second_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Second Button"/>
</{YOUR_PACKAGE_NAME}.ArrowLayout>
</RelativeLayout>
and MainActivity.java like:
public class MainActivity extends AppCompatActivity {
private ArrowLayout mArrowLayout;
private Button mFirstButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mArrowLayout = (ArrowLayout) findViewById(R.id.arrow_layout);
mFirstButton = (Button) findViewById(R.id.first_button);
mFirstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mArrowLayout.animateArrows(1000);
}
});
}
}
you got something like that (on First Button click):
For other cases ( Second Button is exactly to the left (or above, or below) or more complex above-right/below-left etc. of First Button) you should modify part for calculating arrow begin and end points:
private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();
// Second Button above
// ----------+----------
// | |
// Second Button tho the left + First Button + Second Button tho the right
// | |
// ----------+----------
// Second Button below
//
// + - is arrow start point position
if (toViewBounds to the right of fromViewBounds){
pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if (toViewBounds to the left of fromViewBounds) {
pointFrom.x = fromViewBounds.left;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if () {
...
}
return pointFrom;
}
Use Path and Pathmeasure for Drawing Animated Line. I have Made and test it.
Make Custom View and pass view coordinates points array to it,
public class AnimatedLine extends View {
private final Paint mPaint;
public Canvas mCanvas;
AnimationListener animationListener;
Path path;
private static long animSpeedInMs = 2000;
private static final long animMsBetweenStrokes = 100;
private long animLastUpdate;
private boolean animRunning = true;
private int animCurrentCountour;
private float animCurrentPos;
private Path animPath;
private PathMeasure animPathMeasure;
float pathLength;
float distance = 0;
float[] pos;
float[] tan;
Matrix matrix;
Bitmap bm;
public AnimatedLine(Context context) {
this(context, null);
mCanvas = new Canvas();
}
public AnimatedLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(15);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setColor(context.getResources().getColor(R.color.materialcolorpicker__red));
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
setLayerType(LAYER_TYPE_SOFTWARE, mPaint);
}
bm = BitmapFactory.decodeResource(getResources(), R.drawable.hand1);
bm = Bitmap.createScaledBitmap(bm, 20,20, false);
distance = 0;
pos = new float[2];
tan = new float[2];
matrix = new Matrix();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mCanvas = canvas;
if (path != null) {
if (animRunning) {
drawAnimation(mCanvas);
} else {
drawStatic(mCanvas);
}
}
}
/**
* draw Path With Animation
*
* #param time in milliseconds
*/
public void drawWithAnimation(ArrayList<PointF> points, long time,AnimationListener animationListener) {
animRunning = true;
animPathMeasure = null;
animSpeedInMs = time;
setPath(points);
setAnimationListener(animationListener);
invalidate();
}
public void setPath(ArrayList<PointF> points) {
if (points.size() < 2) {
throw new IllegalStateException("Pass atleast two points.");
}
path = new Path();
path.moveTo(points.get(0).x, points.get(0).y);
path.lineTo(points.get(1).x, points.get(1).y);
}
private void drawAnimation(Canvas canvas) {
if (animPathMeasure == null) {
// Start of animation. Set it up.
animationListener.onAnimationStarted();
animPathMeasure = new PathMeasure(path, false);
animPathMeasure.nextContour();
animPath = new Path();
animLastUpdate = System.currentTimeMillis();
animCurrentCountour = 0;
animCurrentPos = 0.0f;
pathLength = animPathMeasure.getLength();
} else {
// Get time since last frame
long now = System.currentTimeMillis();
long timeSinceLast = now - animLastUpdate;
if (animCurrentPos == 0.0f) {
timeSinceLast -= animMsBetweenStrokes;
}
if (timeSinceLast > 0) {
// Get next segment of path
float newPos = (float) (timeSinceLast) / (animSpeedInMs / pathLength) + animCurrentPos;
boolean moveTo = (animCurrentPos == 0.0f);
animPathMeasure.getSegment(animCurrentPos, newPos, animPath, moveTo);
animCurrentPos = newPos;
animLastUpdate = now;
//start draw bitmap along path
animPathMeasure.getPosTan(newPos, pos, tan);
matrix.reset();
matrix.postTranslate(pos[0], pos[1]);
canvas.drawBitmap(bm, matrix, null);
//end drawing bitmap
//take current position
animationListener.onAnimationUpdate(pos);
// If this stroke is done, move on to next
if (newPos > pathLength) {
animCurrentPos = 0.0f;
animCurrentCountour++;
boolean more = animPathMeasure.nextContour();
// Check if finished
if (!more) {
animationListener.onAnimationEnd();
animRunning = false;
}
}
}
// Draw path
canvas.drawPath(animPath, mPaint);
}
invalidate();
}
private void drawStatic(Canvas canvas) {
canvas.drawPath(path, mPaint);
canvas.drawBitmap(bm, matrix, null);
}
public void setAnimationListener(AnimationListener animationListener) {
this.animationListener = animationListener;
}
public interface AnimationListener {
void onAnimationStarted();
void onAnimationEnd();
void onAnimationUpdate(float[] pos);
}
}
I have a problem with my drawing app, in that the mask filters are using a lot of paramaters and lisiting it in to the array list. I think everytime I draw the maskfilter gets added to the arraylist and then the canvas draws the whole array list from scratch. Or it is the problem with onDraw please give me suggestions for performance improvements.
public class PaintingActivity extends AppCompatActivity {
private PaintView paintView;
private Toolbar mTopToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_paint);
mTopToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(mTopToolbar);
paintView = (PaintView) findViewById(R.id.paintView);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
paintView.init(metrics);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.draw_menu, menu);
return super.onCreateOptionsMenu(menu);
}
private void colorPicker() {
final Context context = PaintingActivity.this;
ColorPickerDialogBuilder
.with(context)
.setTitle("Choose color")
.initialColor(DEFAULT_COLOR)
.wheelType(ColorPickerView.WHEEL_TYPE.CIRCLE)
.density(12)
.setOnColorSelectedListener(new OnColorSelectedListener() {
#Override
public void onColorSelected(int selectedColor) {
Toast.makeText(context, "onColorSelected: 0x" + Integer.toHexString(selectedColor), Toast.LENGTH_SHORT).show();
}
})
.setPositiveButton("ok", new ColorPickerClickListener() {
#Override
public void onClick(DialogInterface dialog, int selectedColor, Integer[] allColors) {
//changeBackgroundColor(selectedColor);
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.build()
.show();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.normal:
paintView.normal();
return true;
case R.id.emboss:
paintView.emboss();
return true;
case R.id.blur:
paintView.blur();
return true;
case R.id.color_picker:
colorPicker();
return true;
case R.id.clear:
paintView.clear();
return true;
}
return super.onOptionsItemSelected(item);
}
}
Then my PaintView
public class PaintView extends View {
public static int BRUSH_SIZE = 20;
public static final int DEFAULT_COLOR = Color.RED;
public static final int DEFAULT_BG_COLOR = Color.WHITE;
private static final float TOUCH_TOLERANCE = 4;
private float mX, mY;
private Path mPath;
private Paint mPaint;
private ArrayList<FingerPath> paths = new ArrayList<>();
private int currentColor;
private int backgroundColor = DEFAULT_BG_COLOR;
private int strokeWidth;
private boolean emboss;
private boolean blur;
private MaskFilter mEmboss;
private MaskFilter mBlur;
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
public PaintView(Context context) {
this(context, null);
}
public PaintView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(DEFAULT_COLOR);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setXfermode(null);
mPaint.setAlpha(0xff);
mEmboss = new EmbossMaskFilter(new float[] {1, 1, 1}, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(5, BlurMaskFilter.Blur.NORMAL);
}
public void init(DisplayMetrics metrics) {
int height = metrics.heightPixels;
int width = metrics.widthPixels;
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
currentColor = DEFAULT_COLOR;
strokeWidth = BRUSH_SIZE;
}
public void normal() {
emboss = false;
blur = false;
}
public void emboss() {
emboss = true;
blur = false;
}
public void blur() {
emboss = false;
blur = true;
}
public void clear() {
backgroundColor = DEFAULT_BG_COLOR;
paths.clear();
normal();
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
mCanvas.drawColor(backgroundColor);
for (FingerPath fp : paths) {
mPaint.setColor(fp.color);
mPaint.setStrokeWidth(fp.strokeWidth);
mPaint.setMaskFilter(null);
if (fp.emboss)
mPaint.setMaskFilter(mEmboss);
else if (fp.blur)
mPaint.setMaskFilter(mBlur);
mCanvas.drawPath(fp.path, mPaint);
}
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.restore();
}
private void touchStart(float x, float y) {
mPath = new Path();
FingerPath fp = new FingerPath(currentColor, emboss, blur, strokeWidth, mPath);
paths.add(fp);
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touchUp() {
mPath.lineTo(mX, mY);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN :
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE :
touchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP :
touchUp();
invalidate();
break;
}
return true;
}
}
Also my layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lolow.asserty.asserty.paint.PaintingActivity"
android:background="#color/lightGrey">
<android.support.v7.widget.Toolbar
android:id="#+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.lolow.asserty.asserty.paint.PaintView
android:id="#+id/paintView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:layout_marginBottom="100dp"
android:background="#drawable/paint_view_border"/>
</RelativeLayout>
</RelativeLayout>
This is also my path class:
public class FingerPath {
public int color;
public boolean emboss;
public boolean blur;
public int strokeWidth;
public Path path;
public FingerPath(int color, boolean emboss, boolean blur, int strokeWidth, Path path) {
this.color = color;
this.emboss = emboss;
this.blur = blur;
this.strokeWidth = strokeWidth;
this.path = path;
}
}
I want to design a Scratch Card-like effect in android. Is It possible to do this with a Framelayout or RelativeLayout by stacking the coupon at the back of the bitmap and setting the colour of CustomCanvas to gray or something. I've tried doing this but I wasn't able to get the stacked image from the back. The background was just turning white and nothing else. I then tried to the COLOR.TRANSPARENT and there was no Scratch at all. How can I do this?
Below is my CustomCanvas Class extending the View class:
public class CanvasView extends View {
public int width,height;
private Bitmap bitmap;
private Canvas canvas;
Context context;
private Path mPath;
private Paint paint;
private float mX, mY;
private static final float TOLERANCE = 5;
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(100f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap,0,0,paint);
canvas.drawPath(mPath, paint);
}
private void startTouch(float x, float y){
mPath.moveTo(x,y);
mX = x;
mY = y;
}
private void moveTouch(float x, float y){
float dx = Math.abs(x-mX);
float dy = Math.abs(y-mY);
if(dx >= TOLERANCE || dy >= TOLERANCE){
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
public void clearCanvas(){
mPath.reset();
invalidate();
}
private void upTouch(){
mPath.lineTo(mX,mY);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
startTouch(x,y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
float r = calculatingPercentage(bitmap.getWidth(),bitmap.getHeight());
if(calculatingPercentage(bitmap.getWidth(),bitmap.getHeight()) >= 50.00){
Toast.makeText(getContext(),"Done 50%",Toast.LENGTH_SHORT).show();
}
invalidate();
break;
}
return true;
}
//FUNCTION I WAS USING TO CALCULATE THE SCRATCHED AREA'S PERCENTAGE
private float calculatingPercentage(int width,int height){
int[] xArray = new int[100];
int[] yArray = new int[100];
float percentTransparent;
Random r = new Random();
for(int i = 0; i<100;i++){
xArray[i] = r.nextInt(width - 10) +10;
}
for(int i = 0; i<100;i++){
yArray[i] = r.nextInt(height - 10) +10;
}
int pixelCount = 0;
for(int i = 0; i<100;i++){
int x = xArray[i];
int y = yArray[i];
int color = Color.WHITE;
int black = Color.BLACK;
if(bitmap.getPixel(xArray[i],yArray[i]) == Color.WHITE){
pixelCount++;
}
}
percentTransparent = (pixelCount/100);
return percentTransparent;
}
}
ActivityB which is calling the Canvasview class:
public class ActivityB extends Activity {
private CanvasView customCanvas;
private Button bt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_a);
customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
bt = (Button) findViewById(R.id.buttonAgain);
bt.setVisibility(View.VISIBLE);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
clearCanvas();
}
});
}
public void clearCanvas(){
customCanvas.clearCanvas();
}
}
And here is my fragment_a.xml layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/firstFrame"
android:background="#F07818"
tools:context="com.example.dremer.fragmentspractice.FragmentA">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="40dp"
android:src="#drawable/benz"/>
<com.example.dremer.fragmentspractice.CanvasView
android:id="#+id/signature_canvas"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#FFFFFF"
android:background="#FFFFFF"/>
<Button
android:id="#+id/buttonAgain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scratch Again"
android:layout_gravity="bottom|center"
android:paddingBottom="20dp"/>
</FrameLayout>
Can you guys please help me with this?
Thanks!
I want to set my custom View size to be *4 bigger than the screen so I can use accelerometer to move some object around that View which is in the background.
Background.java
package com.theeye.game;
private Paint backgroundSquarePaint = new Paint();
private Paint eraser = new Paint();
private Paint paintBorder = new Paint();
private Rect backgroundSquare = new Rect();
private Rect square = new Rect();
private int scale, width, height, maxWidth, maxHeight, min, randomX, randomY, randomAngle;
private DisplayMetrics metrics = new DisplayMetrics();
private Random rW = new Random();
private Random rH = new Random();
private Random rA = new Random();
private View view = findViewById(R.id.background);
public Backgroind(Context context) {
super(context);
init(null, 0);
}
public Backgroind(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public Backgroind(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attrs, int defStyleAttyr) {
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
scale = metrics.densityDpi / 160;
height = metrics.heightPixels;
width = metrics.widthPixels;
maxWidth = width - 200 * scale;
maxHeight = height - 200 * scale;
min = 200 * scale;
randomX = rW.nextInt((maxWidth - min) + 1) + min;
randomY = rH.nextInt((maxHeight - min) + 1) + min;
randomAngle = rA.nextInt((359 - 0) + 1) + 0;
paintBorder.setAntiAlias(true);
paintBorder.setColor(Color.LTGRAY);
paintBorder.setStyle(Paint.Style.STROKE);
paintBorder.setStrokeWidth(5f);
paintBorder.setStrokeJoin(Paint.Join.ROUND);
paintBorder.setStrokeCap(Paint.Cap.ROUND);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
backgroundSquare.set(0, 0, width * 4, height * 4);
backgroundSquarePaint.setColor(Color.parseColor("#162BA1"));
square.set(0 - 110, 0 - 110, 0 + 110, 0 + 110);
Animation scale = new ScaleAnimation(0.5f, 1f, 0.5f, 1f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(10000);
scale.setFillAfter(true);
view.startAnimation(scale);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(backgroundSquare, backgroundSquarePaint);
canvas.translate(randomX, randomY);
canvas.rotate(randomAngle);
canvas.drawRect(square, eraser);
canvas.drawRect(square, paintBorder);
canvas.restore();
}
}
MainActivity
package com.theeye.game
private DisplayMetrics metrics = new DisplayMetrics();
private int height, width;
private SensorManager senSensorManager;
private Sensor senAccelerometer;
private long lastUpdate = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindowManager().getDefaultDisplay().getMetrics(metrics);
height = metrics.heightPixels;
width = metrics.widthPixels;
senSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
senAccelerometer = senSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
senSensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
super.onPause();
senSensorManager.unregisterListener(this);
}
#Override
protected void onResume() {
super.onResume();
senSensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor mySensor = sensorEvent.sensor;
if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = sensorEvent.values[0];
float y = sensorEvent.values[1];
float z = sensorEvent.values[2];
long curTime = System.currentTimeMillis();
if ((curTime - lastUpdate) > 100) {
lastUpdate = curTime;
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<com.theeye.game.Backgroind
android:id="#+id/background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.theeye.game.Shapes
android:id="#+id/shapes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
I have developed an application which draws lines on the screen .I want to use different colors for the lines but all the lines are appearing in same color.
I have a color picker in my code to change the colors and i want to draw lines of different colors each time by selecting colors, but as of now since i am constructing lines and rendering it.When i choose initially red and draw a line, and then later select blue and draw a line , now the older line is also getting changed to blue instead of red, However i want the red to remain as red, and blue as such can any one help?
My code is given below
MyDemo.java:
public class MyDemo extends Activity implements
ColorPickerDialog.OnColorChangedListener {
private LinearLayout root;
private Button btnReset;
private Button btnPickColor;
public static int selectedColor = Color.BLACK;
MyImageView view;
private static final String COLOR_PREFERENCE_KEY = "color";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_demo);
root = (LinearLayout) findViewById(R.id.root);
btnReset = (Button) findViewById(R.id.reset);
btnPickColor = (Button) findViewById(R.id.pickColor);
final MyImageView view = new MyImageView(this);
view.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT));
root.addView(view);
btnReset.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
view.reset();
}
});
btnPickColor.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// color picker class call
int color = PreferenceManager.getDefaultSharedPreferences(
MyDemo.this).getInt(COLOR_PREFERENCE_KEY, Color.WHITE);
new ColorPickerDialog(MyDemo.this, MyDemo.this, color).show();
}
});
}
public void colorChanged(int color) {
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putInt(COLOR_PREFERENCE_KEY, color).commit();
selectedColor = color;
}
}
MyImageView.java:
public class MyImageView extends ImageView implements View.OnTouchListener {
private int id = -1;
private Path path;
private List<Path> paths = new ArrayList<Path>();
private List<PointF> points = new ArrayList<PointF>();
boolean multiTouch = false;
MyDemo demo;
public MyImageView(Context context) {
super(context);
this.setOnTouchListener(this);
Log.d("Constructor", " Control in constructor");
}
public void reset() {
paths.clear();
points.clear();
path = null;
id = -1;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int colorPicked = MyDemo.selectedColor;
if (colorPicked != Color.BLACK) {
Paint paint = createPen(colorPicked);
paint.setStyle(Paint.Style.STROKE);
for (Path path : paths) {
canvas.drawPath(path, paint);
}
for (int i = 0; i < points.size(); i++) {
PointF p = points.get(i);
canvas.drawText("" + p.x, p.y, i, createPen(colorPicked));
}
} else {
Paint paint = createPen(colorPicked);
paint.setStyle(Paint.Style.STROKE);
for (Path path : paths) {
canvas.drawPath(path, paint);
}}
for (int i = 0; i < points.size(); i++) {
PointF p = points.get(i);
canvas.drawText("" + p.x, p.y, i, createPen(colorPicked));
}
}
private PointF copy(PointF p) {
PointF copy = new PointF();
copy.set(p);
return copy;
}
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
multiTouch = false;
id = event.getPointerId(0);
PointF p = getPoint(event, id);
path = new Path();
path.moveTo(p.x, p.y);
paths.add(path);
points.add(copy(p));
break;
case MotionEvent.ACTION_POINTER_DOWN:
multiTouch = true;
for (int i = 0; i < event.getPointerCount(); i++) {
int tId = event.getPointerId(i);
if (tId != id) {
points.add(getPoint(event, i));
}
}
break;
case MotionEvent.ACTION_MOVE:
if (!multiTouch) {
p = getPoint(event, id);
path.lineTo(p.x, p.y);
}
break;
}
invalidate();
return true;
}
private PointF getPoint(MotionEvent event, int i) {
int index = 0;
return new PointF(event.getX(index), event.getY(index));
}
public Paint createPen(int color) {
Paint pen = new Paint();
pen.setColor(color);
float width = 3;
pen.setStrokeWidth(width);
return pen;
}
}
ColorPickerDialog.java:
public class ColorPickerDialog extends Dialog {
public interface OnColorChangedListener {
void colorChanged(int color);
}
private final OnColorChangedListener mListener;
private final int mInitialColor;
private static class ColorPickerView extends View {
private final Paint mPaint;
private final Paint mCenterPaint;
private final int[] mColors;
private final OnColorChangedListener mListener;
ColorPickerView(Context c, OnColorChangedListener l, int color) {
super(c);
mListener = l;
mColors = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF,
0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 };
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(s);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(32);
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setColor(color);
mCenterPaint.setStrokeWidth(5);
}
private boolean mTrackingCenter;
private boolean mHighlightCenter;
#Override
protected void onDraw(Canvas canvas) {
float r = CENTER_X - mPaint.getStrokeWidth() * 0.5f;
canvas.translate(CENTER_X, CENTER_X);
canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
if (mTrackingCenter) {
int c = mCenterPaint.getColor();
mCenterPaint.setStyle(Paint.Style.STROKE);
if (mHighlightCenter) {
mCenterPaint.setAlpha(0xFF);
} else {
mCenterPaint.setAlpha(0x80);
}
canvas.drawCircle(0, 0, CENTER_RADIUS
+ mCenterPaint.getStrokeWidth(), mCenterPaint);
mCenterPaint.setStyle(Paint.Style.FILL);
mCenterPaint.setColor(c);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(CENTER_X * 2, CENTER_Y * 2);
}
private static final int CENTER_X = 100;
private static final int CENTER_Y = 100;
private static final int CENTER_RADIUS = 32;
private int ave(int s, int d, float p) {
return s + java.lang.Math.round(p * (d - s));
}
private int interpColor(int colors[], float unit) {
if (unit <= 0)
return colors[0];
if (unit >= 1)
return colors[colors.length - 1];
float p = unit * (colors.length - 1);
int i = (int) p;
p -= i;
// now p is just the fractional part [0...1) and i is the index
int c0 = colors[i];
int c1 = colors[i + 1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
return Color.argb(a, r, g, b);
}
private static final float PI = 3.1415926f;
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - CENTER_X;
float y = event.getY() - CENTER_Y;
boolean inCenter = java.lang.Math.sqrt(x * x + y * y) <= CENTER_RADIUS;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTrackingCenter = inCenter;
if (inCenter) {
mHighlightCenter = true;
invalidate();
break;
}
case MotionEvent.ACTION_MOVE:
if (mTrackingCenter) {
if (mHighlightCenter != inCenter) {
mHighlightCenter = inCenter;
invalidate();
}
} else {
float angle = (float) java.lang.Math.atan2(y, x);
// need to turn angle [-PI ... PI] into unit [0....1]
float unit = angle / (2 * PI);
if (unit < 0) {
unit += 1;
}
mCenterPaint.setColor(interpColor(mColors, unit));
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mTrackingCenter) {
if (inCenter) {
mListener.colorChanged(mCenterPaint.getColor());
}
mTrackingCenter = false; // so we draw w/o halo
invalidate();
}
break;
}
return true;
}
}
public ColorPickerDialog(Context context, OnColorChangedListener listener,
int initialColor) {
super(context);
mListener = listener;
mInitialColor = initialColor;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnColorChangedListener l = new OnColorChangedListener() {
public void colorChanged(int color) {
mListener.colorChanged(color);
dismiss();
}
};
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(LinearLayout.VERTICAL);
layout.setGravity(Gravity.CENTER);
layout.setPadding(10, 10, 10, 10);
layout.addView(new ColorPickerView(getContext(), l, mInitialColor),
new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
setContentView(layout);
setTitle("Pick a Color");
}
}
I'm not sure but it seams a problem is in your onDraw method
Paint paint = createPen(colorPicked);
paint.setStyle(Paint.Style.STROKE);
for (Path path : paths) {
canvas.drawPath(path, paint);
}
You select a color first (it's the last color you picked) and then with a same color you draw each path.
I think that the solution for this could be to create array of colors which will contain color for every path you create and to pick corresponding color before you draw each path
try this, it will help you
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
int colorPicked = MainActivity.selectedColor;
paint1 = createPen(colorPicked);
for (Path p : paths){
canvas.drawPath(p, paint1);
paint1.setColor(colorPicked);
}
}
private Paint createPen(int color) {
// TODO Auto-generated method stub
paint1 = new Paint();
paint1.setAntiAlias(true);
paint1.setDither(true);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeJoin(Paint.Join.ROUND);
paint1.setStrokeCap(Paint.Cap.ROUND);
paint1.setStrokeWidth(3);
return paint1;
}