I want to make a custom View so I extended the View class and override the onDraw(Canvas canvas) method.
The problems is, I found out that the method is never stopped being called.
well it seems that calling View.invalidate on a different View causes this view to redraw to.
I can't post the code in here so I try to describe only the relevant parts.
in the activity I create a FrameLayout m_mainLayout which is the one I finally pass to setContentView() method.
I add different Views and Layouts to m_mainLayout, one of them is GameView m_gameView which extends View and a GameFrameView which extends RelativeLayout and to this layout I add the View in question.
now, I constantly call (every ~100 ms) m_gameView.invalidate().
how does it cause other views to be redrawn ?
what do I need to do to stop this?
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); <-- add this
int width = canvas.getWidth();
int height = canvas.getHeight();
Log.i("AttackDialogView", "onDraw ");
}
Related
I'm trying to implement a custom view that is created by some data - specifically I am trying to create a revenue projection graph via a custom view.
The way i'm doing this is by having 10 data points, and drawing lines between each data point. (and then also drawing the X and Y axis lines).
I was thinking about creating a view that encapsulates the 10 coordinates, and draw them via the canvas in onDraw(). something like this:
class RevView extends View {
private List<Point> mPoints;
public void configurePoints(float[] revenues) {
// convert revenues to points on the graph.
}
// ... constructor... etc.
protected void onDraw(Canvas canvas) {
// use canvas.drawLine to draw line between the points
}
}
but in order to do so, i think i need the width/height of the view, which isn't rendered until onDraw.
is this the right approach? or am i even supposed to pass in a Points list to the view? Or what's a better way to build this?
You should override the onMeasure() method which will be called before onDraw().
https://developer.android.com/reference/android/view/View.html#onMeasure(int,%20int)
Measure the view and its content to determine the measured width and the measured height.
You should use onMeasure() method which is called before onDraw() method, so you'll be able to get your view dimensions like this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
}
You should avoid getting View dimensions directly in onDraw() method because it will be called many times a second
New Android programmer here.
I'm trying to display an .png image as a bitmap on Android. The only way I have been able to display the converted image at all is with a custom class that extends View. However, my image is too large to be displayed entirely on the screen and I would like to be able to scroll with it. But when I define a ScrollView and put the Canvas with the Bitmap into it, I get a blank screen. I had no luck setting this up with the layout file, so it is all done in the Activity class.
This is where the Activity is created:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ScrollView scroll = new ScrollView(this);
scroll.addView(new CustomView(this));
setContentView(scroll);
}
And this is my CustomView class:
private class CustomView extends View{
public CustomView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas){
Bitmap bitmapMap = BitmapFactory.decodeResource(getResources(),R.drawable.resourceimage);
canvas.drawBitmap(bitmapMap,0,0,null);
this.setWillNotDraw(false);
}
}
And if I replace this line of code in my Activity: setContentView(scroll)
with this line: setContentView(new CustomView(this)), I can see the image, albeit not the entire image. So, is there a way to set this up in the layout files instead? Or is there something I'm missing that I need to declare in the ScrollView class?
EDIT: I would prefer not to use ImageView, because I would like to change the image in specific locations, and using bitmap seemed to be the easiest way to accomplish that, via x and y coordinates.
Your custom view needs to override the onMeasure method and properly set the measured width and height so that the parent views (in this case the ScrollView) can know how much space to allocate for the child.
I'm interested in using a custom View to draw, measure, and display a set of buttons that is dependent on a back-end for my application. This requires me to implement this in Android dynamically. Would you help me get started?
Here we go: first in my MainActivity I instantiate my custom class which inherits from TableLayout which is also a view:
var keyboardView = new KeyboardView(this, layout, droidLayout, this.Colors);
Then I set the content view to the fresh instance of my custom class: SetContentView(keyboardView); Here's my class's constructor which just helps me get scope on all of the info I need:
public KeyboardView(Context context, KeyboardLayout layout, int droidLayout, Dictionary<string, int> colors)
: base(context) {
this._Colors = colors;
this._Context = context;
this.KeyboardLayout = layout;
this.SetWillNotDraw(false);
//this.ButtonLayout = ll;
this.DrawingCacheEnabled = true;
this.DroidLayout = droidLayout;
I've also overridden both OnMeasure and OnDraw:
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int h = 100;
int w = 100;
//Overall keyboard/View dimensions //Difference between Canvas size and KeyboardDimensions?
this.SetMeasuredDimension(w, h);
//this.Layout.CanvasSize.
}
protected override void OnDraw(Canvas canvas) {
var bt = new Button(this._Context);
bt.Text = "laaaa";
this.AddView(bt);
}
Now what's happening is that OnDraw and OnMeasure both get it, in fact, OnDraw seems to be getting hit lots of time -- more so then I wish to count. However, the one button that I added via AddView is NOT drawn on the screen. If you guys could help me get this one button on the screen I can get to writing the core logic!
On a side-note: I can draw stuff on my screen if I set an XML layout file as the view as such: SetContentView(Resource.Layout.LayoutName) But, since the nature of my program requires dynamic views being added all the time, I'd rather avoid writing lengthy Layout files. Thanks guys! Bump my question up if you think it's a worthwhile one!
In general, this is a good place to start. In particular it details how to add custom attributes, how to perform custom drawing, and how to design custom events that make sense in the context of your own application.
Also, it looks like you're trying to use C# style syntax in Java, which won't work for things like inheritance. Reading some java tutorials might help you out.
This is a good resource for that and should help you get up and going. Good luck!
Don't forget to call the super() to allow the parent class to do what it needs to do for the overridden method.
protected override void OnDraw(Canvas canvas) {
super(canvas)
}
Related link
http://developer.android.com/guide/topics/ui/custom-components.html#compound
I am using the onDispatchDraw(Canvas canvas) method to draw lines in my view. When I call canvas.drawLine() it always draws the line on top of all my views. Is there any way to draw the line under a button but on top of another view in my layout using canvas.drawLine()?
I have tried the following but it still draws the line over the button.
Button b;
RelativeLayout r;
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
Paint p = new Paint();
p.setColor(Color.Black);
canvas.drawLine(0,0,100,100,p);
r.removeView(b);
r.addView(b);
}
You're trying to reinvent the wheel. Z-ordering is already implemented in the window management subsystem and you can use it.
Create a custom view you want to draw on.
Make it non-clickable using android:clickable="false" or setClickable(false).
Make its background transparent and implement dispatchDraw().
Put all the views you don't want to draw on above this view in the view hierarchy.
Call super.dispatchDraw() after drawing the line. The dispatchDraw is called by viewgroup for drawing its children, so in your case, calling super.dispatchDraw() will draw the button first then you are drawing the line over it. Do dispatchDraw this way :
Updated code
class myListViewWithLine extends ListView {
....
#Override
protected void dispatchDraw(Canvas canvas) {
Paint p = new Paint();
p.setColor(Color.Black);
canvas.drawLine(0,0,100,100,p);
super.dispatchDraw(canvas);
}
....
}
Another method will be to just add a view with 1 dip as height and background color whatever you like under the button. It should look like like a line.
View v = new View(this);
ViewGroup.LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT,1);
v.setLayoutParams(lp);
v.setBackgroundColor(Color.WHITE);
addView(v,INDEX NUMBER AFTER THE BUTTON);
I have a FrameLayout that contains several ImageView. On the main activity, I record the touch events in order to move my FrameLayout and the images inside with the finger (drag).
For doing so, I am calling canvas.translate(x,y) inside the onDraw of the framelayout which is called by a invalidate() in the activity touch event handler.
Everything works like a charm except that after the translate, I am not able to click on my ImageView. In fact, the click listener of each image is still at the original place before the translate.
I have read that I should manually update the layout of each image after the translate but how to do that ? If I change the margin with the translate value, the images are going two times further ...
I would really appreciate any help on that one.
Cheers.
Here is the frameLayout where I translate the canvas in the onDraw() method (the ImageView are added to that FrameLayout in my main Activity).
public class TopView extends FrameLayout {
public float mPosX = 0;
public float mPosY = 0;
public TopView(Context context)
{
super(context);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(1920, 3200, Gravity.CENTER);
this.setLayoutParams(lp);
setWillNotDraw(false);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(this.mPosX, this.mPosY);
}
}
You can use setPadding(this.mPosX,this.mPosY,0,0) in the the constructor. It should work.