I need your experience.
Problem : I need to be able to draw thing (rect, circ,etc) on one part of a FlipperView....
My main.xml has a main linearLayout. In this LinearLayout I have a ViewFlipper with 2 linearlayouts in it. The first linearlayout has soms buttons, inputfiels,etc... the second one should have a special view in wich I can draw the things I choose in the first part.
So I have created a new view wich extends the View class so I can play with the ondraw methode. But I can not get it to work.
This is what I have so far...
MAIN.XML
<LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="#+id/layout_main" xmlns:android="http://schemas.android.com/apk/res/android">
<ViewFlipper android:id="#+id/details" android:layout_width="fill_parent" android:layout_height="fill_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:orientation="vertical" android:layout_height="match_parent">
//BUTTONS TEXTFIELDS ETC
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:orientation="vertical" android:layout_height="match_parent">
//an instance of my new ViewClass
<Vierbergen.Tim.ViewClass
android:id="#+id/draw" android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</ViewFlipper>
</LinearLayout>
The VIEWCLASS.java
public class ViewClass extends View {
Paint paint = new Paint();
public DrawView(Context context) {
super(context);
paint.setColor(Color.WHITE);
}
#Override
public void onDraw(Canvas canvas) {
//depending on some params....
draw this, draw that...
}
}
and then my main activity
DRAWER.JAVA
public class SmsDraw extends Activity implements OnTouchListener{
ViewClass vClass;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
vClass = (ViewClass) findViewById(R.id.draw);
}
// with somewhere a draw function excecuted by a button
private void start() {
//where can I get a canvas ? Canvas c = new Canvas();
blablalba
vClass.onDraw(c);
}
So I need to be able to draw on the thing VIEWCLASS with id = draw in my main.xml...
How can I do this ? please help me with an explanation and solution and not just a solution :-)
Thanks VeeTee
Your onDraw method will be called by the framework if your View is attached to the view hierarchy. You don't need to call it yourself.
If you're unsure about your onDraw code, try using the code from a sample like DrawPoints in API Demos.
You don't call onDraw yourself, as Matthew has said. Here's a very simple addition to your ViewClass that would allow you to draw rectangles or circles. It hasn't been tested, so proceed carefully.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class DrawView extends View {
private boolean drawRect;
private boolean drawCircle;
private float rect_w;
private float rect_h;
private int rect_color;
private float circle_radius;
private int circle_color;
Paint paint;
public DrawView(Context context) {
super(context);
paint = new Paint();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
}
public void drawRect(float w, float h, int color) {
// define these variables privately at the top of the class
this.drawRect = true;
this.drawCircle = false;
this.rect_w = w;
this.rect_h = h;
this.rect_color = color;
}
public void drawCircle(float radius, int color) {
// define these variables privately at the top of the class
this.drawRect = false;
this.drawCircle = true;
this.circle_radius = radius;
this.circle_color = color;
}
#Override
public void onDraw(Canvas canvas) {
if (drawRect) {
paint.setColor(this.rect_color);
canvas.drawRect(0, 0, rect_w, rect_h, paint);
}
if (drawCircle) {
paint.setColor(this.circle_color);
canvas.drawCircle(0, 0, circle_radius, paint);
}
}
}
Then in your view Class, call it like this:
vClass = (DrawView) findViewById(R.id.draw);
vClass.drawRect(3,4,0x334434);
Seems like the problem it wasn't working the first time was...
when the main activity want to setContentView(...)... it crashes....
But when I leave it out of the xml
and create it at runtime like this
viewClass = new ViewClass(this);
layke = (LinearLayout) findViewById(R.id.layoutDraw);//wich is the second part of the flipperview
layke.addView(viewClass); // to at it to the right layout
it works....
Related
I've been trying to draw circles as player positions on a map, but I want to use XML for layout of other things around the layout such as buttons, having the map as a background. However I can't seem to have both things working together. They work perfectly fine separated and content is drawn fine if I use the custom view class as the setContentView. But, if I use the customview in the xml, the onDraw() function only goes through once on start and cannot be invalidate()'d.
This is the layout XML:
<android.support.constraint.ConstraintLayout 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=".TrackMap">
<com.b143lul.android.logreg.CircleView android:id="#+id/CircleView"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="#drawable/resized_track_2"
></com.b143lul.android.logreg.CircleView>
And my code in the custom view is:
public class CircleView extends View {
SharedPreferences sharedPreferences;
JSONObject groupScores;
Paint paint1;
String localUsername = "";
public CircleView(Context context) {
super(context);
init(context);
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public void init(Context context) {
paint1 = new Paint();
paint1.setColor(Color.BLUE);
this.setWillNotDraw(false);
}
protected void onDraw(Canvas canvas) {
// Bunch of circle drawing.
}
public void update(JSONObject allGroupScores, String username) {
groupScores = allGroupScores;
localUsername = username;
invalidate();
// Invalidate being used to refresh the draw function
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
As for how I'm implementing the customview, I start in onCreate():
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
circleView = new CircleView(this);
//setContentView(circleView);
setContentView(R.layout.activity_track_map);
And then the update function of the customview class is called after the StringRequest that gets the scores every 5 seconds (which works all perfectly well):
try {
groupScores = new JSONObject(response);
circleView.update(groupScores, username);
So, I'm just wondering how I can use both at once, so that the drawing goes over the XML. Sadly I can't seem to find anything on it online and I'm sorry if it's something really dumb I'm not doing! Thanks in advance! :)
Just to mention: The update() function is being reached but invalidate() is not making the onDraw() refresh. This is run if that view is used as setContentView() though.
Turns out,
circleView = new CircleView(this);
loads the class outside of the XML, which was part of the problem. However, when I tried to load the view by findViewById() I got a NullPointerException. So, the setContentView({Your layout XML}) needs to be called before trying to get the class by view. AKA solution is:
setContentView(R.layout.activity_track_map);
circleView = (CircleView)findViewById(R.id.CircleView);
I am trying to draw a single line in Android using canvas
My class :
public class LineDrawer extends View {
public LineDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
float left = 20;
float top = 20;
float right = 50;
float bottom = 100;
canvas.drawLine(left, top, right, bottom, paint);
}
}
My Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LineDrawer lineDrawer = new LineDrawer(this);
setContentView(R.layout.activity_Main);
}
}
I cannot find where is the problem , I try all the solutions in the internet but nothing happen , still a blank activity..
Should I import some code ?
lineDrawer is created but not added anywhere. Just creating a view is not enough, you need to add it to the current displayed views to be taken into account and drawn. You have two options:
Add it to your XML layout. You will have to add the following constructor to your custom view.
public LineDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
Use addView(). Anyway, given how simple is your example, I'll use first (common) method.
As an additional comment, the Paint paint object should be created on view initialization, as is a costly operation. See in the original documentation for more information about this.
I would like to add TextView and EditView into my custom LinearLayout programmatically.
But I don't know how.
Something like this (that doesn't work):
<com.custom.FavoritesViewer
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="#+id/favoritesViewer">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"/>
</com.custom.FavoritesViewer>
and my custom layout
public class FavoritesViewer extends LinearLayout {
private Bitmap fullImage;
private int canvasWidth;
private int canvasHeight;
private final Paint paint = new Paint();
public FavoritesViewer(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
initializeCanvasSize(context);
}
private void initializeCanvasSize(Context context) {
final Pair<Integer, Integer> screenSize = Utils.getScreenSize(context);
canvasWidth = screenSize.first;
canvasHeight = screenSize.second;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(canvasWidth, canvasHeight / 3);
}
#Override
protected void onDraw(Canvas cvs) {
if (fullImage == null) {
fullImage = Bitmap.createBitmap(canvasWidth, canvasHeight / 3, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(fullImage);
paint.reset();
paint.setColor(Color.parseColor("#AA000000"));
canvas.drawRect(0, 0, canvasWidth, canvasHeight / 3, paint);
}
cvs.drawBitmap(fullImage, 0, 0, null);
}
}
So I have a canvas (like background) and I would like to add some standart Views on top. I cannot add it on onDraw.
Is it any way to add View into custom Layout?
EDITTED
I need to implement some special UI with buttons. I want to wrap this in one component. I draw that UI on canvas and somehow should add buttons (It's enough for me to add simple ImageButton, not to draw an image and emulate button's behaviour). That's why I selected Layout as container and need to add Views programmatically.
As long as you call through to super (probably super.onDraw()), I imagine the parent class will draw the views you add as expected. It looks like you're just overriding onDraw, which would prevent the parent LinearLayout class from rendering it's content (like the TextView).
Try commenting out your onDraw method first, and see if the LinearLayout behaves as expected.
Also, what's the goal of the Custom Layout? There may be a better way to achieve your goal.
What I am trying to achieve is to make an arc shaped seekbar. I know there are plenty of libraries I could use to achieve this, but I am just trying my hands on custom made views.
I have encountered couple of problems:
I have a class which extends SeekBar, and I have implemented onDraw and onMeasure methods as well, but I am not able to view that in layout editor in eclipse, here is the code for the custom view class:
package com.custom.android.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;
public class CustomSeekBar extends SeekBar {
public CustomSeekBar(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomSeekBar(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Here is my layout 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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.custom.android.views.CustomSeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar"/>
</RelativeLayout>
If I use canvas class to draw an arc or any shape, would that be a good starting point?
What exactly is wrong with the eclipse adt and how could I use the onDraw method to give shape to that seekbar?
Drawing a ProgressBar with any shape, is pretty easy. With the SeekBar you have some complexity, since you have to achieve 3 diferent things:
Draw the line
Draw the draggable thumb, if you want.
Handle the user interaction
You have to think of it as an arc that is draw inside a rectangle. So point 3 could be easy: just let the user move the finger in a horizontal line, or exactly over the arc, but considering only the x coordinate of the touch event. What does this mean, in short? ok, good news: you dont have to do anything, since thats the normal behavior of the base SeekBar.
For the second point, you can choose an image for the handler, and write it in the corresponding position with a little maths. Or you can forget the handler for know, and just draw the seek bar as a line representing the full track, and another line over it representing the progress. When you have this working, if you want you can add the handler.
And for the first point, this is the main one, but its not hard to achieve. You can use this code:
UPDATE: I made some improvements in the code
public class ArcSeekBar extends SeekBar {
public ArcSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ArcSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private Paint mBasePaint;
private Paint mProgressPaint;
private RectF mOval = new RectF(5, 5, 550, 550);
private int defaultmax = 180;
private int startAngle=180;
private int strokeWidth=10;
private int trackColor=0xFF000000;
private int progressColor=0xFFFF0000;
public void setOval(RectF mOval) {
this.mOval = mOval;
}
public void setStartAngle(int startAngle) {
this.startAngle = startAngle;
}
public void setStrokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
}
public void setTrackColor(int trackColor) {
this.trackColor = trackColor;
}
public void setProgressColor(int progressColor) {
this.progressColor = progressColor;
}
public ArcSeekBar(Context context) {
super(context);
mBasePaint = new Paint();
mBasePaint.setAntiAlias(true);
mBasePaint.setColor(trackColor);
mBasePaint.setStrokeWidth(strokeWidth);
mBasePaint.setStyle(Paint.Style.STROKE);
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setColor(progressColor);
mProgressPaint.setStrokeWidth(strokeWidth);
mProgressPaint.setStyle(Paint.Style.STROKE);
setMax(defaultmax);// degrees
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawArc(mOval, startAngle, getMax(), false, mBasePaint);
canvas.drawArc(mOval, startAngle, getProgress(), false, mProgressPaint);
invalidate();
//Log.i("ARC", getProgress()+"/"+getMax());
}
}
Of course, you can and you should make everything configurable, be means of the contructor, or with some setters for the start and end angles, dimensions of the containing rectangle, stroke widths, colors, etc.
Also, note that the arc is drawn from 0 to getProgress, being this number an angle relative to the x axis, growing clocwise, so, if it go from 0 to 90 degrees, it will be something like:
Of course you can change this: canvas.drawArc get any number as an angle, and it is NOT treated as module 360, but you can do the maths and have it starting and ending in any point you want.
In my example the beggining is in the 9 of a clock, and it takes 180 degrees, to the 3 in the clock.
UPDATE
I uploaded a running example to github
I could not find tips or examples about how to do this. I want to add a progressbar over a Rect that I have already drawn, so how I can I do this?
Your answers will be truly appreciated! :)
Edited
public class MainView extends View {
public MainView(Context context) {
super(context);
}
public MainView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
logo.set(getPX(5), getPX(10), getPX(65), getPX(70));
background.set(getPX(30), getPX(2), canvas.getWidth() - getPX(10),
getPX(81));
canvas.drawRect(background, paint);
canvas.drawRect(logo, paint);
}
final private int getPX(float dp) {
return (int) (getResources().getDisplayMetrics().density * dp);
}
}
Based on your implementation (as you shown it to me):
<ScrollView .. >
<LinearLayout .. >
<MainView .. />
<MainView .. />
<MainView .. />
</LinearLayout>
</ScrollView>
my proposed solution would be - extend your CustomView class with LinearLayout (because then you can add additional views to your custom view):
public class MainView extends LinearLayout {
private Rect logo;
private Rect background;
public MainView(Context context) {
super(context);
setWillNotDraw(false); //needed in order to call onDraw method
logo = new Rect();
background = new Rect();
}
public MainView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
logo = new Rect();
background = new Rect();
RelativeLayout progressBarLayout = new RelativeLayout(context);
RelativeLayout.LayoutParams lay = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
ProgressBar progressBar = new ProgressBar(context, null, R.attr.progressBarStyleHorizontal);
progressBar.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
progressBar.setIndeterminate(true); //remove that (only for demonstration purposes)
lay.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
progressBarLayout.addView(progressBar, lay);
addView(progressBarLayout);
//Apart from the ProgressBar you are able to add as many views as you want to your custom view and align them as you would like
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
logo.set(getPX(5), getPX(10), getPX(65), getPX(70));
background.set(getPX(30), getPX(2), canvas.getWidth() - getPX(10),
getPX(81));
canvas.drawRect(background, paint);
canvas.drawRect(logo, paint);
}
final private int getPX(float dp) {
return (int) (getResources().getDisplayMetrics().density * dp);
}
}
In this example I am only showing how to add ProgressBar component, because that was your original question
You will need to add some more code in order to carry out your requirements (got from questioner):
P.S. This is just a solution to your particular problem/request. I would advise to use ListView component, which is better in the sense that it reuses views, so in such cases where you will have LOTS OF custom view instances, your application might become unusable, because there will be too much load on the activity class.
In order for you to migrate to using ListView component, try some examples first, like this