Inflating a View Inside a Runnable - android

I am attempting to write code that would allow me to have 1-2 views at the top of a listview that are of a different arrangement than the other views in the listview. I decided to try the method of using addHeaderView() for the 1-2 views that will differ in display.
I have two xml layout files, one that defines the view format that most of the listview views will fall under (list_image_checkbox_row.xml) and one for the Header Views (catalog_featured_row.xml). The adapter constructor uses list_image_checkbox_row as its resource in the constructor.
I used a Thread to set the adapter and load the views. I am able to programmatically create views and use addHeaderView to at them to the listview (using the same image resource), but I get errors when I try to use addHeaderView on a view I have inflated from a layout xml file.
handler.post(new Runnable() {
#Override
public void run() {
lv = (ListView)findViewById(R.id.sources_list);
Activity context = BrowseSourceActivity.this;
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.catalog_featured_row, null);
ImageView feat_view = (ImageView) view.findViewById(R.id.image);
feat_view.setImageResource(R.drawable.arrow);
lv.addHeaderView(feat_view);
setListAdapter(array);
}
});
I get a force close error and haven't been able to find anything in logcat (when I was trying to debug yesterday I got a NullPointerException). If I comment out the lv.addHeaderView(feat_view) line, the application does not force close on me. Here is the code for featured_catalog_row.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="70dip"
android:padding="5dip">
<ProgressBar android:id="#+id/spinner"
android:indeterminate="true"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_centerInParent="true"
/>
<ImageView android:id="#+id/image"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
Any suggestions for how to get the inflater to work?

Perhaps you should store lv in an instance variable instead of looking it up dynamically. Note that both View and Activity have a method called findViewById, and so your results will depend on how your code is organized.

Related

Can't remove or hide a view in Android

I'm trying to remove a progress indicator after loading my data in a fragment involving a ListView. Here is my completion handler:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
dataSource = (ArrayList<Map<String, Object>>) task.getResult();
PostAdapter adapter = new PostAdapter(getActivity(), dataSource);
ListView list = (ListView) rootView.findViewById(R.id.listView);
list.setAdapter(adapter);
View indicator = getActivity().findViewById(R.id.indicator);
RelativeLayout layout = (RelativeLayout) getActivity().findViewById(R.id.layout);
layout.removeView(indicator);
}
});
The last 3 lines of code is the relevant part. Everything is called correctly, nothing is null etc. in debug everything works perfectly. The adapter also works correctly, populating my list, but the indicator is still on the screen. I've also tried setting it's visibility to GONE or HIDDEN but they also don't seem to hide it either. I've seen Android - Can't hide progress bar but it's answers involve setEmptyView() which I'm not using anyway. I am using the same fragment (of course, a different instance) in another tab, and it works correctly.
Here is my layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp"
android:background="#ffffffff"
android:id="#+id/layout">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView"
android:layout_margin="0dp"
android:padding="0dp" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/indicator"
android:indeterminate="true"
android:layout_centerInParent="true" />
</RelativeLayout>
What am I doing wrong?
I've found out the problem (thanks to all the commenters). I was calling activity's methods to find and remove the view. The problem is that, I have multiple instances of the same fragment in the same activity, under different tabs. I've used my fragment's root view to find and remove the indicator, instead of the activity, and it worked.
Change the codes of last three lines in onClick(...) as:
View indicator = (ProgressBar)rootView.findViewById(R.id.indicator);
and use:
indicator.setVisibility(ProgressBar.GONE);
or codes:
RelativeLayout layout = (RelativeLayout) rootView.findViewById(R.id.layout);
layout.removeView(indicator);

ListView being drawn twice

So I am rather confused right now. I am using an XML layout so I can show an empty view like so:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<RelativeLayout
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/samples_empty"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" >
</ListView>
</RelativeLayout>
As you have probably seen a billion times here on so.
So I do setContentView(R.layout.foo) and it works the first time, but if I return to this Activity (As in onPause has been called and then onResume) I get this:
I call notifyDataSetChanged(); on the adapter and that works fine, what I don't get is why its being drawn twice?
Its not like I am creating a new ListView and then adding it to the view, I'm sure I would know about it if I was.
The getView method of the adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RecordView av;
if(convertView == null){
av = new RecordView(mCtx, this, mRecords[position], position);
}else{
av = (RecordView) convertView;
av.setRecord(mRecords[position]);
}
return av;
}
This would be what it would look like normally...
NOTE
This doesn't seem to be happening every single time, and doesn't happen on a single event happening, but when it does, its when I return from another screen.
UPDATE
I noticed that when I had another activity on top (something that was transparent, like facebook chat heads, then I could see that the problem had occurred then, It doesn't seem to happen on onResume, but more likely on onPause which I actually don't do anything in.
You have this problem because you are dynamically create the row view each time while the convertview still has the old view existing and it is being reused. To get around this problem, you should give an id to every view (that is, every child view in your RecordView)when you dynamically create them, for example a child textview in your RecordView class should be instantiated like this
TextView tv = new TextView(this.getContext());
tv.setId(1);
...
Then, in your getView:
if(convertView == null){
av = new RecordView(mCtx, this, mRecords[position], position);
}else{
av = (RecordView) convertView;
av.findViewById(1).setText(mRecords[position]);
}
assuming your mRecords holds an array of String. If you have variant layout for different rows, you can provide a type to each view. See this for further details.
try changing android:layout_height="wrap_content" of listview to android:layout_height="fill_parent"

Custom Listview Adapter, adding a static footer, and understanding R.id.list

There is something I'm just not getting, and I'm looking for assistance in understanding what is happening here.
I have a custom list adapter (that just extends BaseAdapter) that I have successfully been using to generate and display a list. Now I want to add a static footer to the bottom of my list. After looking at a number of resources (specifically this one) I've come to realize that my reluctance of using XML has to come to an end, and set up the following xml layout in a file called devices_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout android:id="#+id/bottom_control_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<ToggleButton android:id="#+id/bottom_control_toggle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textOff="Filter Favourites OFF"
android:textOn="Filter Favourites ON"/>
</LinearLayout>
<ListView android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_above="#id/bottom_control_bar">
</ListView>
<TextView android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/main_empty_list"
android:layout_above="#id/bottom_control_bar"/>
</RelativeLayout>
After some adjustments to the activity that holds the list, I ran the code. I see my footer, (and also the tab widget which is parent to everything), but the area where the list goes is empty.
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setContentView(R.layout.devices_list);
db = new DbManager(this);
db.open();
AllCur = db.fetchAllDevices();
startManagingCursor(AllCur);
list = new DeviceListAdapter(this, AllCur); //make my custom list adapter
setListAdapter(list);
}
Is there some way to link up the ListView widget declared in my xml with my DeviceListAdapter? It's pretty clear to me now that I'm not entirely sure about how this is all working. Any help in clarification would be much appreciated.
You have both the ListView and the TextView set to android:layout_above="#id/bottom_control_bar", which means the TextView will overlap the ListView. And, you have said that your ListView height is 0dip, which will make for an extremely short list.
I would define the ListView as being above the TextView and anchored to the top of the screen (android:layout_alignParentTop="true").
Is there some way to link up the
ListView widget declared in my xml
with my DeviceListAdapter?
You already are, by calling setListAdapter().

