Drawing a shape? - java

I have tried to draw a shape for a long time without an answer. I used setContentView(view) for that view. However, I have another setContentView:
setContentView(R.layout.activity_main);//Buttons, Views from XML...
setContentView( mCustomDrawableView);//Custom Shape
Problem is, it only shows ONE view, the shape. The Buttons defined on XML are gone. Basically whichever setContentView(view) is called last is the only View who is drawed. I've also tried many other ways to draw the shape without an answer that works.
I have seen that multiple setContentView do overlap each other, but this is the only way that partially works. Any way to display both Views or another way to draw a shape and my XML Views?
EDIT:
My shape is made in java, not XML
EDIT 2:
Here is my customShape Class:
public class CustomDrawableView extends View {
private ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
int x = 10;
int y = 10;
mDrawable = new ShapeDrawable(new RectShape());
// If the color isn't set, the shape uses black as the default.
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x,y,x+10,y+10);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}

OK ... setContentView is something which takes a layout/view/whatever and the input defines the contents of the activity/fragment your are setting it to.
So of course your first call does the whole layout, and the second wipes that and sets the content to what you're sending to it.
The thing is to have your view and then add it to a layout. If you have any view in your initial setContentView xml named (+id) and defined (Layout x = getfromid(r.id.x) you can then add your view to that layout with x.addview(myview) or x.addview(CustomDrawableView(context)).
-edit for clarification-
An Activity/Fragment has a Layout which you set using setContentView. You do this once, normally. A Layout defines where Views are placed.
So, you setContentView(your.xml) in onCreate. If you have a programmatic View class, you can then do two things: add new YourView to a named and specified layout in your act/frag (using any layout defined in xml, passed to your frag/act in the xml passed in setContentView and then defined by Layout x = findViewById(R.id.idoflayoutinxmlinfield 'android:id="#+id/x") in code.
Then you can x.addview(new MyView(context)) or Myview z = new MyView(context) and x.addView(z);
-edit2-
When you get better, you can also just add your custom View to the original layout by adding it to the original xml definition.
-edit3-
Sigh. OK. Here it is:
In your activities java file:
RelativeLayout myRellayout;
#Override
public void onCreate(){
setContenView(your.xml)
myRelLayout = findViewByID(R.id.myrellayout);
myRelLayout.addView(new MyCustomView(this));
//or define your custom view and the add it by:
//MyCustomView mcv = new MyCustomView(this);
//myRelLayout.addView(mcv);
}
And your xml should contain any layout like this:
<RelativeLayout
android:id="#+id/myrellayout"
android:width="whatever"
android:height="whatever"
/>

Related

Changing LayoutParams with an OnClickListener?

I'm learning how to work with LayoutParams. there is something I don't understand about setting layout params. here is my code:
layout = findViewById(R.id.relative_layout);
img = findViewById(R.id.img_view);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) img.getLayoutParams();
img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
params.addRule(RelativeLayout.CENTER_IN_PARENT);
img.setLayoutParams(params); //Setting LayoutParams?
}
});
If I put this code directly in OnCreate() method, it works fine but why when I transfer it inside an OnClickListener, I need to set the layout params again? what logic is behind this?
You are modifying the Params after they are being set. This means that the current solution for solving the constraints optimally is not valid anymore, and they need to be recalculated. By changing this one constraint, your whole layout may change.
By setting the layout params on the layout again, it's resolving and recalculating new positions for every view, including your newly added constraint. As long as you don't call recalculation methods, the old layout will be used.

Programmatic changes to a view aren't applied

I made a list that's loaded with Contact friends and the user can select them by tapping on them. If a person is selected, the listitem's backgorund changes colour, if deselected, the bg colouring goes away.
Problem is, when I call my method on an OnClickListener, it's fine.
When I however call it in a loop to colour already selected friends (e.g. when revisiting the list), it doesn't do the colouring.
The loop that goes through the elements to call colorize if needed:
for (int i = 0; i < adapter.getCount();i++){
ContactFriend cf = (ContactFriend) adapter.getItem(i);
View v = getViewByPosition(i,listView);
colorizeFriendBg(v, cf);
adapter.notifyDataSetChanged();
}
note I do the exact same in the listener and it works fine there.
And the colorizer:
private void colorizeFriendBg(View v, ContactFriend friend){
if(friend.isSelected()){
v.setBackgroundColor(0x993399ff);
}else{
v.setBackgroundColor(0x00000000);
}
v.invalidate();
}
This issue is quite strange and I have no idea what to do in order to make it right. The whole bunch is called from onActivityCreated, if that matters.
Edit:
I debugged it of course and the code runs and should change the colour, not running isn't the issue.
Edit again:
here's the listener implementation:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ContactFriend fr = (ContactFriend) adapter.getItem(position);
addToSelected(fr);
//TODO: make it switch some BG colour when clicked. use getViewByPosition.
View v = getViewByPosition(position,listView);
colorizeFriendBg(v,fr);
adapter.notifyDataSetChanged();
}
});
what type of item View are you getting from the Adapter?
that View could / should implement colorize() and color itself;
for example: v.colorize(contact.isSelected()) to switch colors.
or with Android Data-Binding XML (where the viewModel is an instance of Contact):
<data class="com.acme.databinding.ContactViewHolderBinding">
<variable name="viewModel" type="com.acme.model.Contact"/>
</data>
...
android:backgroundColor="#{viewModel.isSelected ? R.color.MAGENTA : R.color.BLACK}"
class Contact just would require a getter and a setter for property isSelected.
one actually can also bind event handlers, which would be an alternate approach.
You need to call invalidate() on your view to make the color changes visible.
invalidate() forces a redraw with the new colors.

