is there a way to get every view that is inside my activity?
I have over 200 views including buttons, and images, so i want to be able to access them by using a loop
for example something like
for (View v : this)
{
//do something with the views
//depending on the types (button, image , etc)
}
is there a way to get every view that
is inside my activity?
Get your root View, cast it to a ViewGroup, call getChildCount() and getChildAt(), and recurse as needed.
I have over 200 views including
buttons, and images, so i want to be
able to access them by using a loop
That is a rather large number of Views.
To be specific:
private void show_children(View v) {
ViewGroup viewgroup=(ViewGroup)v;
for (int i=0;i<viewgroup.getChildCount();i++) {
View v1=viewgroup.getChildAt(i);
if (v1 instanceof ViewGroup) show_children(v1);
Log.d("APPNAME",v1.toString());
}
}
And then use the function somewhere:
show_children(getWindow().getDecorView());
to show all Views in the current Activity.
Try to find all view associated with the Activity.
give the following command.
ViewGroup viewgroup=(ViewGroup)view.getParent();
viewgroup.getchildcount();
iterate through the loop.
We will get the Result.
Nice way to do this in Kotlin recursivelly:
private fun View.getAllViews(): List<View> {
if (this !is ViewGroup || childCount == 0) return listOf(this)
return children
.toList()
.flatMap { it.getAllViews() }
.plus(this as View)
}
You can use the hierarchyviewer, It allows you to see the view hierarchy including those created in code. It's primary reason is for debugging things like this. The latest Android Studio now has this feature in the Device Monitor that lets you make a dump of the UI to debug it.
Related
I am using recycler view in a chat app, now as you all know in a chat room we have 2 different views.
Right view : the one that you sent.
Left view : the one that you received.
Now I managed to achieve what I want by using one layout item and inside that item I used 2 relative layouts, one layout for the right view and another for the left view.
And in order to know what layout to show I did this in onBindViewholder:
onBindViewHolder(){
if(/*I sent the message*/){
right_layout.setVisibility(view.VISIBLE);
left_layout.setVisibility(view.GONE);
}
else {
right_layout.setVisibility(view.GONE);
left_layout.setVisibility(view.VISIBLE);
}
}
I don't have any problem with using the above method. But my question is why others use that thing that is called multiple view types in which they use 2 view holders? Should I use it instead?
First: in your case, I'm not sure whether it's really necessary to use two view types. A typical RecyclerView on a mobile phone will show between seven and ten rows at a time and it will generate some more just to be able to scroll smoothly should the need arise. Compared to a gaming app, there's next to nothing to do UI-wise. But you asked why someone might want to use multiple view types, so here goes:
Right now 50% of the Views you're inflating will be set to GONE, so it's like a continous waste of CPU time and (worse on a mobile device) it's draining the battery unnecessarily. By using two view types, you avoid this (to a certain extent, I suppose that determining the view type takes less energy).
Another reason is better performance which results in a better user experience: RecyclerView was developed to efficiently deal with lists of items. By telling it which row can be reused for which list position, you are making the best use of its recycling algorithm.
From a developer point of view, I'd keep your approach as long as the difference between the two types is no more than two or three lines of code. But there are lists where the difference is more like twenty lines of code. In this case, using different view types, layout files and ViewHolders improves the readability of your code.
Yes, you should implement different a ViewHolder for each type of view. This provides a few advantages, like not having to maintain the relevant state, decluttering layouts, and generally easier to reason about once additionally functionality is included in the future.
It's also easy to implement:
#Override
public int getItemViewType(int position) {
Message message = messages.get(position);
if (message instanceof ReceivedMessage) {
return VIEWTYPE_RECEIVED;
}
if (message instanceof SentMessage) {
return VIEWTYPE_SENT;
}
return 0;
}
then:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
switch (viewType) {
case VIEWTYPE_RECEIVED:
View receivedView = inflater.inflate(R.layout.item_received, parent, false);
ReceivedViewHolder receivedViewHolder = new ReceivedViewHolder(receivedView);
return receievedViewHolder;
case VIEWTYPE_SENT:
// ...
}
}
finally:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEWTYPE_RECEIEVED:
// ...
}
}
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
It's maybe a bit abstract but I'd like to know if some of you have a decent solution to that problem:
I have a complicated layout and I need to find ALL the instances of a certain type of view.
I have few solutions but I find none of them perfect and I'd like to know if there is another way or a way to improve them.
MY FIRST OPTION
We can iterate in the view tree with getChildCount() and getChildAt() and then check with instanceof like in lots of SO answers.
for (int i = 0; i<parentContainer.getChildCount(); i++){
View child = getChildAt(i);
if (child instanceof BlaBla){
// Do something wonderful
}
}
It is highly inefficient because I have these instances in many places and in particular in nested places so I need to make this method recursive.
MY SECOND OPTION
It would be to work with dynamic tags or ids and use findViewById or findViewWithTag. But the issue is that it makes something more to configure and as always it makes the software more complicated.
So my question is: how can I do a complete search in the view tree in
order to find all instances of a component without doing the search
myself (because it would be probably be very inefficient)? Is that
somehow possible?
So, I'm not sure second option is possible as in this case you'll need to create this views in runtime and assign some generated ID with some bit mask to recognize them later. If to create your views from layout you will end up with traversing tree view and assigning these special IDs which is pretty much accends to 1st option.
In my project I also have to dynamically apply colors to some views and I do it without recursion. Pattern is following:
ArrayList<View> views = new ArrayList<>();
views.add(getWindow().getDecorView());
do {
View v = views.remove(0);
if (v instanceof ViewGroup) {
ViewGroup group = (ViewGroup) v;
for (int i = 0; i < group.getChildCount(); i++) {
views.add(group.getChildAt(i));
}
}
if (v instanceof MyCustomView) {
//do whatever you need here
}
} while(!views.isEmpty());
So you get rid of using recursion and replace it with own stack and iteration through it. This solution quite efficient especially if you can skip things like ListView, RecyclerView.
How can I monitor multiple views in my Android application. The reason I'm interested in this, is cause, my app has a view that has lot of fields EditTexts, Spinners, CheckBoxes, RadioButtons and some EditTexts are hidden unless a particular Spinner Item is selected or a Radio Button is checked. All I want the observer to do is to check all fields are filled before the submit Button is enabled and if any View was un-hidden it must check if the Fields have been filled too. This is where I'm stuck on what to do: I have a four boolean checks that check for the four hidden Views, but then I don't want to write a long if/else statement. Thanks
The following code will help you to check an unknown number of Views of different types contained in a single ViewGroup. In your case with just four views, I think you are likely prefer four 'if' clauses.
You could mark all Views which you want to check by adding a tag in the layout file:
android:tag="CheckMe"
Tags don't have to be unique, so you can use the same tag for all Views.
In addition to that, give an id to the ViewGroup containing your Views (this could be a LinearLayout)
android:id="#+id/myLayout"
Then in your activity you can first get the ViewGroup
ViewGroup myLayout = (ViewGroup) findViewById(R.id.mylayout);
and then cycle over the child Views:
for (int i = 0; i < myLayout.getChildCount(); i++)
{
View v = myLayout.getChildAt(i);
if (v.getTag().toString().equals("CheckMe")
{
if (v instanceof EditText)
{ // do something
}
else if (v instanceof SomeOtherView)
{ // do something else
}
}
}
I'm trying to inspect a code for a very big Android (Amazon Fire TV) activity but i keep loosing the focus in the running app and i don't know what element is being focused.
I'm looking for a way (Wether it's an App, a developer setting - Show Layout Limits gets near - or something i can code inside the activity) to see what view is being focused, without having to change the layout (Selectors) of every single view.
What do you suggest?
Activity has a method called getCurrentFocus().
Maybe you could call hasFocus() on all the Views if the above doesn't work. I imagine the method would look something like this:
public View getFocusedView(View layout)
{
View focusedView = null;
// Note: I'm not sure if FOCUS_DOWN is the right one to use here
// so you may want to see the other constants offered
ArrayList<View> views = layout.getFocusables(View.FOCUS_DOWN)
for(View v: views)
{
if(v.hasFocus())
{
focusedView = v;
}
}
return focusedView;
}