Android: failed to setContentView when switching to ListActivity

[update] I got the error, which says "Your content must have a ListView whose id attribute is 'android.R.id.list'". Appearently nothing in my xml is ListView. But is that required?
This is an follow-up issue on my previous question
android: which view should I use for showing text and image?
I read the article about creating ListView for LinearLayout. However, my following code failed at the setContentView() function when I changed "extends Activity" to "extends ListActivity", any idea why?
private TextView mSelection;
//private ImageView mImages;
static final String[] keywords = new String[]{"China", "Japan", "USA", "Canada"};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contactLayout);
mSelection = (TextView)findViewById(R.id.ContactNames);
ArrayAdapter adapter = new ArrayAdapter<String>(this, R.layout.contactlayout, R.id.ContactNames,keywords);
setListAdapter(adapter);
}
My Layout is from this article: http://www.curious-creature.org/2009/02/22/android-layout-tricks-1/
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="#drawable/icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/ContactNames"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="My Application" />
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:text="Simple application that shows how to use RelativeLayout" />
</LinearLayout>
I think you misunderstood the other posts I showed you in the previous question. They were explaining how to use a custom layout for each row in your list, not how to define the entire layout file for the activity. You need something like this:
(main.xml)
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:id="#android:id/list">
</ListView>
Note the very important line android:id="#android:id/list". You must have that in your ListView as that's what tells Android where your list is. The cacheColorHint is useful if your background isn't black - see this post for more details about that.
With the above lines you can give your activity a list that will be recognised properly. Here's a basic example:
public class TestProject extends ListActivity {
final static String[] ITEMS = {"blah", "floop", "gnarlp", "stuff"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.listrow, R.id.textview, ITEMS);
setListAdapter(adapter);
}
}
Then the listrow layout is just this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textview"/>
</LinearLayout>
This is a really simple layout. If you want to get something more complicated, changes are you'll have to use a BaseAdapter, as that gives you calls getView(...) before each row. In that you can use different layouts depending on the contents of each row. However, BaseAdapter looks scary when you first try it, so be warned! :)
Yes, if you are using a ListActivity, you need to have a ListView who's id is android.R.list in your layout file.
If you aren't using a ListView in your layout, and I don't see one in there, then switch to using a regular Activity.
Actually, your (custom) layout doesn't need a ListView when using a list activity. The easy way to solve this is just remove the setContentView() line altogether. In simple terms, when you do it, Android "assumes" the layout you're using to contain a single full-screen ListView, and provides it for you.
If you want a different (richer) interface for the Activity though, you must code the XML and use the informed ID for Android to know how to show the list implied by the activity being a ListActivity after all. Note that the layout for an item isn't the same as the list, and although I haven't tried that, I assume you can have a custom item layout without having an explicit ListView in the activity layout.

Is possible to set an listener just for a part of a custom view?

I want to create a custom view (extends View) which contains an image and a text.
Is it possible to set an listener just for image if i use canvas.drawBitmap(imageBitmap, ...) method?
Thank you!
If you want your view just to show an image and a text, why not create a specific layout in your XML file and use setContentView to display the view?
If thats what you were looking for, I think that is the easiest approach. XML example coming soon.
Here is the contents of my personal view to display my main application, just to demonstrate how I can switch view's, your view would be different including a TextView and an ImageView.
dblist.xml inside of res/layout folder.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF">
<ListView android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="#000000"/>
</LinearLayout>
This contents display my database results onto a Spinner
I can call that view by using:
setContentView(R.layout.dblist);
And if I want to return to my main layout, I can switch the view by calling
setContentView(R.layout.main);
EDIT:
You can manipulate the view you are working with programmatically by using findViewById method. Let's use my dblist.xml file as an example, notice that my ListView has an id of list. So I would call:
ListView myList = (ListView) findViewById(R.id.list);
With corresponds to that layout, so whenever I want to change anything to my XML file, I would reference myList use methods and callbacks to meet my needs.
I hope this helps.

Categories

Resources