The java backend code to the xml code

I have a basic android application that has several pages accessible from the home page, anyways I change the layout by doing
public void main(View view) {
setContentView(R.layout.main);
}
and changing the layout to the main game page. Now I want to add characters to it and so I added protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint green = new Paint();
green.setColor(Color.GREEN);
Rect theRect = new Rect();
theRect.set(0,0,canvas.getWidth()/2,canvas.getHeight()/2);
canvas.drawRect(theRect, green);
}
but I only want to call it for the "main" or the scene/layout that the game is played in. It appears to not be called at all and that may be due to the fact that I do not completely understand the relationship between the xml and multiple java files and functions. I'm new to android so this may be a stupid question, I just couldn't find anything with multiple web searches, with no luck.
You can add your game view into activity class:
Class GameView extends View/SurfaceView
{
//-- contains onDraw() method
}
To add this game view into your activity, you have to do
get reference of layout in whioch you want to add this gameview.
eg: LinearLayout layout=(LinearLayout) findViewById(R.id.linearlayout);
Add GameView into this layout
eg: layout.addView(new GameView());
For more detail, you can get reference from http://www.edu4java.com/en/androidgame/androidgame3.html
Hope this helps you

Add a Line/Image above ActionBar

I want to add a line above the action bar like in the "pocket"-app. How can i do this?
Here is a picture for example:
Thanks
tomtom
Taking advantage of an Activity's WindowManager, we can draw any view we want on top. Here's some (half-pseudo) code that should help:
// Create an instance of some View that does the actual drawing of the line
View customView = new CustomView(<some context>);
// Figure out the window we have to work with
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
// Make sure the view is measured before doing this
int requestedHeight = customView.getLayoutParams().height;
// setup the params of the new view we'll attach
WindowManager.LayoutParams wlp = new WindowManager.LayoutParams(
rect.width(), requestedHeight,
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
// set the parameters so we fit on the top left of the window
wlp.x = 0;
wlp.y = rect.top;
wlp.gravity = Gravity.TOP;
// finally add it to the screen
getWindowManager().addView(header, wlp);
The only thing to be careful is that you can't run that code from onCreate() or any lifecycle method of the Activity because the Window won't have been created yet (You'll get a BadTokenException). One way might be post a Runnable on the Window's DecorView so that your code to add the CustomView runs after the Window is created:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
getWindow().getDecorView().post(<Runnable that execs code above>);
}
As for the actual CustomView that will display that multi-coloured bar, I feel like that's a good exercise :-)
All you'd need to do is have the onDraw() method use canvas.drawRect() with specific x and widths.
Hope that helps.
What Pocket does
As for how Pocket actually does it. If you use HierarchyViewer on the Pocket app, you'll be able to determine that Pocket uses a custom class for their ActionBar. Since they already rebuild all the features of the ActionBar for their needs, in their case, adding the line is like adding a regular View to some ViewGroup.

When in the Activity life cycle are UI elements available to be measured?

I am defining some animations based on the inflated dimensions of some UI controls. What is the earliest point in the Activity life cycle I can tap into to know when the UI elements have been sized and I can query them for their dimensions?
Right after you set the Content of you Activity via the setContentView() method is the earliest I've been able to grab information from my widgets (size, text and others).
Per Rich's request:
You can determine when the width and height by using the GlobalLayoutListener like so:
final View myView = findViewById(R.id.id_of_view);
ViewTreeObserver vto = myView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int viewHeight = myView.getHeight();
int viewWidth = myView.getWidth();
// Do what you want with the width and height
ViewTreeObserver obs = myView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});
Full (better) answer: How to retrieve the dimensions of a view?
If you want to drill down to the point that widget have JUST been placed you have to extend each widget you want to monitor.
Then override onDraw method and capture if that view has been drawn one time
private boolean imVisible=false;
public boolean imVisible() {
return imVisible;
}
#Override
protected void onDraw(Canvas canvas) {
if(!imVisible){
imVisible=true;
}
super.onDraw(canvas);
}
Then you can do a for loop to the widgets of interest and you know they are drawn at their position with dimentions.
A far better solution is when the onDraw gets called the first time fire a listener that is drawn. You have to set an array of listeners that watch the progress of the widgets. That way the exact moment that the last widget is on the screen... you know it.

Categories

Resources