Android how to inflate ListView with two different items - android

I am trying to inflate listView with two different layouts using ArrayAdapter.
I want to show some user posts. If it contains a picture use one layout if it does not use second.
I am doing so far
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Post post = getItem(position);
boolean noImg = post.mImgUrl1 == null && post.mImgUrl2 == null;
if (convertView == null){
if (noImg)
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item_no_img, parent ,false);
else
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item, parent, false);
}
if (noImg == false) {
mTitle = (TextView) convertView.findViewById(R.id.tv_titlePost);
mText = (TextView) convertView.findViewById(R.id.tv_textPost);
mImage = (ImageView) convertView.findViewById(R.id.iv_postimg);
}
else {
mNoImgTitle = (TextView) convertView.findViewById(R.id.tv_noImgTitle);
mNoImgText = (TextView) convertView.findViewById(R.id.tv_noImgText);
}
if (noImg) {
mNoImgTitle.setText(post.mTitle);
mNoImgText.setText(post.mText);
if (post.mImgUrl1 != null)
Glide.with(mImage.getContext()).load(post.mImgUrl1).into(mImage);
else Glide.with(mImage.getContext()).load(post.mImgUrl2).into(mImage);
}
else {
mNoImgTitle.setText(post.mTitle);
mNoImgText.setText(post.mText);
}
return convertView;
}
But i realized that textviews from second layout cannot be found by findViewById function. And it always returns a null-pointer.
What are my solutions ? Should i make only one Layout and somehow trick it using visibility ? Or there is a way ?

There are a few different scenarios that you will see when getView() is called:
convertView is null: In this case just create the layout that you need depending upon noImg;
convertView is defined but is of the wrong type. In this case, you will need to discard the old view and create the one you need;
convertView is defined but is of the correct type, in which case, we do nothing with it.
The following code is one approach to making sure that you are always working with the right layout and can find the ids that you expect. This code uses a tag in the inflated layout to identify the layout type that can be checked.
if (convertView == null || noImg != (boolean) convertView.getTag()) {
// Either there is no view defined or it is the wrong type. Create
// the type we need.
if (noImg) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item_no_img, parent, false);
} else {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item, parent, false);
}
convertView.setTag(noImg);
}

With BaseAdapter you can create more complex layouts. Try using it.
Here is the example of use for your situation:
Activity:
public class TestActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] data={"false","false","true","true","false","true"};
ListView listViewTest=(ListView)findViewById(R.id.listViewTest);
TestAdapter testAdapter=new TestAdapter(data);
listViewTest.setAdapter(testAdapter);
}
}
Adapter:
public class TestAdapter extends BaseAdapter {
private String[] data;
TestAdapter(String data[]) {
this.data = data;
}
#Override
public int getCount() {
return data.length;
}
#Override
public Object getItem(int i) {
return data[i];
}
#Override
public long getItemId(int i) {
return i;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (data[position].equals("false")) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_no_img, parent, false);
} else if (data[position].equals("true")) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_img, parent, false);
}
return convertView;
}
}
Layouts:
activity_main
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/listViewTest"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
list_item_no_img
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewTest"
android:gravity="center"
android:layout_centerInParent="true"
android:textSize="32sp"
android:text="Test"/>
</RelativeLayout>
list_item_img
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewTest"
android:gravity="center"
android:layout_centerInParent="true"
android:textSize="32sp"
android:text="Test"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#mipmap/ic_launcher"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/textViewTest"
android:layout_toEndOf="#+id/textViewTest" />
</RelativeLayout>
Result:

Related

The TextViews populated in my GridView appear blank

