I'm using RelativeLayout to absolutely position some standard views (like TextView).
What I'd like to do is to draw a custom line on this RelativeLayout's Canvas using Canvas.drawLine that is drawn behind all its other subviews.
These other subviews are added with explicitely defining RelativeLayout.LayoutParams, but I'd like to leave the decision of where to paint itself to my custom line.
I tried wrapping this line in a CustomView with overloaded View.onDraw(Canvas canvas) method and simply adding the view without specifying any LayoutParams, so:
public class CustomView extends View {
public CustomView(Context context, int x0, int y0, int x1, int y1) {
super(context);
setClickable(false);
setBackgroundColor(Color.TRANSPARENT);
}
public void onDraw(Canvas canvas) {
Log.i("myapp", "i'm not called! :(")
Paint p = new Paint();
p.setColor(Color.BLACK);
canvas.drawLine(x0, y0, x1, y1, p);
}
}
And usage:
CustomView v = new CustomView(MyActivity.this, 0, 0, 100, 100);
relativeLayout.addView(v);
... but this onDraw method is never called.
Is there a way to make this work?
Edit: works if I substitute:
relativeLayout.addView(v)
with
relativeLayout.addView(v,
new RelativeLayout.LayoutParams(SOME_WIDTH, SOME_HEIGHT));
The point is, I know neither SOME_WIDTH, nor SOME-HEIGHT at that point.
try this custom RelativeLayout:
class RL extends RelativeLayout {
private Paint mPaint;
public RL(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(5);
mPaint.setColor(0xffffffff);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int cnt = getChildCount();
for (int i = 0; i < cnt; i++) {
View child = getChildAt(i);
int l = child.getLeft();
int t = child.getTop();
int r = child.getRight();
int b = child.getBottom();
if (i % 2 == 0) {
canvas.drawLine(l, t, r, b, mPaint);
} else {
canvas.drawLine(l, b, r, t, mPaint);
}
}
super.dispatchDraw(canvas);
}
}
and test it ba adding the following in onCreate() method:
RelativeLayout rl = new RL(this);
TextView tv;
List<String> list = Arrays.asList("one", " two ", "three", " four ", "fife");
int i = 0;
for (String string : list) {
int id = 1000 + i;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (i != 0) {
params.addRule(RL.BELOW, id - 1);
}
tv = new TextView(this);
tv.setTextSize(48);
tv.setTextColor(0xffff0000);
tv.setText(string);
rl.addView(tv, params);
tv.setId(id);
i++;
}
setContentView(rl);
So.
I ended up creating a CustomController which has some methods to calculate position/size and using this controller when creating RelativeLayout.LayoutParams for each CustomView(context, controller).
I guess you cannot have a subview in a RelativeLayout without specifying its RelativeLayout.LayoutParams.
Easiest way is to call the super.draw(Canvas) method after you finished your background in the onDraw() method.
That will cause it to draw the children last.
Related
I have written a custom view to display images dynamically inside a circle.
I have overwritten Viewgroup's dispatchDraw method to draw circle. After this the child ImageViews are not displaying on screen, if I do no override the method, then they are displaying on screen.
Here is my class:
public class CustomView extends RelativeLayout {
private Paint paint;
private View mView;
private Context context;
private void init(Context context) {
LinearLayout layout = new LinearLayout(context);
layout.setGravity(Gravity.CENTER);
layout.setOrientation(LinearLayout.VERTICAL);
// Set generic layout parameters
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
Button button = new Button(context);
button.setText("Button!");
layout.addView(button, params); // Modify this
ImageView imageView = new ImageView(context);
imageView.setImageResource(R.drawable.coffe_selected);
layout.addView(imageView);
this.addView(layout);
}
public CustomView(Context mContext) {
super(mContext);
context = mContext;
// create the Paint and set its color
paint = new Paint();
paint.setColor(0xFF1f5b83);
init(context);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int width = this.getWidth();
int height = this.getHeight();
canvas.drawCircle(width / 2, height / 2-64, 200, paint);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Take a look at the source code of ViewGroup and what is happening in dispatchDraw.
Just one line out of it:
more |= drawChild(canvas, transientChild, drawingTime);
as you can see, the childs are drawn there.
So if you don't call the super method of dispatchDraw, it is possible that the childs are not drawn.
Simply call:
super.dispatchDraw(canvas);
I´m trying to display a moving cloud over a blue sky. The blue background is displayed but the cloud doesn't appear. I've tried the different approaches in other questions here but nothing works. My code is:
public class CloudBackground extends View {
Bitmap cloud;
int x = 0;
int y = 0;
Paint paint = new Paint();
Rect rectangle = new Rect(0,0,100,100);
public CloudBackground(Context context,AttributeSet attrs) {
super(context,attrs);
cloud = BitmapFactory.decodeResource(getResources(),R.drawable.cloud1);
}
#Override
protected void onDraw (Canvas canvas){
super.onDraw(canvas);
Rect back = new Rect();
back.set(0,0,canvas.getWidth(), canvas.getHeight());
Paint pBlue = new Paint();
pBlue.setStyle(Paint.Style.FILL);
pBlue.setColor(Color.CYAN);
canvas.drawRect(back, pBlue);
drawCloud(x,y,canvas);
if (x < canvas.getWidth())
x = x + 10;
else {
y = y + 10;
x = 0;
}
invalidate();
}
private void drawCloud(int x2, int y2, Canvas canvas) {
canvas.drawBitmap(cloud, x2, y2,paint);
}
Ok. Try this:
public CloudBackground(Context context,AttributeSet attrs) {
super(context,attrs);
cloud = BitmapFactory.decodeResource(context.getApplicationContext().getResources(),R.drawable.cloud1);
}
The issue was finally solved using a.jpg instead a .png. As simple as that and with no reason.
I'm trying to draw a circumference that would represent the battery life in a activity.
My parent layout is a relative layout.
This is the inner class that draws the view:
public class DrawView extends View {
Paint mPaint = new Paint();
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG |
Paint.DITHER_FLAG |
Paint.ANTI_ALIAS_FLAG);
mPaint.setDither(true);
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
int size = 200;
int radius = 190;
int delta = size - radius;
int arcSize = (size - (delta / 2)) * 2;
int percent = 42;
//Thin circle
canvas.drawCircle(size, size, radius, mPaint);
//Arc
mPaint.setColor(getResources().getColor(R.color.eCarBlue));
mPaint.setStrokeWidth(15);
RectF box = new RectF(delta,delta,arcSize,arcSize);
float sweep = 360 * percent * 0.01f;
canvas.drawArc(box, 0, sweep, false, mPaint);
}
}
And in onCreate() I start the view this way:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
ViewGroup myLayout = (ViewGroup) findViewById(R.id.mainlayout);
drawing = new DrawView(this);
myLayout.addView(drawing);
}
But I need to locate this view in the layout, especifically in the center of it. To achieve this, I have modified onCreate()'s code this way:
ViewGroup myLayout = (ViewGroup) findViewById(R.id.mainlayout);
drawing = new DrawView(this);
RelativeLayout.LayoutParams layoutParams= new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
drawing.setLayoutParams(layoutParams);
myLayout.addView(drawing);
But it isn't having effect on the view. What would be then the correct way to define the params for the view?
You're adding DrawView with LayoutParams.WRAP_CONTENT, which means the system needs to ask the view its width and height. You should implement onMeasure() for that. But I've never done that so don't know the details.
Another way is to just use LayoutParams.MATCH_PARENT and draw your stuff in onDraw() centered yourself. In this case you will need to know the width and height of the canvas in order to calculate the coordinates of your draw calls. Calling getWidth() in onDraw() does not give you the expected results. You need to override onSizeChanged() and record the new width and height, like this:
private int canvasWidth;
private int canvasHeight;
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasWidth = w;
canvasHeight = h;
}
Then in onDraw() you can use canvasWidth and canvasHeight because the onSizeChanged() has already happened.
I've defined a custom drawable shown below:
private class SquareDrawableView extends View {
private ShapeDrawable mDrawable;
public SquareDrawableView(int x, int y, int size, int color, Context context) {
super(context);
mDrawable = new ShapeDrawable(new RectShape());
mDrawable.getPaint().setColor(color);
mDrawable.setBounds(x, y, x + size, y + size);
}
public void setColor(int color) {
mDrawable.getPaint().setColor(color);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}
I'm trying to tile a number of these across a LinearLayout. Presently, when I run this code, I just get one square appearing in the "first" position of the LinearLayout. What am I doing wrong here?
top = (LinearLayout)findViewById(R.id.top);
SquareDrawableView[] topSquares = new SquareDrawableView[horizCount];
for(int i = 0; i < horizCount; ++i) {
topSquares[i] = new SquareDrawableView(i*SQUARE_SIZE,0,SQUARE_SIZE,BLUE,this);
top.addView(topSquares[i]);
}
I want to add canvas to relative layout. How, I do this ?
public class Board extends View{
Canvas c;
public Board(Context context) {
super(context);
// TODO Auto-generated constructor stub
character = BitmapFactory.decodeResource(getResources(), R.drawable.female_char_2);
bg = BitmapFactory.decodeResource(getResources(), R.drawable.ice_texture);
//ARENA LAYOUT is Relative Layout
arenaLayout = (RelativeLayout) findViewById(R.id.arena_layout);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.setBitmap(bg.copy(Bitmap.Config.ARGB_8888, true));
//set Background
Rect area = new Rect();
area.set(0,0,canvas.getWidth(), canvas.getHeight());
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
canvas.drawRect(area, paint);
//menggambar
menggambar(x,y,canvas);
if(x < canvas.getWidth()) {
x = x + 10;
} else {
y = y + 10;
x = 0;
}
}
private void menggambar(int x2, int y2, Canvas canvas) {
// TODO Auto-generated method stub
canvas.drawBitmap(character, x2, y2, paint);
arenaLayout.addView(getApplicationContext());
}
}
I want to add canvas to relative layout which possess name arenaLayout. How to add canvas to 'arenaLayout' relative layout.
If you confuse read my source code. You can give simple code to add canvas to relative layout
may be you want something in this way...
public class MainActivity extends Activity
{
...........
..........
onCreate(Bundle savedInstances)
{
........
super.oncreate(savedInstances);
arenaLayout = (RelativeLayout) findViewById(R.id.arena_layout);
Board board = new Board(this);
setContentView(board) ;
..........
}
}
or you can add the view to relativeLayout here.