Drawing a Path with multiple colors - java

I am drawing on android.graphics.Canvas using android.graphics.Path. I want to allow the user to draw using a range of colors and stroke widths, however, whenever the color or stroke width is changed, everything is re-drawn using the new color or stroke width. How can I fix this?
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/btnW10"
android:layout_margin="3sp"
android:layout_weight="1"
android:text="Width10"/>
<Button
android:id="#+id/btnW40"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3sp"
android:layout_weight="1"
android:text="Width40"/>
<Button
android:id="#+id/btnW70"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3sp"
android:layout_weight="1"
android:text="Width70"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"></LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3sp"
android:layout_weight="1"
android:id="#+id/btnBlue"
android:text="Blue"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3sp"
android:layout_weight="1"
android:id="#+id/btnRed"
android:text="Red"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="3sp"
android:layout_weight="1"
android:id="#+id/btnGreen"
android:text="Green"/>
</LinearLayout>
<com.vladislav.canvaswc.DrawLine
android:id="#+id/drwLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
main.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
DrawLine dr;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnB=(Button) findViewById(R.id.btnBlue);
Button btnG=(Button) findViewById(R.id.btnGreen);
Button btnR=(Button) findViewById(R.id.btnRed);
Button btnW10=(Button) findViewById(R.id.btnW10);
Button btnW40=(Button) findViewById(R.id.btnW40);
Button btnW70=(Button) findViewById(R.id.btnW70);
dr =(DrawLine) findViewById(R.id.drwLine);
btnB.setOnClickListener(this);
btnG.setOnClickListener(this);
btnR.setOnClickListener(this);
btnW10.setOnClickListener(this);
btnW40.setOnClickListener(this);
btnW70.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.btnBlue : dr.changeColor(Color.BLUE);break;
case R.id.btnGreen : dr.changeColor(Color.GREEN);break;
case R.id.btnRed : dr.changeColor(Color.RED);break;
case R.id.btnW10 : dr.changeWidth(10f);break;
case R.id.btnW40 : dr.changeWidth(40f);break;
case R.id.btnW70 : dr.changeWidth(70f);break;
}
}
}
drawline.java ChangeWidthColor its my interface with 2 methods changeColor()
and changeWidth()
public class DrawLine extends View implements ChangeWidthColor {
private Paint paint = new Paint();
private Path path = new Path();
public float x;
public float y;
public DrawLine(Context context) {
super(context);
init(context);
}
public DrawLine(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DrawLine(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public void init(Context context) {
//paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.MITER);
//paint.setStrokeWidth(5f);
}
#Override
public void changeColor(int color) {
paint.setColor(color);
}
#Override
public void changeWidth(float width) {
paint.setStrokeWidth(width);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x= event.getX();
y= event.getY();
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
x= event.getX();
y= event.getY();
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
invalidate();
break;
}
invalidate();
return true;
}
}

This solution maintains a List of Path objects and a List of Integers for storing colors. These Lists are in parallel - 1 entry in each list for a Path and color pair. onDraw iterates these Lists, drawing each Path with the corresponding color.
Each time the user clicks to change the color, a new Path is created. ACTION_DOWN and ACTION_MOVE call moveTo(x,y) and lineTo(x,y) on the path, and ACTION_UP causes the Path and current color to be added to the lists.
Note: I have not implemented a solution for stroke width, however, you should be able to follow the example and add this yourself.
public class DrawLine extends View {
private Path path = new Path();
private Paint paint = new Paint();
private int currentColor = Color.BLACK;
private List<Path> paths = new ArrayList<Path>();
private List<Integer> colors = new ArrayList<Integer>();
private float x;
private float y;
public DrawLine(Context context) {
super(context);
init(context);
}
public DrawLine(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DrawLine(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public void init(Context context) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setColor(currentColor);
}
public void changeColor(int color) {
currentColor = color;
path = new Path();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int x = 0; x < paths.size(); x++) {
paint.setColor(colors.get(x));
canvas.drawPath(paths.get(x), paint);
}
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
paths.add(path);
colors.add(currentColor);
invalidate();
break;
}
invalidate();
return true;
}
}
`

public class DrawLine extends View implements ChangeWidthColor {
private Path path = new Path();
private Paint paint = new Paint();
private int currentColor = Color.BLACK;
private float width = 1f;
public float x;
public float y;
private LinkedList<Integer> color = new LinkedList<Integer>();
private LinkedList<Float> widthSize = new LinkedList<Float>();
private LinkedList<Path> paths = new LinkedList<Path>();
public DrawLine(Context context) {
super(context);
init(context);
}
public DrawLine(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DrawLine(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public void init(Context context) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setColor(currentColor);
paint.setStrokeWidth(5f);
}
#Override
public void changeColor(int color) {
currentColor = color;
path = new Path();
}
#Override
public void changeWidth(float width) {
this.width = width;
path = new Path();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < paths.size(); i++) {
paint.setStrokeWidth(widthSize.get(i));
paint.setColor(color.get(i));
canvas.drawPath(paths.get(i), paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
path.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
color.add(currentColor);
paths.add(path);
widthSize.add(width);
break;
default:
return false;
}
invalidate();
return true;
}
}

you need to call invalidate() inside your custom view whenever you are modifying something related to UI. so updated methods will be:
#Override
public void changeColor(int color) {
paint.setColor(color);
invalidate();
}
#Override
public void changeWidth(float width) {
paint.setStrokeWidth(width);
invalidate();
}

Related

How to convert a canvas drawing to an image then Drag it around screen?

I'm trying to draw on a canvas then onClick change it to bitmap image then I would like to make it draggable. I can draw on canvas successfully
but how do I convert this drawing to an image then make it draggable? I don't mind If it's possible to drag the drawing without converting to an image. Can someone please help.
public class MainActivity extends AppCompatActivity implements ColorPickerDialog.OnColorChangedListener {
protected DrawView canvasView;
private ImageButton blackCircle;
private Button pens,select;
private Paint mPaint;
Bitmap viewCapture = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPaint = new Paint();
canvasView = (DrawView) findViewById(R.id.canvas_view);//The drawing mechanism
colorChanged(mPaint.getColor());
blackCircle = (ImageButton) findViewById(R.id.black_circle);
blackCircle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
canvasView.setPathColor(Color.BLACK));
blackCircle.setPressed(true);
}
});
select = (Button) findViewById(R.id.ic_select);
select.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
canvasView.setVisibility(View.VISIBLE);
canvasView.setPathColor(0);
//Drag the drawn image
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout) findViewById(R.id.main);
drawingView = new DrawingView(MainActivity.this);
relativeLayout.addView(drawingView);
//Create a bitmap image of current drawing
canvasView.setDrawingCacheEnabled(true);
viewCapture = Bitmap.createBitmap(canvasView.getDrawingCache());
canvasView.setDrawingCacheEnabled(false);
}
});
}// End of Create();
private void clearCanvas(View v) {
canvasView.clear();
}
#Override
public void colorChanged(int color) {
mPaint.setColor(color);
canvasView.setPathColor(mPaint.getColor());
}
/************** OnTouch ***************/
class DrawingView extends View{
float x,y;
public DrawingView(Context context){
super(context);
viewCapture = BitmapFactory.decodeResource(context.getResources(), R.id.main);
}
public boolean onTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
x =(int)event.getX();
y =(int)event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
x =(int)event.getX();
y =(int)event.getY();
invalidate();
break;
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.CYAN);
canvas.drawBitmap(viewCapture, x, y, paint);
}
}
}//End of Class
public class DrawView extends View {
private Paint drawPaint, canvasPaint;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private SparseArray<Path> paths;
public DrawView(Context context) {
super(context);
setupDrawing();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupDrawing();
}
private void setupDrawing() {
paths = new SparseArray<>();
drawPaint = new Paint();
drawPaint.setColor(Color.BLACK);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(9);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
for (int i=0; i<paths.size(); i++) {
canvas.drawPath(paths.valueAt(i), drawPaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int id = event.getPointerId(index);
Path path;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
path = new Path();
path.moveTo(event.getX(index), event.getY(index));
paths.put(id, path);
break;
case MotionEvent.ACTION_MOVE:
for (int i=0; i<event.getPointerCount(); i++) {
id = event.getPointerId(i);
path = paths.get(id);
if (path != null) path.lineTo(event.getX(i), event.getY(i));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
path = paths.get(id);
if (path != null) {
drawCanvas.drawPath(path, drawPaint);
paths.remove(id);
}
break;
default:
return false;
}
invalidate();
return true;
}
public void setPathColor(int color) {
drawPaint.setColor(color);
}
public void clear() {
canvasBitmap.eraseColor(Color.TRANSPARENT);
paths.clear();
invalidate();
System.gc();
}
}
For the next steps I assume you'd like to let the users drag the picture which they just created with the DrawView. So I'd introduce another View which can display the bitmap and set its position and dimensions exactly like those of the DrawView. (The best way to achieve this depends on the type of ViewGroup you are using)
Create a bitmap from the canvas as shown in converting a canvas into bitmap image in android
Set the bitmap as the new View's background with setBackground(Drawable), toggle the visibility of both Views as needed.
To make the new View draggable, use a View.OnTouchListener and change the position according to the MotionEvents
Another option: just drag the DrawView by using a flag to indicate whether the users can drag it or draw on it. You override onTouchEvent() already, now check the flag first and if it is set to "drag" then evaluate the delta between two MOVEs and adjust the position accordingly.

How to add delay in onDraw() in android canvas?

I am making a project. It draws concentric circles in android canvas. When the user drags the screen, all the circles move accordingly. Here is my code so far.
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
tools:context=".MainActivity"
android:background="#android:color/white">
<RelativeLayout
android:id="#+id/scrollableSpace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true">
<project.myProject.DrawOrbit
android:id="#+id/orbitsRegion"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />
</RelativeLayout>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnTouchListener
{
PointF center;
center.x=500;center.y=500;
float radius[]={100,200,300,400,500};
DrawOrbit orbit;
int startX=0,startY=0,currentX=0,currentY=0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout layout=(RelativeLayout)findViewById(R.id.scrollableSpace);
orbit.draw(center,radius);
layout.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent motionEvent)
{
final int action= motionEvent.getAction();
switch(action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
startX=(int)motionEvent.getRawX();
startY=(int)motionEvent.getRawY();
break;
}
case MotionEvent.ACTION_MOVE:
{
currentX=(int)motionEvent.getRawX();
currentY=(int)motionEvent.getRawY();
float diffX=currentX-startX;
float diffY=currentY-startY;
startX=currentX;
startY=currentY;
center.x+=diffX;
center.y+=diffY;
orbit.draw(center,radius);
break;
}
}
return true;
}
}
DrawOrbit.java
public class DrawOrbit extends View
{
PointF center;
float radius[];
public DrawOrbit(Context context) {
super(context);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
int len=radius.length;
for(int a=0;a<len;a++)
{
canvas.drawCircle(center.x,center.y,radius[a],paint);
}
}
public void draw(PointF center, float radius[])
{
this.center=center;
this.radius=radius;
invalidate();
requestLayout();
}
}
What I want to do is that the circles should appear one by one. First the inner most circle then the next one after some delay then the next and so on. The same effect should be seen when the screen is dragged. How can I achieve this? Any help will be appreciated.
What you're looking for is a timeout. I'd suggest creating a new thread to draw everything, and start by drawing the first circle, have the method look like this:
public void drawCircle() {
//Draw logic
TimeUnit.SECONDS.sleep(3);
drawNextCircle();
}
//Assuming on java 1.8+
Thread thread = new Thread() => {
drawCircle();
}
What this will do is have the thread sleep for 3 seconds, and then continue normal operation after the time period is up. You can change it to other measurements of time such as TimeUnit.MILLISECONDS or TimeUnit.MINUTES as well.
edit: You likely won't want this in your main thread, as it will stop the ENTIRE application from working for however long you put the thread on timeout, so it's almost essential for this to work that you put it in a separate thread.
edit 2: It would make more sense to add a separate util method to timeout and then call another method via reflection than the above code, but the same code would be needed.
Figured out the answer to my own question. I had to use a Handler, multiplied the delay with the for-loop variable and made 1 then 2 then so on.. circles to get the desired effect. Here is the code.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnTouchListener
{
PointF center;
center.x=500;center.y=500;
float radius[]={100,200,300,400,500};
DrawOrbit orbit;
int startX=0,startY=0,currentX=0,currentY=0;
Handler handler1 = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout layout=(RelativeLayout)findViewById(R.id.scrollableSpace);
for (int a = 0; a<radius.length ;a++)
{
final int index=a;
handler1.postDelayed(new Runnable() {
#Override
public void run() {
orbit.draw(center,radius,index);
}
}, 300 * a);
}
layout.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent motionEvent)
{
final int action= motionEvent.getAction();
switch(action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
startX=(int)motionEvent.getRawX();
startY=(int)motionEvent.getRawY();
break;
}
case MotionEvent.ACTION_MOVE:
{
currentX=(int)motionEvent.getRawX();
currentY=(int)motionEvent.getRawY();
float diffX=currentX-startX;
float diffY=currentY-startY;
startX=currentX;
startY=currentY;
center.x+=diffX;
center.y+=diffY;
break;
}
case MotionEvent.ACTION_UP:
{
for (int a = 0; a<radius.length ;a++)
{
final int index=a;
handler1.postDelayed(new Runnable() {
#Override
public void run() {
orbit.draw(center,radius,index);
}
}, 300 * a);
}
break;
}
}
return true;
}
}
DrawOrbit.java
public class DrawOrbit extends View
{
PointF center;
float radius[];
int index;
public DrawOrbit(Context context) {
super(context);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public DrawOrbit(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
int len=radius.length;
for(int a=0;a<index;a++)
{
canvas.drawCircle(center.x,center.y,radius[a],paint);
}
}
public void draw(PointF center, float radius[],int index)
{
this.center=center;
this.radius=radius;
this.index=index;
invalidate();
requestLayout();
}
}

Scrolling and clicklistener on views

I have an Adapter that extends RecyclerView.Adapter. Each line of adapter is a custom FrameLayout with GestureDetector implemented. The FrameLayout xml file has 2 RelativeLayout.
In this FrameLayout I redirect X and Y positions to the RelativeLayout that is in the foreground.
In each RelativeLayout I have a setOnClickListener. This is because if user clicks the RelativeLayout that is on the foreground, an activity is started. If the user scrolls the former, the latter is available to click.
What I'm facing is:
Implementing setOnClickListener on these layouts I lost view scroll feature of my custom FrameLayout. And I need the scroll feature and clickListener for each view.
This is my xml
file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:visibility="invisible"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
...
</RelativeLayout>
</FrameLayout>
My custom FrameLayout:
public class SwipeView extends FrameLayout implements GestureDetector.OnGestureListener{
private RelativeLayout container;
private RelativeLayout backgroundContainer;
private GestureDetector detector;
private float posX;
private float posY;
private float lastTouchX;
private float lastTouchY;
private float xLimit;
private int mActivePointerId;
public SwipeView(#NonNull Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.adapter_work_order, this);
initViews(context, null);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
private void initViews(Context context, AttributeSet attrs){
this.container = this.findViewById(R.id.container);
this.backgroundContainer = this.findViewById(R.id.background_container);
this.checkboxDownload = this.findViewById(R.id.checkbox_download);
this.textviewWo = this.findViewById(R.id.textview_wo);
this.downloadedWo = this.findViewById(R.id.textview_downloaded_wo);
this.textviewTime = this.findViewById(R.id.textview_time);
this.textviewField1 = this.findViewById(R.id.textview_field_1);
this.textviewField2 = this.findViewById(R.id.textview_field_2);
this.textviewField3 = this.findViewById(R.id.textview_field_3);
this.deleteLabel = this.backgroundContainer.findViewById(R.id.delete_wo);
}
#Override
public boolean onTouchEvent(MotionEvent event){
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Remember where we started (for dragging)
lastTouchX = x;
lastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(event, 0);
if(x > container.getRight() + container.getX()){
Log.e("ERROR", "TOUCHED OUTSIDE");
}
else {
Log.e("ERROR", "TOUCHED INSIDE: ");
}
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Calculate the distance moved
final float dx = x - lastTouchX;
final float dy = y - lastTouchY;
posX += dx;
posY += dy;
if(posX <= -xLimit){
posX -= dx;
}
if(posX > 0.0f){
posX = 0.0f;
}
invalidate();
// Remember this touch position for the next move event
lastTouchX = x;
lastTouchY = y;
container.setX(posX);
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
}
return true;
}
#Override
public boolean onDown(MotionEvent motionEvent) {
return true;
}
#Override
public void onShowPress(MotionEvent motionEvent) {
}
#Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return true;
}
#Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return true;
}
#Override
public void onLongPress(MotionEvent motionEvent) {
}
#Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float velocityX, float velocityY) {
return true;
}
}
And this code is inside my adapter:
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//onItemClickListener.onItemClick(workOrder);
Log.e("ERROR", "FOREGROUND CLICKED");
}
});
holder.backgroundContainer.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
Log.e("ERROR", "BACKGROUND CLICKED");
}
});
Finally this is the code that is inside my Activity:
((MyAdapter) recyclerview.getAdapter())
.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
#Override
public void onItemClick(Item item) {
if(!presenter.getDownloadAvailability()) {
...
}
}
});
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:fillViewport="false">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
</ScrollView>

Custom Circular TextView - Text Not Centered in Circle

I put together a TextView class utilizing some different suggestions I've seen and wrote this class to display a TextView inside of a circle. The circle comes out great, but the text appears slightly above the center of the circle.
I can't figure out what's causing this. Here's my code:
CircularTextView
public class CircularTextView extends AppCompatTextView {
private ShapeDrawable backgroundDrawable;
private OvalShape ovalShape;
private int backgroundColor;
public CircularTextView(Context context) {
super(context);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
public CircularTextView(Context context, AttributeSet attrs) {
super(context, attrs);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
public CircularTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
//Source https://stackoverflow.com/questions/25203501/android-creating-a-circular-textview/34685568#34685568
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int h = this.getMeasuredHeight();
int w = this.getMeasuredWidth();
int r = Math.max(w, h);
setMeasuredDimension(r, r);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
backgroundDrawable.setShape(ovalShape);
backgroundDrawable.getPaint().setColor(backgroundColor);
setBackground(backgroundDrawable);
}
private void allocateShapes(){
backgroundDrawable = new ShapeDrawable();
ovalShape = new OvalShape();
}
public void setBackgroundColor(int color){
backgroundColor = color;
invalidate();
}
}
TestCircleTextViewActivity
public final class TestCircleTextViewActivity extends BaseActivity {
#BindView(R.id.circle_text)
CircularTextView circleText;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_circular_textview);
ButterKnife.bind(this);
int circleColor = ContextCompat.getColor(this, R.color.color_circle_test_solid);
circleText.setBackgroundColor(circleColor);
}
}
activity_test_circular_textview.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.thinkbubble.app.ui.view.CircularTextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/circle_text"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:padding="4dp"
android:text="Keyword">
</com.thinkbubble.app.ui.view.CircularTextView>
</RelativeLayout>
Use android:gravity="center" for you TextView to make text center in circle

how to draw a Bitmap and move it in java Android?

I have spent a good month trying to do all of this together.
Draw Bitmap to screen
On create move Bitmap down at a constant rate
Stop the Bimap when it reaches the bottom
Allow the Bitmap to be clicked on while it moves (a.k.a. not translation animation)
Thanks for you for helping put this all together in advance.
Here is my attempt:
package com.example.animating;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class FirstView extends View {
private int screenW;
private int screenH;
private float drawScaleW;
private float drawScaleH;
int moveRate = 10, dot1y, dot1x;
private Context myContext;
private Bitmap dot;
private boolean dotSinking = true, gameOver = false;
public FirstView(Context context) {
super(context);
myContext = context;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
dot = BitmapFactory.decodeResource(myContext.getResources(), R.drawable.dot);
screenW = w;
screenH = h;
drawScaleW = (float) screenW / 800;
drawScaleH = (float) screenH / 600;
dot1y = (int) (475*drawScaleH);
dot1x = (int) (55*drawScaleW);
}
public void run() {
if (!gameOver) {
animateDot();
}
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
Paint redPaint = new Paint();
redPaint.setColor(Color.RED);
canvas.drawBitmap(dot, dot1x, dot1y, null);
}
private void animateDot(){
if (dotSinking) {
dot1y -= moveRate;
}
}
}
To answer first part of your question
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<LinearLayout
android:id="#+id/textView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/button1"
/>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Button" />
</RelativeLayout>
MainActivity class
public class MainActivity extends Activity
{
FirstView dv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new FirstView(this);
setContentView(R.layout.activity_main);
final Button b= (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
// TODO Auto-generated method stub
LinearLayout rl= (LinearLayout ) findViewById(R.id.textView1);
rl.addView(dv);
b.setVisibility(View.INVISIBLE);
}
});
}
public class FirstView extends View {
private int screenW;
private int screenH;
private float drawScaleW;
private float drawScaleH;
int moveRate = 10, dot1y, dot1x;
private Context myContext;
private Bitmap dot;
private boolean dotSinking = true, gameOver = false;
public FirstView(Context context) {
super(context);
myContext = context;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
dot = BitmapFactory.decodeResource(myContext.getResources(), R.drawable.ic_launcher);
screenW = w;
screenH = h;
drawScaleW = (float) screenW / 800;
drawScaleH = (float) screenH / 600;
dot1y = 0+dot.getHeight();
dot1x = w/2-(dot.getWidth()/2);
}
public void run() {
if (!gameOver) {
animateDot();
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
Paint redPaint = new Paint();
redPaint.setColor(Color.RED);
canvas.drawBitmap(dot, dot1x, dot1y, null);
run();
}
private void animateDot(){
if (dotSinking) {
if(dot1y<screenH-dot.getHeight())
dot1y += moveRate;
}
}
}
I have a button at the bottom of the screen. When clicked (button becomes invisible) the image moves from the top to the height of the layout. When it reaches the layout height it stops.
I don't know how to add a click listener for the image moving.
Answered first part of the question. Modify the above according to your needs.
you should use the invalidate() method inside your onDraw() to make it redraw your bitmap's new position. and to make it stop when i reaches the bottom use the setbounds method for your bitmap inside an if sentence telling the bitmap that when the distance with the screec is 0 make it stop. you should set an onclicklistener to your bitmap.to.make it.clickable . hope this helps you!

Categories