In my application, I have inflated a view multiple times dynamically as the data comes from the server. For Example, I am inflating a linearLayout multiple times. Now I am trying to test the click on these dynamic views with robotium. The problem is that the layouts that are added dynamically have same id and these are added multiple times. How can I click each of the dynamically inflated linearlayouts.
Here is a static function that does what you want (i think, i do not have the android sdk or anything on my current machine so cannot test). I would not use it as a static function myself, I would put it somewhere that makes sense, but you can do with it what you want!
public static List<View> getViewsById(Solo solo, int id) {
List<View> allViews = getViews();
List<View> matchedViews = new ArrayList<View>();
for(View view : allViews){
if(view!=null && view.getId() == id){
matchedViews.add(view);
}
}
return matchedViews;
}
Use solo.getView(view.class,index).performClick(); to get the view in the specific index and click on it.
It can be done by several ways. One simple solution is when you inflate a view, apply a click listener at that time. Like if you are using some loop, in each iteration, add click listener there in loop.
Related
I want to create a custom ListView.
Initially, the custom ListView has one array of data, but when user taps one of the list items, it's then removed from current array and added to another. If the user taps on the second array, the list item is then added back over into the first array.
Please suggest how to apply logic to do this.
Updates : I wants to use only one listview/recyclerview.
Following are screen shots..
Regarding the object switch - this is a simple transfer between lists, , just know beforehand if the insertion and removal is index based, e.g:
contacts.add(iLocation, ContactObject);
favorites.remove(iOtherLocation);
Regarding the ListView stuff, I would suggest converting to RecyclerView, let's build a general scenario:
You have a screen (Activity or Fragment) that holds one list (the implementation can be ListView or Recycler), and another screen that holds the other list.
In both of your lists you have adapters in which you implement the logic for the clicks on the objects in the lists.
The click transfers the object, either directly to the other list, OR to a temporary Object holder (because you might need it for other stuff), in which case you will need to pull that object from the other view, either way you remove it from the current one.
you switch to the other view, and refresh it.
An easy way to go -
Assuming the screens are the same, use only one Activity, holding a single RecyclerView, and handle 2 adapters, each for every list, the adapters allow you to handle the clicks easily, with an index for the object clicked, the click executes the info swap action,the Activity handles the visual swap Action.
a very general example would be:
//init everything obviously ;)
List<ContactObject> contacts;
List<ContactObject> favoritesContacts;
//the AdapteListener is an interface declared inside the adapter
mContactsRecyclerAdapter = new ContactsRecyclerAdapter(this, contacts,new ContactsRecyclerAdapter.AdapterListener()
{
#Override
public void cellClicked(int iIndex, ContactObject object)
{
favoritesContacts.add(iIndex, ContactObject);
contacts.remove(iIndex);
mContactsRecyclerAdapter.notifyDataSetChanged();
mFavoritesRecyclerAdapter.notifyDataSetChanged();
mRecyclerView.swapAdapter(mFavoritesRecyclerAdapter, false);
}
});
And vice-versa for the other adapter.
Hope this helps, comment if you have problems and I'll update.
Please implements with custom view extend with Linearlayout
Custom view has 2 child Linearlayout in which will add with this custom view
First time add all the element in first Linearlayout and based on user action please remove from first Linearlayout and add it in another layout
I have some content in an Android app which is shown in a list. Each list entry has similar fields - let's say a picture, some text, and a text box. However, some list entries are different than others. The order of the content is based on the result of a server call.
The list itself needs to be fairly dynamic, and I'm currently using a linearlayout rather than a listview for a few reasons. My code looks something like this:
LinearLayout list = findViewById(android.R.id.list);
while (more content to add) {
switch (content type) {
case A:
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
case B:
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
This works great. Except - when I put this in a fragment, and rotate the screen, now my app crashes because I have multiple fields with the same android.R.id.edit identifier.
I had thought this was a fairly elegant solution and the Android gods seem to disagree. Do I need to rip out the ID for all of my xml sublayouts? If I go this route, how should I grab references to the content?
So obviously, ListView or RecyclerView would be preferable to use here, but since you've stated you have reasons not to, I'd suggest that you disable automatic state saving for each of the views.
You can just call editText.setSaveEnabled(false), which will fix the issue, but have the side effect of not automatically retaining the view's state (e.g. input data will be lost). If you're maintaining this data yourself and restoring it on configuration changes or state restoration, this should be a totally workable solution.
I believe you could also just call setSaveFromParentEnabled(false) on the containing LinearLayout (although I haven't used that flag myself), which should disable state saving for any view in the sub-hierarchy. Same caveat applies.
I would suggest using a ListView instead of the LinearLayout and creating a custom adapter to fill the ListView. You could still have the list_item layouts that you have and then add them to the list in the newView method of your adapter. Pass your content type through a method, say getItemViewType(). Something like this:
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Choose the layout type
int contentType = getItemViewType();
int layoutId = -1;
switch (contentType) {
case A: {
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
}
case B: {
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
I think this problem occur due to device screen rotation
When the phone rotates and the screen changes orientation, Android usually destroys your application’s existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.
The most important aspect of handling orientation changes is saving state. In most cases this involves implementing the onSaveInstanceState method (this could be in your Activity, Fragment or both) and placing the values you need to save in the Bundle argument that gets passed to the method.
For more details and code examples...
Please read this article
If you have a dynamic list, you should be using a ListView or RecyclerView.
I have a main activity that uses a FrameLayout with the screen split into left navigation and right content panels. The left panel is used for navigation to load different layouts into the right panel (called contentLayout) which can have multiple layouts glued together. I have a ListView in the left navigation panel with onItemClickListeners. In their separate onItemClick methods, I use contentLayout.removeAllViews() and then inflate their layouts like so:
private final OnItemClickListener redListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
contentLayout.removeAllViews();
if (myFirstClassLayout == null) {
myFirstClassLayout = FirstClassLayout.getLayout(
layoutInflater,
MyActivity.this,
resources,
new ArrayList<String>(),
new ArrayList<Desc>()
);
}
CommonClass.updateListeners();
if (FirstClassLayout != null) contentLayout.addView(myFirstClassLayout);
}
};
private final OnItemClickListener blueListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
contentLayout.removeAllViews();
if (mySecondClassLayout== null) {
mySecondClassLayout = SecondClassLayout.getLayout(
layoutInflater,
MyActivity.this,
resources,
new ArrayList<String>(),
textList,
new ArrayList<Desc>(),
hash
);
}
CommonClass.updateListeners();
if (SecondClassLayout != null) contentLayout.addView(mySecondClassLayout);
}
};
In both the FirstClassLayout and SecondClassLayout, I inflate a ThirdClassLayout which contains a dynamic ListView (let's call it commonListView) that makes use of an ArrayAdapter<String>. The ListView is a listChoiceIndicatorSingle style view, meaning that it shows a radio-button-like icon to the right of each list item.
When the app starts up, the right content panel is empty. If I trigger the redListener or the blueListener, it will display the commonListView in the right content panel with none of the list items selected. If I trigger red first, then trigger blue, then make a selection in the commonListView, the change is reflected back to the FirstClassLayout which is great! However, when I continue by selecting something else in the commonListView from that FirstClassLayout, and then trigger the blueListener to go to the SecondClassLayout, the commonListView in the SecondClassLayout does not reflect the selection I just made. Instead, it is stuck on item I selected when I was last on the SecondClassLayout.
This happens if I perform the testing in the reverse order, so depending on which listener I trigger last, that last listener and layout will have proper UI synching control over both layouts. The one I trigger first seems to be able to only control itself. I need for both listeners to be able to display the exact same ListView selected item on both Layouts at any given time.
The synching of the commonListView from last layout to first layout is achieved through CommonClass.updateListeners() which does:
adapter.clear();
for (String title: statusSet) adapter.add(title);
adapter.notifyDataSetChanged();
commonListView.setItemChecked(positionOfItemChecked, true);
Log.i(TAG, "position checked = " + positionOfItemChecked);
adapter.notifyDataSetChanged();
Since this works from last layout triggered to first, I figure this must be related to a UI refresh problem, but I've searched everywhere for a good solution and tried all suggestions. The Log.i statement prints out correctly in the LogCat for first or last layout triggered, so I know it is hitting the code properly. I just don't understand why the first one is unable to control the last one. I've tried:
commonListView.invalidate();
((BaseAdapter) commonListView.getAdapter()).notifyDataSetChanged();
and AsyncTask and several others, but nothing seemed to work on refreshing the first layout so that it matches the second. Has anyone encountered such an issue? Does anyone know how to fix or get around this?
See if contentLayout.requestLayout() after adding the views fixes your problem.
I figured out what was causing the 2nd one to take hold over the 1st. I inherited a lot of this code and so I'm not familiar with all the parts. Several other pieces were involved. I finally found the portion of code where the original author used a custom AdapterWrapper that implements a custom Listener. The ThirdClassLayout was only creating a new ArrayAdapter and a new AdapterWrapper once by checking to see if the static ArrayAdapter was null. Removing this check allowed both adapter objects to be created new for both layouts. With their own Listeners created, both layouts now respond.
Lets say that I have two different types of RelativeLayouts. That is to say these 2 RelativeLayouts differ because they contain different views. One might have textviews, an image view etc and the other might have also have some textviews which mean something completely different than the other set of textviews in the other relativelayout. Lets say however that both have a Submit Button. So to make my point more clear here is some code:
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
ViewParent parent = v.getParent();
if(parent instanceof CustomRelativeLayout){
CustomRelativeLayout aRelativeLayout = (CustomRelativeLayout)parent;
for(int childrenIndex = 0; childrenIndex < r.getChildCount(); childrenIndex++){
View childView = aRelativeLayout.getChildAt(childrenIndex);
}
}
}
The problem here is is that we don't know which CustomRelativeLayout aRelativeLayout is referring to. Depending on which CustomRelativeLayout it is will depend on what specific childViews i will want to search for and what logic I implement relevant to those views. I would like to be able to have a switch statement to check which type of CustomRelativeLayout is the parent.
So the questions that i would like to ask is:
how do i get more information about which instance of CustomRelativeLayout refers to the button that was clicked? Is there a way to get the instance variable name?
Once i have found out that information how do i get specific information about the children of the parent view that i am working on? The thought is, is that i might have 30 child views in the parent but i am only interested in one specific view(i might want to get the text of one specific textview as an example). I will know to look for it specifically because i would have done a switch statement on the different instances of my CustomRelativeLayouts(the first question) and therefore i know which view i want to look at, which logic to perform or what other methods that i need to call.
Would appreciate any thoughts or help with this.
For your first question, there are couple options:
Use separate OnClickListeners for each button. Then, each button will only trigger its own listener's onClick() method.
You can give each button a different id either in XML (via the android:id property) or in code (via setId(int id)). Then in onClick() you can check the id of the View that was passed as the argument.
For your second question:
Since you have the parent ViewGroup, you can find specific views within it by using:
TextView interestingView = (TextView) parent.findViewById(R.id.interesting);
This will only search the children of the parent view.
If you need to get an unknown number of views the best strategy is probably iterating through them like you are now. You can identify groups of views by setting a tag either in XML (android:tag) or code (setTag(Object tag) and check them as you iterate. For example, if you have a set of TextViews and in each one is either a color or an animal, you might handle that like this:
// defined elsewhere
private static final String TAG_COLOR = "color";
private static final String TAG_ANIMAL = "animal";
...
int count = parent.getChildCount();
for (int i = 0; i < count; i++){
View view = parent.getChildAt(i);
if (TAG_COLOR.equals(view.getTag()) {
// handle color
} else if (TAG_ANIMAL.equals(view.getTag()) {
// handle animal
}
}
There are several ways to do this.
Use "id" to identify views. You can give different id to different views, and then it will be possible to identify them. See http://developer.android.com/reference/android/view/View.html#getId() (the API documentation of View.getId()) for more information.
Mark each view with different tags, and identify them through tags. See http://developer.android.com/reference/android/view/View.html#getTag() (the API documentation of View.getTag()) for more information.
If you want to customize more, just inherit default Android views, and use "instanceof" to identify them.
In my android app, i have a particular scenario, for one of the screens.
I require 2 button,one on each side of the corner(left and right).
Below this i want to populate data in a control.
If left button is clicked, the control should be a gridview.
If right button is clicked , the control should be a listview.
And accordingly the data should be populated.
How should i approach this scenario.
Should i create controls dynamically, or use xml instead
Rgds
Create the view with two different layouts.
Assume that you have 2 xml layouts named gridLayout.xml and listLayout.xml
and that somehow mode is determined earlier in your code and set to one of two constants GRIDVIEW or LISTVIEW. Than you can use a code fragment like:
private Context m_Context = activity.getBaseContext();
private ViewHolder m_Inflater = LayoutInflater.from(m_Context);
...
if (mode == GRIDTYPE)
viewDisplay = m_Inflater.inflate(R.layout.gridLayout, null);
} else {
viewDisplay = m_Inflater.inflate(R.layout.listLayout, null);