I have a listview that uses a customadapter based on the baseadapter. The listview populates ok using external data and I can pick up the click events and know which item has been selected.
I'm having a problem updating the clicked item's views, like TextView and ViewFlipper.
Do I have to update something via the listview or is it via the adapter. I've tried things like the following;
View test = (View)adapter.getView(pos, null, myListView);
ViewFlipper temp = (ViewFlipper)test.findViewById(R.id.flipholder);
temp.showNext();
TextView temp2 = (TextView)test.findViewById(R.id.filmtitle);
temp2.setText("Hello World");
Which results in the viewfliper flipping the first and third item or second and fourth and the text not updating at all.
Any ideas?
Cheers
Try this pattern in getView:
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
static class ViewHolder {
TextView text;
ImageView icon;
}
}
Related
The get view method from the script below crashes everytime I ran the app (the error points to that method ) this is the error :
07-11 17:25:01.147 8512-8512/com.example.android.quakereport E/AndroidRuntime:
FATAL EXCEPTION: main
Process: com.example.android.quakereport, PID: 8512
android.content.res.Resources$NotFoundException: Resource ID #0x0
and this is the ArrayAdapter from the getview method code :
public class EarthQuakeAdapter extends ArrayAdapter<Earthquake> {
public EarthQuakeAdapter(Activity context, ArrayList<Earthquake> Earthquakes) {
// Here, we initialize the ArrayAdapter's internal storage for the context and the list.
// the second argument is used when the ArrayAdapter is populating a single TextView.
// Because this is a custom adapter for two TextViews and an ImageView, the adapter is not
// going to use this second argument, so it can be any value. Here, we used 0.
super(context, 0, Earthquakes);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItemView = convertView;
if(listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
return super.getView(position, convertView, parent);
}
Earthquake currentEarthquake = getItem(position);
TextView magnitude = (TextView) listItemView.findViewById(R.id.magnitude);
// Get the version name from the current AndroidFlavor object and
// set this text on the name TextView
magnitude.setText(Earthquake.getmMagnitude());
// Find the TextView in the list_item.xml layout with the ID version_number
TextView place = (TextView) listItemView.findViewById(R.id.Place); // Get the version number from the current AndroidFlavor object and
// set this text on the number TextView
place.setText(Earthquake.getMplace());
// Find the ImageView in the list_item.xml layout with the ID list_item_icon
TextView date = (TextView) listItemView.findViewById(R.id.Date);
date.setText(Earthquake.getmDate());
// Get the image resource ID from the current AndroidFlavor object and
// set the image to iconView
// Return the whole list item layout (containing 2 TextViews and an ImageView)
// so that it can be shown in the ListView
return listItemView;
}
I can add any code or ressource if needed just tell me on the comments.
You're never really returning the ViewHolder you inflated.
You shouldn't call super when the view is null. You should inflate it, get the child views, populate them and finally return the inflated root view.
Take a look at the docs to learn more about this process.
Try to write the adapter like this:
public class EarthQuakeAdapter extends ArrayAdapter<Earthquake> {
// ...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItemView = convertView;
if(listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
}
Earthquake currentEarthquake = getItem(position);
TextView magnitude = (TextView) listItemView.findViewById(R.id.magnitude);
// Get the version name from the current AndroidFlavor object and
// set this text on the name TextView
magnitude.setText(Earthquake.getmMagnitude());
// Find the TextView in the list_item.xml layout with the ID version_number
TextView place = (TextView) listItemView.findViewById(R.id.Place); // Get the version number from the current AndroidFlavor object and
// set this text on the number TextView
place.setText(Earthquake.getMplace());
// Find the ImageView in the list_item.xml layout with the ID list_item_icon
TextView date = (TextView) listItemView.findViewById(R.id.Date);
date.setText(Earthquake.getmDate());
// Get the image resource ID from the current AndroidFlavor object and
// set the image to iconView
// Return the whole list item layout (containing 2 TextViews and an ImageView)
// so that it can be shown in the ListView
return listItemView;
}
}
You can read more about smooth scrolling here on android's training page.
And as an option, you could switch to RecyclerView, you can read more about it here.
You shouldn't need to call super.getView(...) in the overridden getView function, and you're passing in an invalid resource ID. Simply remove that line, since you're not using its return value anyway:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItemView = convertView;
if(listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
}
...
}
I am trying to implement a Newsfeed-type layout with multiple feed items.
The newsfeed item would have a certain layout when collapsed, and this layout would be replaced by an 'exploded' version, when the item is clicked.
I accomplished this by using a ListView of custom items. The custom item XML layout file has a ViewStub which is what I used to change the layout back and forth.
Now, though, I wanted to 'migrate' the layout over to RecyclerView, and to also follow a ViewHolder design pattern.
The latter is what I have tried first, and I'm running into all sorts of problems.
My approach has been as follows:
Get reference to collapsed layout (events_list_item_content) and expanded layout (events_list_item_selected_content);
Get reference to a simple layout resource file to be set as the ViewStub layout (view_stub_layout).
Get ViewStub reference, set its layout (view_stub_layout) inflate, and add the collapsed layout view to this layout (when first creating the feed, all of its items are going to be collapsed).
(After initialisation, when an item is clicked) Remove previous view (layout) from the ViewStubLayout, add the other type of layout.
Here is my custom adapter class:
public class FeedRecyclerAdapter extends BaseAdapter {
public class ViewHolder {
View inflatedViewStub1;
ViewStub viewStub;
LinearLayout viewStubLayout;
LinearLayout listItemContent, listItemContentSelected;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final FeedItem item = feedItems.get(position);
final ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = inflater.inflate(R.layout.events_list_item_content_new_container, parent, false);
View view = null;
view = inflater.inflate(R.layout.events_list_item_content, null);
viewHolder.listItemContent = (LinearLayout) view.findViewById(R.id.events_list_item_content);
view = inflater.inflate(R.layout.events_list_item_selected_content, null);
viewHolder.listItemContentSelected = (LinearLayout) view.findViewById(R.id.events_list_item_content_selected);
view = inflater.inflate(R.layout.view_stub_layout, null);
viewHolder.viewStubLayout = (LinearLayout) view.findViewById(R.id.view_stub_layout);
viewHolder.viewStub = (ViewStub) convertView.findViewById(R.id.list_item_feed);
(viewHolder.viewStubLayout).addView(viewHolder.listItemContent);
viewHolder.viewStub.setLayoutResource(R.layout.view_stub_layout);
viewHolder.inflatedViewStub1 = viewHolder.viewStub.inflate();
convertView.setTag(viewHolder);
} else viewHolder = (ViewHolder) convertView.getTag();
if (item.getExploded()) {
viewHolder.viewStubLayout.removeAllViews();
viewHolder.viewStubLayout.addView(viewHolder.listItemContentSelected);
} else {
viewHolder.viewStubLayout.removeAllViews();
viewHolder.viewStubLayout.addView(viewHolder.listItemContent);
}
return convertView;
}
However, when testing, the page where the Newsfeed is supposed to appear is blank.
ViewStub stub = (ViewStub) findViewById(R.id.layout_stub);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
stub.setLayoutResource(layoutId);
stub.inflate(); // inflate 1st layout
ll.removeAllViews(); // remove previous view, add 2nd layout
ll.addView(LayoutInflater.from(context).inflate(secondLayoutId, ll, false));
Android ViewStub change layouts programatically
I'm very new to Android dev and programming in general. My goal is to create a quiz in this format.
Each row is a custom view that includes a RadioGroup with 3 RadioButtons. And that is working fine, as you can see in the picture.
This is how the List is being populated:
private void populateListView() {
ArrayAdapter<QuizQuestion> adapter = new MyListAdapter();
ListView list = (ListView) findViewById(R.id.questionsListView);
list.setAdapter(adapter);
}
private class MyListAdapter extends ArrayAdapter<QuizQuestion> {
public MyListAdapter() {
super(ShortQuiz.this, R.layout.item_view, myQuestions);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if (itemView == null) {
itemView = getLayoutInflater().inflate(R.layout.item_view, parent, false);
}
QuizQuestion currentQuestion = myQuestions.get(position);
ImageView imageView = (ImageView) itemView.findViewById(R.id.item_icon);
imageView.setImageResource(currentQuestion.getImageId());
//Question
TextView question = (TextView) itemView.findViewById(R.id.item_question);
question.setText(currentQuestion.getQuestion());
//Game
TextView game = (TextView) itemView.findViewById(R.id.item_game);
game.setText(currentQuestion.getGameName());
//Option A
TextView optionA = (TextView) itemView.findViewById(R.id.item_radioA);
optionA.setText(currentQuestion.getOptionA());
//Option B
TextView optionB = (TextView) itemView.findViewById(R.id.item_radioB);
optionB.setText(currentQuestion.getOptionB());
//Option C
TextView optionC = (TextView) itemView.findViewById(R.id.item_radioC);
optionC.setText(currentQuestion.getOptionC());
return itemView;
}
}
The problem is that I do not know how to 'access' (set/get ids) each RadioGroup after creation in order to validate for right or wrong answers, which I want to do in one go pressing a button, as in: If the selected option is correct then +1 to Correct Answers.
Thanks.
Use a ViewHolder for each row. https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html
You can reference the items in each individual row. You can search for examples for how the ViewHolder is used in practice, but in general each row in the holder will be stored with a setTag() and retreived with a getTag(). So you can obtain the items in a view like:
view.getTag();
When I create a class extends BaseAdapter, I get a warning message
Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling
Do I need to change my code like this suggestion? All my code is running smoothly, it's just whether it is necessary to change the code to do updata code with the latest styles? Or just need to add #SuppressLint({ "ViewHolder", "InflateParams" }) ?
My adapter
public View getView(int position, View convertView, ViewGroup parent) {
View v = inflater.inflate(R.layout.list_item, null);
TextView merchant_type = (TextView) v.findViewById(R.id.merchant_type);
TextView merchant_name = (TextView) v.findViewById(R.id.merchant_name);
TextView merchant_location = (TextView) v.findViewById(R.id.merchant_location);
VoucherBean obj = (VoucherBean) getItem(position);
merchant_type.setText(obj.getMerchantType());
merchant_name.setText(obj.getMerchantName());
merchant_location.setText(obj.getMerchantLocation());
return v;
}
If you want to change my code as recommended above warning, like what my code later? Sorry if my question is too basic for a beginner
if you have more data in your list view then you should use recycling, because it improves scrolling and performance also. here is code look like if you use view holder in your adapter.
your ViewHolder looks like
public class ViewHolder {
public TextView merchant_type;
public TextView merchant_name;
public TextView merchant_location;
}
and your getView method
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
vi = inflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.merchant_type = (TextView) vi.findViewById(R.id.merchant_type);
holder.merchant_name = (TextView) vi.findViewById(R.id.merchant_name);
holder.merchant_location = (TextView) vi.findViewById(R.id.merchant_location);
vi.setTag(holder);
} else
holder = (ViewHolder) vi.getTag();
// now set your text view here like
holder.merchant_name.setText("Bla Bla Bla");
// return your view
return vi;
The View Holder pattern is meant to make your code easier to read and to maintain. If you look at the standard ViewHolder you'll see that its a class that basically does what you do in your getView:
private static class ExampleViewHolder{
TextView merchant_type;
TextView merchant_name;
TextView merchant_location;
public ExampleViewHolder(View view){
merchant_type = (TextView) v.findViewById(R.id.merchant_type);
merchant_name = (TextView) v.findViewById(R.id.merchant_name);
merchant_location = (TextView) v.findViewById(R.id.merchant_location);
}
}
Then in your getView method you'd get your view like this:
ExampleViewHolder holder = new ExampleViewHolder(view);
view.setTag(holder);
It's good to have them and the newer apis kind of force the use of this pattern, however as you can see by the code it doesn't change much besides readability and ease of maintenance.
However! There is an important part of the getView method that you are missing, and is most likely the reason for the warning.
The listview recycles the views, and gives them back to the adapter so that the same view doesnt have to be inflated everytime. Saves resources and a lot of memory, and you're not making use of this very important aspect of the listView.
You see, the way it recycles views is passing the old view, that you inflated before, back to the adapter through the convertView, so if the convertView is not null you can be sure its the inflated layout that you are using. So use that instead of inflating a new one:
View view = convertView;
ExampleViewHolder holder;
if(view == null){//means convertView is also null
view = inflater.inflate(yourlayout, parent, false);
holder = new ExampleViewHolder(view);
view.setTag(holder);
}else{
holder = (ExampleViewHolder) view.getTag();
}
I have developed an app in which I display data in Gridview. All data comes from local storage. I am able to display each data correctly and there are no issues with it. But when I have scroll the Gridview and goto the bottom and getback to Top,it changes position. And sometimes when I scroll down and getback to Top, a blank screen appears on screen;no data found at all!
So I thought that there is issue with getView(). I am unable to figure out the problem
Code of getView():
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewholder;
// LayoutInflater inflator = activit.getLayoutInflater();
if(convertView == null) {
// recycled view is null so create it.
viewholder = new ViewHolder();
convertView = inflator.inflate(R.layout.gridviewrow, null);
viewholder.imgvGridicon = (ImageView) convertView.findViewById(R.id.imgvGridicon);
viewholder.txtGridItemlabel = (TextView) convertView.findViewById(R.id.txtGridItemlabel);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder) convertView.getTag();
}
if ((lstpinfo.get(position).appname.toString()) != null) {
viewholder.imgvGridicon.setImageDrawable((lstpinfo.get(position).icon));
viewholder.txtGridItemlabel.setText(lstpinfo.get(position).appname.toString());
}
return convertView;
}
Update::
Intitalize of inflater::
private LayoutInflater inflator;
private ArrayList<PInfo> lstpinfo = new ArrayList<PInfo>();
public GridViewAdapter(Context cntx, ArrayList<PInfo> lstpinfo) {
activit = cntx;
inflator = LayoutInflater.from(cntx);
this.lstpinfo = lstpinfo;
}
You have to set your view height with fixed value. There's a scrolling bug in android gridview with different heights. Please see this for reference.