I'm using a Gridview to show a list of skills. The code runs fine, even populates exact number of items according to my array. However, the TextView that should display the item names is blank.
This is the code for my gridview adapter
SkillsAdapter.java
public class SkillsAdapter extends BaseAdapter {
private Context context;
private final String[] skillValues;
public SkillsAdapter(Context context, String[] skillValues) {
this.context = context;
this.skillValues = skillValues;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.skills_single_item, null);
// set value into textview
textView = (TextView) gridView.findViewById(R.id.single_label);
textView.setText(skillValues[position]);
} else {
gridView = (View) convertView;
}
return gridView;
}
#Override
public int getCount() {
return skillValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
}
This is the code for my layout file skills_single_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp"
android:background="#drawable/rounded_corners">
<TextView
android:id="#+id/single_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_gravity="center"/>
</RelativeLayout>
The array is not null. The error occurs somewhere around here
// set value into textview
textView = (TextView) gridView.findViewById(R.id.single_label);
textView.setText(skillValues[position]);
Change your layout Item XML with this your I used your Adapter class its work fine at my side
it may help you
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:id="#+id/single_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="8dp"
android:textColor="#000000" />
</RelativeLayout>
Set test in your text view out of if condition like:
if (convertView == null) {
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.skills_single_item, null);
// set value into textview
textView = (TextView) gridView.findViewById(R.id.single_label);
} else {
gridView = (View) convertView;
}
textView.setText(skillValues[position]);
Move following line out of if-else blocks
textView.setText(skillValues[position]);
Like
if (convertView == null) {
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.skills_single_item, null);
textView = (TextView) gridView.findViewById(R.id.single_label);
} else {
gridView = (View) convertView;
}
// set value into textview
textView.setText(skillValues[position]);
I recommend you to read android ViewHolder pattern.

set imageview onclick of current page in viewpager

Hello guys I am using viewpager in my application,
On a page there is one imageview. on this image view i set onclick listener.
When i am going to click on this imageview and change image on it, image of next page is get changed and current page image is remain unchanged.
How to change only image of current Page not next.
Here is my code.
public class KahawateViewPager extends android.support.v4.view.PagerAdapter{
Context kahawateViewPagerContext;
ImageView kahawateLikeUnlike_ImageView;
TextView kahawateIdiom_TextView;
TextView kahawateIdiomCountry_TextView;
LayoutInflater kahawateViewPagerLayout_LayoutInflater;
public KahawateViewPager(Context context)
{
this.kahawateViewPagerContext = context;
}
#Override
public int getCount()
{
//set page count only for testing
return 10;
}
#Override
public Object instantiateItem(ViewGroup container, final int position)
{
kahawateViewPagerLayout_LayoutInflater = (LayoutInflater) kahawateViewPagerContext.getSystemService(kahawateViewPagerContext.LAYOUT_INFLATER_SERVICE);
final View itemView = kahawateViewPagerLayout_LayoutInflater.inflate(R.layout.kahawate_viewpager_layout, container, false);
kahawateIdiom_TextView = (TextView) itemView.findViewById(R.id.textview_idieom);
kahawateIdiomCountry_TextView = (TextView) itemView.findViewById(R.id.textview_ideiomCountry);
kahawateLikeUnlike_ImageView = (ImageView) itemView.findViewById(R.id.imageView_favourite);
setKahawateData(position);
kahawateLikeUnlike_ImageView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(kahawateViewPagerContext,"Current Position"+position,Toast.LENGTH_LONG).show();
if(kahawateLikeUnlike_ImageView.getTag().equals("like"))
{
kahawateLikeUnlike_ImageView.setImageResource(R.drawable.unlike);
kahawateLikeUnlike_ImageView.setTag("unlike");
} else
{
kahawateLikeUnlike_ImageView.setImageResource(R.drawable.like);
kahawateLikeUnlike_ImageView.setTag("like");
}
}
//}
});
((ViewPager) container).addView(itemView);
return itemView;
}
#Override
public boolean isViewFromObject(View view, Object object)
{
return view == ((LinearLayout)object);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
// Remove viewpager_item.xml from ViewPager
((ViewPager) container).removeView((LinearLayout) object);
}
void setKahawateData(int ID)
{
//setdefault image only for testing
kahawateLikeUnlike_ImageView.setImageResource(R.drawable.like);
kahawateLikeUnlike_ImageView.setTag("like");
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
here is my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#f4e9cd">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_weight="1"
android:gravity="right"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView_favourite"
android:layout_gravity="right"
android:src="#drawable/unlike"
android:clickable="true"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="9"
android:orientation="vertical"
android:paddingLeft="5dp"
android:paddingRight="5dp">
<TextView
android:id="#+id/textview_idieom"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|center_vertical"
android:layout_weight="8"
android:gravity="center"
android:text="Big Data"
/>
<TextView
android:id="#+id/textview_ideiomCountry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_weight="2"
android:gravity="right"
android:text="Small Data"
android:paddingRight="5dp"/>
</LinearLayout>
here is screen the star image i want to set/reset
Here toast current position 0 is showing but when i click the position 1 page imageview get set. and if I click on page1 image page 2 imageview get set and so on.
How set/reset current pages imageview?
Try implementing getItemPosition method like this:
#Override
public int getItemPosition(Object object) {
return mDataSource.indexOf(object);
}
This way you'll get the position of each item correctly
I think you have to set viewpager.setOffsetLimit(totalnumberofpage);
where totalnumberofpage is the count of list
Second thing is make ViewHolder class
private class ViewHolder {
TextView textViewItem;}
and use it like this way:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
// The convertView argument is essentially a "ScrapView" as described is Lucas post
// http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
// It will have a non-null value when ListView is asking you recycle the row layout.
// So, when convertView is not null, you should simply update its contents instead of inflating a new row layout.
if(convertView==null){
// inflate the layout
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(layoutResourceId, parent, false);
// well set up the ViewHolder
viewHolder = new ViewHolderItem();
viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);
// store the holder with the view.
convertView.setTag(viewHolder);
}else{
// we've just avoided calling findViewById() on resource everytime
// just use the viewHolder
viewHolder = (ViewHolderItem) convertView.getTag();
}
// object item based on the position
ObjectItem objectItem = data[position];
// assign values if the object is not null
if(objectItem != null) {
// get the TextView from the ViewHolder and then set the text (item name) and tag (item ID) values
viewHolder.textViewItem.setText(objectItem.itemName);
viewHolder.textViewItem.setTag(objectItem.itemId);
}
return convertView;}

