I'm trying to add a view in a viewGroup (without xml) but can't make the view appear. I can't figure out what I'm missing...please help, I've been looking all the web for hours now.
Here is my code (the Background class extends ViewGroup) :
public void setupBackground()
{
this.backgroundView = new Background(activity);
View bgGround = new View(activity);
bgGround.setX(100);
bgGround.setY(200);
bgGround.setBackgroundColor(activity.getResources().getColor(R.color.black));
bgGround.setBackgroundResource(R.drawable.mario_ground);
this.backgroundView.addView(bgGround, 100,100);
this.activity.addContentView(backgroundView, new LayoutParams(bgWidth,bgHeight));
}
Ok, I found it. ViewGroup is not enough for drawing content, I had to extend RelativeLayout (or another kind of layout) instead.
What made me loose lot of time is that I had overrid onLayout method while still in ViewGroup and then changed to RelativeLayout, so the onLayout for RelativeLayout wasn't calling super.onLayout.
Thanks anyway, and sorry for my poor english :)
Related
Bacground
I have been working on stripping out a library that deals with adding Accessibility with Talkback that I have created in an existing app. Originally my custom views were all ViewGroups, so I got everything working amazingly with ViewGroups (focusable navigation with D-pad, initial view focus, and content descriptions)
When I was moving this to a standalone library, I noticed that it didn't work with View. I thought ViewGroup was the superclass, but it turns out that View is the superclass. So I have been trying to find some workarounds to fix my issue. I started to do the following, and have a question based on this approach...
Code In Question
public class Accessibility {
public static ViewGroupAccessibility with(ViewGroup viewGroup) {
return new ViewGroupAccessibility(viewGroup);
}
public static ViewAccessibility with(View view){
return new ViewAccessibility(view);
}
}
I have fully implemented ViewGroupAccessibility and I intend to fully implement ViewAccessibility as it is a stub right now. So far the below code works well with TalkBack, I can do ViewGroup related stuff with ViewGroups, and it appears that I can do View related stuff with Views; however, I am wondering if this is even needed
What I know
Accessibility.with(new RelativeLayout(...)) // Returns ViewGroupAccessibility as RelativeLayout is a ViewGroup
//
...will return a ViewGroupAccessibility that can handle ViewGroup related stuff that can contain many different View and ViewGroup. (See code at the bottom of this post for real usage, and what what methods are available for ViewGroupAccessibility)
Accessibility.with(new Button(...)) // Returns ViewAccessibility as Button is a View
//
...will return a ViewAccessibility that can handle single View only related stuff (that is my assumption). Think only a Button.
What I don't know
// Hypothetical Usage
Accessibility
.with(new ClassThatExtendsView_WithMultipleComponentsThatCanHaveAccessibilitySetOnEachComponentIndividually(...));
// Custom View that extends View
public class ClassThatExtendsView_WithMultipleComponentsThatCanHaveAccessibilitySetOnEachComponentIndividually extends View {
...
}
Is this even possible? If no, then I am good. If yes, then I have a lot extra to think about
It will return a ViewAccessibility that can handle single View only, but then that would be the wrong thing to return.
Another way of asking the question is am I guaranteed that if a user calls Accessibility.with(View) that the given view will ALWAYS be a single view only? Like Just a single Button. Or can the View be made of more than one component
Full Code
You can check out the code at https://codereview.stackexchange.com/questions/134289/easily-add-accessibility-to-your-app-as-an-afterthought-yes-as-an-afterthought (there is also a GitHub link to the original code). I go in incredible detail into how the project was started, my design decisions, and my future goals all to help guide the code review process.
However, here is a snippet of a usage I have for ViewGroup
public class ContributionView extends RelativeLayout implements Mappable<Resume.Contribution> {
// Called from Constructors
private void init(AttributeSet attrs, int defStyle) {
root = (ViewGroup) LayoutInflater.from(getContext()).inflate(
R.layout.internal_contribution_view, this, true);
...
// Declare Navigation Accessibility
Accessibility.with(root)
// Disable certain views in the ViewGroup from ever gaining focus
.disableFocusableNavigationOn(
R.id.contribution_textview_creator,
R.id.contribution_textview_year)
// For all focusable views in the ViewGroup, set the D-pad Navigation
.setFocusableNavigationOn(txtProjectName)
.down(R.id.contribution_textview_description).complete()
.setFocusableNavigationOn(txtContributionDescription)
.up(R.id.contribution_textview_name)
.down(R.id.contribution_textview_link).complete()
.setFocusableNavigationOn(txtProjectLink)
.up(R.id.project_textview_description).complete()
// Set which view in the ViewGroup will have be first to be focused
.requestFocusOn(R.id.contribution_textview_name);
invalidateView();
}
private void invalidateView() {
...
// Declare Content Description Accessibility
Accessibility.with(root)
// Set the content descriptions for each focusable view in the ViewGroup
// Set the content description for the Contribution Name
.setAccessibilityTextOn(txtProjectName)
.setModifiableContentDescription(getProjectName())
.prepend("Contribution occurred on the Project called ")
.append(String.format(" by %s in %s",
getProjectCreator(),
getContributionYear())).complete()
// Set the content description for the Contribution Description
.setAccessibilityTextOn(txtContributionDescription)
.setModifiableContentDescription(getContributionDescription())
.prepend("Description: ").complete()
// Set the content description for the Contribution URL
.setAccessibilityTextOn(txtProjectLink)
.setModifiableContentDescription(getProjectLink())
.prepend("URL is ").complete();
}
...
}
Yes, there is a way to move accessibility amongst the various areas/components of a View. It requires a little work, though.
Start here:
https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeProvider.html
I am new to android, and it seems only viewgroup can be parent of some View, but when I use DDMS to dump view hierarchy of an app, I find there is a View containing some children. Could anyone please explain this to me? Please see the image below:
To investigate on that issue I created a custom ViewGroup that does nothing:
private static class MyViewGroup extends ViewGroup {
public MyViewGroup(Context _context) { super(_context); }
#Override
protected void onLayout(boolean _changed, int _l, int _t, int _r, int _b)
{ /* nothing... */ }
}
and added it to the current content view of an activity:
ViewGroup foo = (ViewGroup)
this.getWindow().getDecorView().getRootView().findViewById(android.R.id.content);
foo.addView(new MyViewGroup(this));
The hierarchy viewer of eclipse shows it labeled as a 'View' too.
Furthermore I looked at the Node details of another 'View' with Children and was able to see a resource-id.
In my case it had the value android:id/action_bar_overlay_layout and I could find that resource-id to be used in an XML-layout for a android.support.v7.internal.widget.ActionBarOverlayLayout component.
It's implementation is in the support library and truely extends Framelayout.
I think this issue is simply the hierarchy viewer only supporting labels for standard view components.
This is just a guess, but I think that any ViewGroup that is not one of the ViewGroup-implementations provided by Android will be shown as View, maybe to hide the name of your custom ViewGroups from others. So it is basically a privacy thing for the developers
I have following issue.
I have two fragments, each of them have a ListView with adapter. By choosing element from first list, I replace the fragment containing first list with fragment containing second list. I want to make each row background transparent only in second Fragment, but the following code used in getView of the second adapter, it looks like it updates the resource? Could anyone explain why?
Code:
First Fragment:
public View getView(int position, View convertView, ViewGroup parent) {
(...)
v.setBackgroundResource(R.drawable.abstract_gray);
(...)
}
Second Fragment:
public View getView(int position, View convertView, ViewGroup parent) {
(...)
Drawable backgroundDrawable = context.getResources().getDrawable(R.drawable.abstract_gray);
backgroundDrawable.setAlpha(ToolsAndConstants.BACKGROUND_TRANSPARENCY);
v.setBackgroundDrawable(backgroundDrawable);
// I call this to check if when I call the drawable from resurce it will not be transparent
v.setBackgroundResource(R.drawable.abstract_gray);
// But it is...So previous fragment background, when I go back with back Button
(...)
}
Maybe this question is really basic, but when I create new Object from resource, I actually work with the resource itself and all changes made on new Object will affect whole application, even if other classes will not have access to that Object??
EDIT:
Sorry, I just realized that I don't create new Drawable, I just make a reference. How can I create new one then? If I cant, how can I change background of only second list?
Marek read this article, it explains your doubts http://android-developers.blogspot.com/2009/05/drawable-mutations.html
I'm writing an app with the sole purpose of trying to understand how the view hierarchy in Android works. I am having some really harsh problems in it right now. I'll try to be concise in my explanation here.
Setup:
Currently I have three Views. 2 are ViewGroups and 1 is just a View. Let's say they're in this order:
TestA extends ViewGroup
TestB extends ViewGroup
TestC extends View
TestA->TestB->TestC
Where TestC is in TestB and TestB is in TestA.
In my Activity I simply display the views like so:
TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(myView);
Problems:
The onDraw(Canvas canvas) method of TestA is never called. I've seen a couple solutions to this saying that my view doesn't have any dimensions (height/width = 0), however, this is not the case. When I override onLayout(), I get the dimensions of my layout and they are correct. Also, getHeight()/Width() are exactly as they should be. I can also override dispatchDraw() and get my base views to draw themselves.
I want to animate an object in TestB. Traditionally, I would override the onDraw() method on call invalidate() on itself until the object finish the animation it was supposed to do. However, in TestB, when I call invalidate() the view never gets redrawn. I'm under the impression that it's the job of my parent view to call the onDraw() method again, but my parent view does not call the dispatchDraw() again.
I guess my questions are, why would my onDraw() method of my parent view never get called to begin with? What methods in my parent view are supposed to be called when one of it's children invalidate itself? Is the parent the one responsible for ensure it's children get drawn or does Android take care of that? If Android responds to invalidate(), why does my TestB never get drawn again?
Ok, after some research and a lot of trying, I've found out I was doing three things wrong in regards to problem #2. A lot of it was simple answers but not very obvious.
You need to override onMeasure() for every view. Within onMeasure(), you need to call measure() for every child contained in the ViewGroup passing in the MeasureSpec that the child needs.
You need to call addView() for every child you want to include. Originally, I was simply created a view object and using it directly. This allowed me to draw it once, but the view was not include in the view tree, thus it when I called invalidate() it wasn't invalidating the view tree and not redrawing. For example:
In testA:
TestB childView;
TestA(Context context){
****** setup code *******
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
childView = new TestB(context);
childView.setLayoutParams(params);
}
#Override
protected void dispatchDraw(Canvas canvas){
childView.draw(canvas);
}
This will draw the child view once. However, if that view needs updating for animations or whatever, that was it. I put addView(childView) in the constructor of TestA to add it to the view tree. Final code is as such:
TestB childView;
TestA(Context context){
****** setup code *******
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
childView = new TestB(context);
childView.setLayoutParams(params);
addView(childView);
}
#Override
protected void dispatchDraw(Canvas canvas){
childView.draw(canvas);
}
Alternatively, I could override dispatchDraw(Canvas canvas) like so if I had many more children to draw, but I need some custom element between each drawing like grid lines or something.
#Override
protectd void dispatchDraw(Canvas canvas){
int childCount = getChildCount();
for(int i = 0; i < size; i++)
{
drawCustomElement();
getChildAt(i).draw(canvas);
}
}
You must override onLayout() (it's abstract in ViewGroup anyway, so it's required anyway). Within the this method, you must call layout for every child. Even after doing the first two things, my views wouldn't invalidate. As soon as I did this, everything worked just perfectly.
UPDATE:
Problem #1 has been solved. Another extremely simply but not-so-obvious solution.
When I create an instance of TestA, I have to call setWillNotDraw(false) or else Android will not draw it for optimization reasons. So the full setup is:
TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
myView.setWillNotDraw(false);
setContentView(myView);
This isn't a direct answer, but here is a fantastic tutorial on how to draw custom views and gives a great crash cours in how to animate a view. The code is very simple and clean too.
Hope this helps!
This is a dumb question and I know the answer is sitting in front of me, I'm just having trouble searching for it in the right way.
I've got a custom view that has been set as the content view and inflated from xml. How can I access the instance to call methods on it from the activity class? I remember seeing something akin to getResourceById() a while back, but now I can't seem to find it and I'm not even sure if that's the best way to do it.
Sorry for the dumb question.
If you have used an inflater, you will be given an instance of a View class. You then use your instance like so
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = li.inflate(R.layout.small_listview_row, null);
TextView tvItemText = (TextView)row.findViewById(R.id.tvItemText);