public class SongListAdapter_AddMode extends ArrayAdapter{
Context context;
ArrayList<Song> songs;
public SongListAdapter_AddMode(Context context, ArrayList<Song> songs) {
super(context, R.layout.list_item1_addmode, songs );
this.songs = songs;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder;
final int pos = position;
Log.d("TAG", "position :" + position);
Song currentSong = songs.get(position);
Log.d("TAG", "position : " + position );
if( convertView == null ){
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate( R.layout.list_item1_addmode, parent, false );
holder.titleLabel = (TextView) convertView.findViewById( R.id.topLabel );
holder.artistLabel = (TextView) convertView.findViewById( R.id.bottomLabel );
holder.cover = (ImageView) convertView.findViewById( R.id.list_image );
holder.button = (ImageView) convertView.findViewById(R.id.addButton);
holder.button.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("TAG", "pos" + pos );
}
});
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
Uri coverPath = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), currentSong.getID() );
Picasso.with(context).load( coverPath ).error( R.drawable.untitled ).into( holder.cover );
holder.titleLabel.setText( currentSong.getName() );
holder.artistLabel.setText( currentSong.getArtistName() );
return convertView;
}
private class ViewHolder {
TextView titleLabel;
TextView artistLabel;
ImageView cover;
ImageView button;
}
}
Well this is a small example of what an ArrayAdapter might look like. As you can see in the getView() method, I get the currentSong from a list which is actually very huge. It contains 1600~ Songs. When I print the position to the LogCat, it says 0 up to 7. How am I getting the correct position in the song list but when I print it, it is totally different?
I ALWAYS get the correct song. Even in the 900~ and above. But the position(LogCat) is always from 0 up to 7...
I need to get the current row. I want to add an onClickListener() to a button from the current View in this method and when I click it, I want to do something which I cannot do with an onItemClickListener() on the ListView.
It seems that you set the button click listener to print the position of the recyclable views thus you get 0-7.
Instead set the listener outside the if (convertview == null) check.
I ALWAYS get the correct song
of course since the code is correct
But the position(LogCat) is always from 0 up to 7...
This is due to how the ListView recycles its rows. 7 items are visible on the screen of your test device. See this post
You should override getCount:
#Override
public int getCount(){
return songs.size();
}
Because it is essential for correct displaying the list, but returns 0 b default.
When your view has wrong position ids or displays wrong items in your autocomplete dropdown view, then instantiate your view like this (Kotlin):
val view: View = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false)
Related
Hello guys I want to check through my GridView and see if any of the checkBox is checked or not. Eventually, I want to delete the rows in my database and update my custom adapter but I'm lost on what function to use or how to approach it. I know to use some kind of while/for loop but which data type can surf and iterate each list items? I was looking around and .getChildAt(index) seems like something that would be useful but it doesn't work/appear for my custom adapter variable in my MainActivity.
Here is my Custom Adapter class for reference
//variable responsible for making checkbox visible or not
private boolean displayCheckBox;
//constructor - it takes the context and the list of words
WordAdapter(Context context, ArrayList<WordFolder> word){
super(context, 0, word);
}
//sets the visibility of the checkBox
public void setCheckBoxVisibility(boolean visible){
this.displayCheckBox = visible;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View listItemView = convertView;
if(listItemView == null){
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.folder_view, parent, false);
}
//getting the checkBox view id
CheckBox checkBox = (CheckBox) listItemView.findViewById(R.id.check_box);
checkBox.setVisibility(displayCheckBox ? View.VISIBLE : View.GONE);
//Getting the current word
WordFolder currentWord = getItem(position);
//making the 3 text view to match our word_folder.xml
TextView date_created = (TextView) listItemView.findViewById(R.id.date_created);
TextView title = (TextView) listItemView.findViewById(R.id.title);
TextView desc = (TextView) listItemView.findViewById(R.id.desc);
//using the setText to get the text and set it in the textView
date_created.setText(currentWord.getDateCreated());
title.setText(currentWord.getTitle());
desc.setText(currentWord.getTitleDesc());
return listItemView;
}
}
My program is supposed to show a list of Apps and their data usage. If you click that app, it starts a new activity page and gives more information about that app. Everything works fine on the initial page and all apps show accurate information, but after I scroll down it messes up - the "more info" page shows a different app than the one I clicked.
I'm pretty sure the problem is around the onclick event not being binded to the holder? However I can't figure out what to do there. It should be noted that the problem is fixed if i get rid of the if(convertView == null)/else condition, but I know that is bad practice since we don't want to keep re-generating the previous items
Here is the getView code in my customAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final DataUsageModel model;
model = getItem(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.customlayout, null);
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intentMoreInfo = new Intent(view.getContext(),MoreInfoActivity.class);
intentMoreInfo.putExtra("wifiUsage",model.getWifiUsage());
intentMoreInfo.putExtra("mobileUsage",model.getMobileUsage());
intentMoreInfo.putExtra("appName",model.getName());
intentMoreInfo.putExtra("pname",model.getPname());
context.startActivity(intentMoreInfo);
}
});
holder.nameText = (TextView) convertView.findViewById(R.id.textViewWord);
holder.totalUsageText = (TextView) convertView.findViewById(R.id.textViewDescription);
holder.imageView = (ImageView) convertView.findViewById(R.id.iconImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.nameText.setText(model.getName());
long usageInMB = (model.getWifiUsage() + model.getMobileUsage())/(1024*1024);
holder.totalUsageText.setText(String.valueOf(usageInMB) + " MB");
holder.imageView.setImageDrawable(model.getImageRes());
return convertView;
}
public class ViewHolder {
TextView nameText;
TextView totalUsageText;
ImageView imageView;
}
Here is my code for the new activity started:
public class MoreInfoActivity extends AppCompatActivity {
TextView wifiUsage;
TextView mobileUsage;
ImageView icon;
TextView appName;
Button backButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.more_info_layout);
wifiUsage = findViewById(R.id.moreInfoWifi);
mobileUsage = findViewById(R.id.moreInfoMobile);
appName = findViewById(R.id.moreInfoAppName);
icon = findViewById(R.id.moreInfoIcon);
backButton = (Button)findViewById(R.id.moreInfoBack);
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
appName.setText(getIntent().getStringExtra("appName"));
wifiUsage.setText("Wifi Usage: " + String.valueOf(getIntent().getLongExtra("wifiUsage",0)/(1024*1024)) + " MB");
mobileUsage.setText("Mobile Usage: " + String.valueOf(getIntent().getLongExtra("mobileUsage",0)/(1024*1024)) + " MB");
try {
icon.setImageDrawable(getPackageManager().getApplicationIcon(getIntent().getStringExtra("pname")));
}catch(PackageManager.NameNotFoundException e){
e.printStackTrace();
}
You should set OnClickListener for every iteration of the getView method, not just when convertView == null.
In your case, the current data position is not synced with the current view (holder) position for every getView iteration
Furthermore, it is a very bad practice to start the activity from the adapter itself.
The adapter role is to bind view to data, and that's all.
So I have a List View that when you click on a row it opens up a new activity. In the new activity there's a checkbox. If you check the check box and then go back to the listview activity it should set a checkmark next to the list view item that was initially clicked.
Whats happening right now is when I check the checkbox and return to the listview every row has a checkmark next to it regardless of which row the checkbox was checked from.
heres my mainactivity with the listview and on click listener that starts the second checkbox activity
//fill list view with xml array of routes
final CharSequence[] routeListViewItems = getResources().getTextArray(R.array.routeList);
//custom adapter for list view
ListAdapter routeAdapter = new CustomAdapter(this, routeListViewItems);
final ListView routeListView = (ListView) findViewById(R.id.routeListView);
routeListView.setAdapter(routeAdapter);
routeListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int listViewItemPosition = position;
CharSequence route = routeListViewItems[position];
int imageId = (int) image.getResourceId(position, -1);
if (route.equals(routeListViewItems[position]))
{
Intent intent = new Intent(view.getContext(), RouteDetails.class);
intent.putExtra("route", routeDetail[position]);
intent.putExtra("imageResourceId", imageId);
intent.putExtra("routeName", routeListViewItems[position]);
intent.putExtra("listViewItemPosition", listViewItemPosition);
startActivity(intent);
}
}
}
);
}
then heres what im passing from the second activity back to the listview activity
#Override ///////for back button///////
public void onBackPressed() {
super.onBackPressed();
////////sets checkmark next to listview item//////
if (routeCheckBox.isChecked())
{
Intent check = new Intent(RouteDetails.this,MainActivity.class);
check.putExtra("checkImageResource", R.drawable.checkmark);
check.putExtra("listViewItemPosition", listViewItemPosition);
startActivity(check);
}
}
and heres back in my listview activity where I recieve the info from my checkbox activity and set the checkmark
edited to include full adapter class
edited to include code that I found to solve my issue!
class CustomAdapter extends ArrayAdapter<CharSequence> {
public CustomAdapter(Context context, CharSequence[] routes) {
super(context, R.layout.custom_row ,routes);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater routeInflater = LayoutInflater.from(getContext());
View customView = convertView;
if(customView == null){customView = routeInflater.inflate(R.layout.custom_row, parent, false);}
CharSequence singleRoute = getItem(position);
TextView routeText = (TextView) customView.findViewById(R.id.routeText);
routeText.setText(singleRoute);
/////////////set check mark/////////////
ImageView checkImageView = (ImageView) customView.findViewById(R.id.checkImageView);
int checkImageResourceId = ((Activity) getContext()).getIntent().getIntExtra("checkImageResource",0);
int listViewItemPosition = ((Activity) getContext()).getIntent().getIntExtra("listViewItemPosition",0);
/////my solution was just setting where listviewitemposition == position in my getview method//////
if (listViewItemPosition == position)
{
checkImageView.setImageResource(checkImageResourceId);}
//////////////////////////////////////////////////
return customView;
}
thanks for any help!
The solution was I just needed use an if statement to set listviewItemPosition == position in my getView method
/////my solution was just setting where listviewitemposition == position in my getview method//////
if (listViewItemPosition == position)
{
checkImageView.setImageResource(checkImageResourceId);}
//////////////////////////////////////////////////
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();
I have a ListView which is supposed to become a menu with two drawables and two text views per row.
Activity Code:
ArrayList<MenuItem> itemArray = new ArrayList<MenuItem>();
itemArray.add(new MenuItem("Headertexxt", "subbtexdt"));
itemArray.add(new MenuItem("asf", "asf"));
ListView listView = (ListView) findViewById(R.id.listViewCM);
String[] array = getResources().getStringArray(R.array.buttonsCM);
int[] images = new int[] { R.drawable.btn_car, R.drawable.btn_star, R.drawable.btn_bag};
listView.setAdapter(new HomeScreenButtonsAdapterSubtext(this, R.layout.row,
itemArray, images, R.drawable.list_arrow));
Utils.setListViewHeightBasedOnChildren(listView);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
findViewById(R.id.buttonCreditCompilation).performClick();
break;
case 1:
findViewById(R.id.buttonYourCredits).performClick();
break;
}
}
});
Adapter code:
public class HomeScreenButtonsAdapterSubtext extends ArrayAdapter<MenuItem> {
private Drawable[] drawables;
private Drawable arrowDrawable;
private ArrayList<MenuItem> items;
public HomeScreenButtonsAdapterSubtext(Context context, int resourceId,
ArrayList<MenuItem> items, int[] images, int arrowImage) {
super(context, resourceId, items);
this.items = items;
Resources resources = context.getResources();
if (images != null) {
drawables = new Drawable[images.length];
int i = 0;
for (int id : images) {
Drawable drawable = resources.getDrawable(id);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawables[i++] = drawable;
}
}
arrowDrawable = resources.getDrawable(arrowImage);
arrowDrawable.setBounds(0, 0, arrowDrawable.getIntrinsicWidth(),
arrowDrawable.getIntrinsicHeight());
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
if (v instanceof TextView) {
Drawable dr = drawables != null ? drawables[position %
drawables.length] : null;
((TextView) v).setCompoundDrawables(dr, null, arrowDrawable, null);
Utils.setFont((TextView) v);
}
// View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
MenuItem station = items.get(position);
if (station != null) {
TextView tt = (TextView) v.findViewById(R.id.headerText);
TextView bt = (TextView) v.findViewById(R.id.subText);
if (tt != null) {
tt.setText(station.getHeaderText());
}
if (bt != null) {
bt.setText(station.getSubText());
}
}
return v;
}
The problem I have right now is that I can't set the listview height based on the children. I'm trying to do that here: Utils.setListViewHeightBasedOnChildren(listView); but getting the error: arrayadapter requires the resource id to be a textview at this row. Does anyone know a solution for this?
Can I use some other method for setting the ListView height?
Thanks
but getting the error: arrayadapter requires the resource id to be a
textview at this row.
R.layout.row is a layout file which it doesn't have just a TextView. If you call the super constructor you have used and you also call the super.getView(in the getView method) method in the adapter, then ArrayAdapter will complain as it expects a single widget in the layout file passed to it(a single TextView).
I don't understand why you have that piece of code in the getView method(with the super call) when you know precisely that the row can't be an instance of Textview .
I'm not sure about setting the height of the ListView either, if you're trying to show all the rows of the ListView, don't do it(as you make the ListView basically useless). If you still want to do this, then it's better to lose the ListView and build the row layouts manually.
In fact it does not really make sense to set the height of ListView depending on its content.
Because the whole point of a ListView is to make its content scrollable (however big it is)...so it is supposed to have a fixed height.