Android SherlockListFragment list adapter with custom layout for each item

I have a SherlockListFragment to display a list with certain information if is available on a fetched JSONObject. There are four types of information: phone, website, location and opening hours, and each one of those has its own "icon" to be displayed on the list. So, my problem is how to display custom layouts for each one of the types, and also how can I add and delete items from the list programmatically.
Thanks in advance
Create interface (or abstract class), lets say:
public interface DataType{
public MyType getType(); //where MyType is my some enum.
}
public enum MyType { TYPE_1(R.layout.layout1), TYPE_2(R.layout.layout2), TYPE_3(R.layout.layout3)
private resourceId;
MyType(int resource){
resourceId = resource;
}
public getResourceLayout(){
return resourceId;
}
};
In your class implement this interface like:
public class MyData1 implement DataType{
//...
#Override
public MyType getType(){
return TYPE_1
}
}
public class MyData2 implement DataType{
//...
#Override
public MyType getType(){
return TYPE_2
}
}
etc...
Make your adapter take objects of type DataType.
In your getView() of your adapter decide which layout to inflate something like:
public View getView(..... ){
//.....
if(convertView==null){
convetView = mLayoutInflater.inflate(dataAtThatPosition.getResourceLayout(), null);
//.....
}
// use getType() to evaluate further actions if needed based on the type
}
Another approach is leaving interface/abstract class behind and decide which layout to inflate based on the assignation of the class. In that case your adapter will take generic type T, when deciding which layout to inflate you will have to do something like this in your getView():
public View getView(..... ){
T data = getItemAtPosition(position);
if(convertView==null){
convertView = mLayoutInflater.inflate((data.isAssignableFrom(MyData1.class)? R.layout.layout1 : data.isAssignableFrom(MyData2.class)? R.layout.layout1 : R.layout.layout3), null);
}
}
Personally I think the second approach is quite dirty. :)
I hope this gives you idea. :)
Your best bet would be to handle it in the getView(int, View, ViewGroup) method of your ListAdapter. If you construct the adapter with a context and JSONObjects (in an array or JSONArray) it's pretty simple using each JSONObjects' has(String) method to check which it is. Just inflate a different layout depending on which the object has.
The getView(int, View, ViewGroup) method in your Adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
jsonObject = json.getJSONObject(position);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (jsonObject.has("phone")) {
convertView = inflater.inflate(R.layout.list_item_phone, null);
TextView details = (TextView) convertView.findViewById(R.id.item_message);
details.setText(jsonObject.getString("phone"));
} else if (jsonObject.has("website")) {
convertView = inflater.inflate(R.layout.list_item_website, null);
TextView details = (TextView) convertView.findViewById(R.id.item_message);
details.setText(jsonObject.getString("phone"));
} else if (jsonObject.has("location")) {
convertView = inflater.inflate(R.layout.list_item_location, null);
TextView details = (TextView) convertView.findViewById(R.id.item_message);
details.setText(jsonObject.getString("phone"));
} else if (jsonObject.has("opening_hours")) {
convertView = inflater.inflate(R.layout.list_item_opening_hours, null);
TextView details = (TextView) convertView.findViewById(R.id.item_message);
details.setText(jsonObject.getString("phone"));
}
} catch (JSONException e) { e.printStackTrace(); }
return convertView;
}
You will probably want to do more in each if statement, like add click listeners or whatever... unless you have autoLink on your views. If you're relying on autoLink to resolve the clickability of the data then you wouldn't need to inflate different layouts, you could just use switch the icon instead.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
jsonObject = json.getJSONObject(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, null);
((TextView) convertView.findViewById(R.id.item_message)).setAutoLinkMask(Linkify.ALL); //autoLink phone, address, etc.
}
ImageView icon = (ImageView) convertView.findViewById(R.id.item_type_icon);
if (jsonObject.has("phone")) {
icon.setImageResource(R.id.phone_icon);
} else if (jsonObject.has("website")) {
icon.setImageResource(R.id.website_icon);
} else if (jsonObject.has("location")) {
icon.setImageResource(R.id.location_icon);
} else if (jsonObject.has("opening_hours")) {
icon.setImageResource(R.id.opening_hours_icon);
}
} catch (JSONException e) { e.printStackTrace(); }
return convertView;
}
EDIT: Since it seems like you may need the separate views dynamically...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
jsonObject = json.getJSONObject(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, null); //This should have a linear layout with icons and textview for each node...
}
if (jsonObject.has("phone")) {
LinearLayout phoneLayout = (LinearLayout) convertView.findViewById(R.id.phone_layout); //This layout should already have the appropriate icon in it //*/
phoneLayout.setVisibility(View.VISIBLE); //Necessary because the convert view doesn't guarantee what state this will be in as we've been modifiying them. //*/
((TextView) phoneLayout.findViewById(R.id.message)).setText(jsonObject.getString("phone"));
} else { ((LinearLayout) convertView.findViewById(R.id.phone_layout)).setVisibility(View.GONE); }
if (jsonObject.has("website")) {
LinearLayout websiteLayout = (LinearLayout) convertView.findViewById(R.id.website_layout);
websiteLayout.setVisibility(View.VISIBLE);
((TextView) websiteLayout.findViewById(R.id.message)).setText(jsonObject.getString("website"));
} else { ((LinearLayout) convertView.findViewById(R.id.website_layout)).setVisibility(View.GONE); }
if (jsonObject.has("location")) {
LinearLayout locationLayout = (LinearLayout) convertView.findViewById(R.id.location_layout);
locationLayout.setVisibility(View.VISIBLE);
((TextView) locationLayout.findViewById(R.id.message)).setText(jsonObject.getString("location"));
} else { ((LinearLayout) convertView.findViewById(R.id.location_layout)).setVisibility(View.GONE); }
if (jsonObject.has("opening_hours")) {
LinearLayout openingHoursLayout = (LinearLayout) convertView.findViewById(R.id.opening_hours_layout);
openingHoursLayout.setVisibility(View.VISIBLE);
((TextView) openingHoursLayout.findViewById(R.id.message)).setText(jsonObject.getString("opening_hours"));
} else { ((LinearLayout) convertView.findViewById(R.id.opening_hours_layout)).setVisibility(View.GONE); }
} catch (JSONException e) { e.printStackTrace(); }
return convertView;
}
list_item.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:id="#+id/phone_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_list_item_phone" />
<TextView
android:id="#+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout
android:id="#+id/website_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_list_item_website" />
<TextView
android:id="#+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<!-- ETC. A layout for each item. -->
</LinearLayout>

