I have a ListView to show a list of articles. Each ListView element is a LinearLayout. On each article, there is a TextView button to edit the title (another TextView). However, the button and the title are not under a direct parent (and actually not at the same level).
A sample structure is shown below:
<LinearLayout>
<LinearLayout
android:id="#+id/title_zone">
<TextView
android:id="#+id/title">
<ImageView
android:id="#+id/icon_popularity">
</LinearLayout><!--end of title_zone-->
<LinearLayout
android:id="#+id/content_zone">
...
</LinearLayout><!--end of content_zone-->
<LinearLayout
android:id="#+id/button_zone">
<LinearLayout
android:id="#+id/author_buttons">
<TextView
android:id="#+id/edit_title_button"
android:onClick="editTitle">
...
</LinearLayout><!--end of author_buttons-->
</LinearLayout><!--end of button_zone-->
</LinearLayout>
I write a SimpleAdapter to apply data to views, so the root LinearLayout will have a tag of the article ID. When editTitle() is called, it needs to find its parent's parent's parent to the root. And after new title is entered, a message will send to server containing new title and the article ID. Also, the title text will be changed visually, which means I need to find the title TextView based on the root.
The problem is that this querying root process is tightly coupled to the UI structure. If I changed the structure in XML, I need to pay attention to change the querying code in Java. (The querying title view is relatively easy, if the root is obtained.)
Is there a more maintainable way to implement my purpose?
OK. A solution that works for my situation is as follows:
In the customized simple adapter for this ListView, set the article ID as the edit_title_button's tag. And set the article ID composed string as the root LinearLayout's tag, e.g., "a_123" if the article ID is 123.
So, when editTitle(View v) is called, we can get the story ID by v.getTag(). Of course, we can also get the ListView by its ID (e.g., ListView list=findViewById(R.id.my_list_view);). Then, we can simply get the root LinearLayout by list.findViewWithTag("a_123").
Since the root LinearLayout is obtained without knowing the UI structure, following codes are easy to maintain.
Related
I have an android application where not all elements have unique ID's. For example, two TextViews are each called "itemButton" and are on the same screen. I want to give every element a unique identifier by setting a tag on each element.
My current solution is to iterate through every element in the application and set the tag for each element. This is a very expensive solution because I have many elements. Is there another property you know of that would help identify an element other than setting a unique tag for each element?
View IDs have no special requirement that they be unique. However, you will run into difficulties if you use non-unique IDs on a single screen.
The two most common issues you will face if you use non-unique IDs are (1) failure of the system to automatically save instance state for the view and (2) findViewById() returning the "wrong" view.
Activity.findViewById() will search the view hierarchy for the first view it finds with the matching ID. If you have two views in your hierarchy with the same ID, that means you won't be able to find the second one using this method. However, you can use View.findViewById() instead.
View.findViewById() will search the view hierarchy starting from the view you're invoking the method on, which means that you can differentiate between two views with the same ID as long as they have different parents.
In your case, I suspect you can do something like the following:
View parentOne = findViewById(R.id.parentOne);
View childOne = parentOne.findViewById(R.id.someIdBeingReused);
View parentTwo = findViewById(R.id.parentTwo);
View childTwo = parentTwo.findViewById(R.id.someIdBeingReused);
I don't think you got it right, you should assign unique ids to your elements by using in each of them: android:id="#+id/YOUR_ID", then you can find them with findViewById(R.id.YOUR_ID), so, if you have two text views, lets say, username and password, you set the ids on each:
<TextView android:id="#+id/username" .../>
<TextView android:id="#+id/password" .../>
and then you get them in your activity or fragment with:
TextView txtUsername = findViewById(R.id.username);
TextView txtPassword = findViewById(R.id.password);
Do Android's R.id need to be unique (as I've kept them so far)?
Or is it OK to reuse an R.id across different views?
ID's are used for finding views inside a view hierarchy. Android uses depth-first search algorithm - meaning it looks to the very bottom of one tree branch, then to another one etc. If you have two views with same ID, then the algorithm will find first view and stop searching further.
There is no strict requirement on uniqueness of ID. For instance, when you have a list view, then each item of that list will be inflated using same layout and in most cases will have same shared ID, which is totally ok.
Keeping that in mind, if you have two (or more) views sharing same ID, you should help Android to pick up the right one. For that you will first need to search for the correct parent of that view, and then for the view itself.
For instance, if you have two views with the same ID in two different fragments, then you should first search for fragment container view, and then for the view with shared ID inside that container.
Yes, it is OK to have the same ID in different layouts. You could find more info here: http://developer.android.com/guide/topics/ui/declaring-layout.html
Any View object may have an integer ID associated with it, to uniquely identify the View within the tree. When the application is compiled, this ID is referenced as an integer, but the ID is typically assigned in the layout XML file as a string, in the id attribute.
In a relative layout, sibling views can define their layout relative to another sibling view, which is referenced by the unique ID.
An ID need not be unique throughout the entire tree, but it should be unique within the part of the tree you are searching (which may often be the entire tree, so it's best to be completely unique when possible).
It is ok to use same id with different layouts.
For eg :
a.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a);
b.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.b);
a.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/id_rel"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp"
>
b.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/id_rel"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp"
>
But using same id in one layout can cause exception
For most cases you can reuse id's in different layouts. But you should be aware about possibility of including one layout into another using , tags or using custom compund views as well as list items or fragments added to current view.
I found two SO threads that tell how to center title and message in an AlertDialog object and faked my way through writing a method that I hope to be able to call to center any AlertDialog. It worked fine on a phone and a tablet to display even multi-line messages, both with and without '\n's.
public void showCenteredInfoDialog(TextView _title, TextView _message) {
_title.setGravity(Gravity.CENTER);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setPositiveButton("OK", null);
builder.setCustomTitle(_title);
builder.setMessage(_message.getText());
AlertDialog dialog = builder.show();
TextView messageView = (TextView)
dialog.findViewById(android.R.id.message);
messageView.setGravity(Gravity.CENTER);
}
I did a considerable amount of customizing--i.e., I have SOME clue about what I found and did--but one line has left me wondering:
TextView messageView = (TextView) dialog.findViewById(android.R.id.message);
What is android.R.id.message?
Here is all the documentation I could find about it:
android.R.id
public static final int message = 16908299
Where can I find more documentation for the Android.R.id objects (and more)? This seems to be a possible gold mine.
In Android, views contained in layouts generally (though not always) have an id. The purpose of this id is to be able to identify particular views, for example:
Button button = (Button)layout.findViewById(R.id.button1);
button.setOnClickListener(...);
When you create a layout XML file, you're generally creating new ids for your views, the syntax is:
<Button
android:id="#+id/button1"
...
This will create an integer value in your project's R file (R.id.button1).
android.R.id, on the other hand, contains the ids of views that are either defined in the Android framework, or must be somehow referenced by it.
In your example, the AlertDialog.Builder creates a TextView with a fixed id, android.R.id.message. That way, you can take the view hierarchy returned by show(), and find the TextView inside it.
You can take a look at the full list of predefined ids in the documentation, however this list is not very informative in itself. The ids are generally mentioned in the documentation for each particular feature that uses them.
As an example of the other use case (marking your own view with a predefined android id), when using a ListFragment, if you provide a custom layout then you must include a ListView with id R.id.list. This is because the ListFragment class inspects the inflated layout to look for this widget. See the documentation:
ListFragment has a default layout that consists of a single list
view. However, if you desire, you can customize the fragment layout by
returning your own view hierarchy from onCreateView(LayoutInflater,
ViewGroup, Bundle). To do this, your view hierarchy must contain a
ListView object with the id "#android:id/list" (or list if it's in
code)
Optionally, your view hierarchy can contain another view object of any
type to display when the list view is empty. This "empty list"
notifier must have an id "android:empty".
android.R.id.message refers to the Resource with an id named message.
For example, the TextView here:
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="#+id/backbutton"
android:text="Back"
android:layout_x="10px"
android:layout_y="5px"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/message"
android:layout_x="10px"
android:layout_y="110px"
android:text="First Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</AbsoluteLayout>
Excuse me when I tell you that if you didn't know that, you really should read the Android Training Guide: http://developer.android.com/training/index.html
#matiash 's answer gives some good insight.
In the simplest terms, suppose you were providing a custom layout for a dialog, you would just access a view using R.id.viewId - an id which you defined in the custom xml layout.
android.R.id.message will let you access views that are predefined by android. Such as in your case, you're using an AlertDialog with a predefined layout. So, using the particular id you'll be able to access the TextView where you can set a message in the AlertDialog.
I want this type of layout how i can do this
all the titles should be clickable
please help me
I used Button and TextView but it seems very dull
Inflate a LinearLayout with a TextView and an image (the right arrow). That LinearLayout must have clickable="true" and onClick="methodYouLike"
To identify what is being clicked you can add a tag to each, with an id, like android:tag="1", android:tag="2" ...
On the Activity onClick receives a View, só you can get that view, an obtain the tag, and do what you need.
If you are not content with the standard UI you can always write your own custom widgets. See here for some more information.
Aso if you need not only 4 items but many, you can use ListView You can provide a layout resource id and it will be atomatically inflated into each row. Datasource could be as DB so array and so on.
How yo set listview background like this.
I want to appear when the number of record 0
There is special method in ListView - setEmptyView(). You can find examples of using it here or here.
Upd: second link is unavailable now. Here is quote from article:
When you set a ListView’s “empty view” programmatically, you can end up scratching your head as to why your empty view actually doesn’t appear when the list is empty.
If this happens, then what you forgot is that you must manually add your empty view to your view hierarchy, cos ListView won’t do it for you. Although it’s obvious when you think about it, the documentation doesn’t mention this detail, and Googling shows at least one person had the problem.
Here’s the code with the lines that it’s all too easy to forget at numbers 4 and 5…
TextView emptyView = new TextView(context);
emptyView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
emptyView.setText(“This appears when the list is empty”);
emptyView.setVisibility(View.GONE);
((ViewGroup)list.getParent()).addView(emptyView);
list.setEmptyView(emptyView);
Just set a background image at the parent layout and then set the color of the ListView to fully transparent:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
style="#style/Main" android:background="#drawable/background">
<ListView android:cacheColorHint="#00000000" .../>
</LinearLayout>
Read the documentation of the ListActiviy. You can define a view which will automatically shown when the list is empty and has not items. The view for the empty list got to have the id android:id/empty.
So no need to play around with the background.
You can set a drawable as background with ListView.setBackgroundDrawable()
You need to check before passing array/arraylist into adapter ,if the length of array/arraylist is 0 then add this image to your main layout.