findViewByID returning null on BaseAdapter

I started using fragments to develop a new app, one of the fragments is a ListFragment,
when I use the adapter I made to show the list's rows I get some null from some findViewById that I use on the getView() method.
The structure is:
activity_main.xml
<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"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/fragment_container">
</FrameLayout>
</RelativeLayout>
frag_detail.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#android:id/list"
android:layout_gravity="center"/>
</LinearLayout>
detail_list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/detailItemLayout" >
<ImageButton
android:layout_width="64dp"
android:layout_height="64dp"
android:id="#+id/imgIcon"
android:layout_below="#+id/txtTitle"
android:layout_centerHorizontal="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="txtTitle"
android:id="#+id/txtTitle"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="label1"
android:id="#+id/label1"
android:layout_below="#+id/imgIcon"
android:layout_alignParentLeft="true"/>
</RelativeLayout>
DetailFragment class
public class DetailFragment extends ListFragment {
private Context context;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_detail,container,false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
MonsterDataSource ds = new MonsterDataSource(context);
List<Monster> lsMonster = ds.getAllMonsters();
//ListView lsView = (ListView)this.getActivity().findViewById(R.id.lsDetail);
MonsterListAdapter adapter = new MonsterListAdapter(context,lsMonster);
//lsView.setAdapter(adapter);
setListAdapter(adapter);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
context = activity.getApplicationContext();
}
}
And the MonsterListAdapter class
public class MonsterListAdapter extends BaseAdapter {
static class ViewHolder {
private TextView txtTitle;
private ImageView imgIcon;
private TextView txtHealth;
}
private Context context;
private LayoutInflater inflater;
private List<Monster> data;
private ViewHolder holder;
public MonsterListAdapter(Context c, List<Monster> data){
context = c;
this.data = data;
inflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/*Basic BaseAdapter Methods*/
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = inflater.inflate(R.layout.menu_list_item, null);
holder = new ViewHolder();
/*Those Guys get null*/
RelativeLayout layout = (RelativeLayout)view.findViewById(R.id.detailItemLayout);
holder.txtTitle = (TextView) view.findViewById(R.id.txtTitle);
holder.imgIcon = (ImageView) view.findViewById(R.id.imgIcon);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
Monster monster = data.get(i);
holder.txtTitle.setText(monster.name);
holder.imgIcon.setImageResource(monster.imgResID);
holder.txtHealth.setText(monster.health);
return view;
}
}
In case anyone asking, I'm trying to retrieve the relative layout to add some controls dynamically after.
Thanks
You posted the XML for detail_list_item.xml but in your code you inflate menu_list_item.xml.
detail_list_item.xml contains all of the IDs you are trying to find so it appears you have accidentally inflated the wrong View in getView
Additionally, you should remove your ViewHolder as a member variable and place it as a local variable in the getView method. You are reusing that variable for multiple views at the same time which means you have the potential for modifying the wrong views at different positions (data going to the wrong positions, etc).
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder holder;
...
...//all of your other code here
}
Try without affecting "view"
RelativeLayout layout = (RelativeLayout)findViewById(R.id.detailItemLayout);
holder.txtTitle = (TextView) findViewById(R.id.txtTitle);
holder.imgIcon = (ImageView) findViewById(R.id.imgIcon);
Edit This works for me... My texviews nbCommentaires and Comment give NullPointerException if i inflate View to them... I thought you get the same issue...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.comment_row, null);
}
v.setTag(position);
Commentaires o = Global_reco.CommRecos.get(position);
if (o != null) {
TextView nom = (TextView) v.findViewById(R.id.nom_com);
TextView com = (TextView) v.findViewById(R.id.com);
ImageView photo = (ImageView) v.findViewById(R.id.profile_com);
TextView nbCommentaires = (TextView) findViewById(R.id.nb_commentaires);
TextView Comment = (TextView)findViewById(R.id.nbre_comment);
photo.setTag(position);

Android ListView throwing NullPointerException when I try to scroll

Not exactly sure what is going on here so I am hoping one of you can point me in the right direction. I have a ListView within a Tab frame and for some reason, it loads but crashes when I try to scroll.
Here is all of the information I could gather
Here is the Activity that goes into the frame... it parses an XML file into a priority queue, gets the attached intent information, and loads the desired page.
public class QScoresFrameActivity extends ListActivity
{
private PriorityQueue<Score> mScores;
private int mDifficulty;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.scoresframe);
mDifficulty = getIntent().getIntExtra(getResources().getString(R.string.difficulty), getResources().getInteger(R.integer.normal_level));
mScores = QGameGlobals.ParseScoresXML(this,mDifficulty);
setListAdapter(new ScoresAdapter(this));
}
private class ScoresAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
public ScoresAdapter(Context context)
{
mInflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
convertView = mInflater.inflate(R.layout.scoreview, null);
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
public int getCount()
{
return mScores.getLength();
}
public Object getItem(int position)
{
return this;
}
public long getItemId(int position)
{
return position;
}
}
}
The layout file that holds the list:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<ListView android:layout_height="wrap_content" android:layout_width="match_parent" android:id="#+android:id/list" />
</LinearLayout>
The layout file for each item in the list:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="#+id/linearLayout1" android:padding="10dp" android:layout_width="fill_parent" android:baselineAligned="false" android:orientation="horizontal" android:layout_height="wrap_content">
<TextView android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="TextView" android:paddingRight="10dp" android:id="#+id/position" android:layout_width="50dp" android:textColor="#color/teal" ></TextView>
<TextView android:id="#+id/scorerName" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="TextView" android:layout_width="wrap_content" android:layout_gravity="center_vertical"></TextView>
<TextView android:id="#+id/scorerScore" android:text="TextView" android:textAppearance="?android:attr/textAppearanceLarge" android:paddingLeft="20dp" android:layout_height="wrap_content" android:gravity="right" android:textColor="#color/gold" android:layout_width="match_parent" android:layout_gravity="center_vertical"/>
</LinearLayout>
Priority Queue class....
#SuppressWarnings("rawtypes")
public class PriorityQueue<T extends Comparable> extends Queue<T>
{
public PriorityQueue ()
{
super();
}
#SuppressWarnings("unchecked")
#Override
public void enqueue(T item)
/* Enqueue a new object of type T into the queue based on its compareTo method.
* Sorts from greatest to least.
*/
{
TNode newNode = new TNode(item);
if (root == null)
{
root = newNode;
end = newNode;
}
else
{
TNode currNode = root;
TNode lastNode = root;
while (true)
{
int compVal = item.compareTo(currNode.get());
if (compVal == 1 || compVal == 0)
{
if (currNode == root)
{
root = newNode;
}
else
{
lastNode.next = newNode;
newNode.next = currNode;
}
newNode.next = currNode;
break;
}
else
{
if (currNode == end)
{
currNode.next = newNode;
end = newNode;
break;
}
else
{
lastNode = currNode;
currNode = currNode.next;
}
}
}
}
length++;
}
}
Appreciate the help.
A stack trace here would be most helpful. However, offhand, I would guess that the problem is you are storing the inflator in the adapter's constructor. Instead, just call getLayoutInflator() inside your getView() method. If that doesn't resolve it, please do post the logcat.
Just gonna guess because I ran into this when I started out with Android...
I know the documentation says to name your list android:id="#+android:id/list" but try android:id="#id/android:list" instead also if it just so happens that you're starting with no info (empty list) define a view with android:id="#id/android:empty" as a placeholder.
The problem is with your getView() function in your custom listadapter (ScoreAdapter). When you scroll, the listview recycles your views and calls the getView() function. Change you getView() function to this to fix it:
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
if (convertView == null)
{
LayoutInflater mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.scoreview, null);
}
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
Ahh yes, your problem is definitely in the PriorityQueue. That enqueue method is all sorts of broken, sorry. Get out a pencil and paper and walk through the scenarios of adding in the middle of the queue. I'm pretty certain that I see at least one bug, and possibly as many as three.
--randy

Categories

Resources