I have my listview working to display it on the MainActivity and the layout is set to have 3 TextView properties of a new Person object.
My previous code was working but the scrolling was slow, so i looked into using the Holder pattern for optimization. However, when trying to implement this i am getting NullPointerException on lines TextView personID=(TextView) view.findViewById(R.id.person_field1)
listView is set to a ListView on the MainActivity and i need the person objects to display on this view within the MainActivity.
MainActivity class:
public void onButtonClick()
{
//put the persons objects on the listView within MainActivity
listView.setAdapter(new personAdapter(MainActivity.this, R.layout.list_layout,
personList));
}
class personAdapter extends ArrayAdapter<Person>
{
private LayoutInflater layoutInflator;
public personAdapter(Context context, int resource, List<Person> p) {
super(context, resource, p);
layoutInflator = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
Holder holder = null;
Person p = getItem(position);
if (view == null) {
layoutInflator.inflate(R.layout.list_layout, null);
//getting null pointer exceptions here
TextView personID = (TextView) view.findViewById(R.id.person_field1);
TextView personFName = (TextView) view.findViewById(R.id.person_field2);
TextView personLName = (TextView) view.findViewById(R.id.person_field3);
holder = new Holder(personID, personFName, personLName);
view.setTag(holder);
}
else {
holder = (Holder) view.getTag();
}
holder.stop.setText(person.getID());
holder.location.setText(person.getFName());
holder.time.setText(person.getLName());
return view;
}
}
static class Holder
{
public TextView id;
public TextView FName;
public TextView LName;
public Holder(TextView id, TextView FName, TextView LName) {
this.id = id;
this.FName = FName;
this.LName = LName;
}
}
because there really is null
pay attention to check view == null, and then view never initialized
try this
view = layoutInflator.inflate(R.layout.list_layout, null);
Just add reference to your view:
view = layoutInflator.inflate(R.layout.list_layout, null);
view is null, you're even checking for it:
if (view == null) { ...
}
What you're missing is assigning the inflated layout to view
view = layoutInflator.inflate(R.layout.list_layout, null);
Related
Am building an app for exploring files and am using the android native resource layout for data population called android.R.simple_list_item_1, i have tried going through the methods of a listview to see if i can add a drawable to the left of each item in my list but didn't manage. So the only way i get to access a view from a listview is on event OnItemClick where the view tapped is passed as parameter to the method and then i can format it this way for the drawable
public class MainActivity extends AppCompatActivity {
//Define a listview for holding the data mined from storage
public ListView mydata;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Define the listview
mydata=findViewById(R.id.data);
mydata.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
//Cast view to TextView at position
TextView r=(TextView)view;
//get the drawable and set to textview
Drawable mydraw= ResourcesCompat.getDrawable(getResources(),R.drawable.ic_baseline_file_copy_24,null);
r.setCompoundDrawables(mydraw,null,null,null);
}
}
}
Is there in which i can get all the views in the listview, create a loop, iterate through all of them and cast to TextView array and then set my drawable to the TextView(s)?
final static class YourItemClass {
...
}
final static class ViewHolder {
TextView mTextView;
private ViewHolder(#NonNull final View simpleListItem1) {
if (!(simpleListItem1 instanceof TextView)) throw new IllegalArgumentException("TextView was expected as root of Layout");
this.mTextView = (TextView)simpleListItem1;
}
}
final class CustomArrayAdapter extends ArrayAdapter<YourItemClass> {
private final Drawable mDrawable;
private final LayoutInflater mLayoutInflater;
public CustomArrayAdapter(#NonNull final Context context, #NonNull final List<YourItemClass> objects) {
super(context, resource, objects);
this.mDrawable = ResourcesCompat.getDrawable(getResources(),R.drawable.ic_baseline_file_copy_24,null);
this.mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull final ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = this.mLayoutInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
final YourItemClass cYourItemClassAtSpecificPosition = this.getItem(position);
holder.mTextView.setCompoundDrawables(this.mDrawable, null, null, null);
return convertView;
}
}
Then from your Activity/Fragment:
private void setItemsToListView(#NonNull final List<YourItemClass> items) {
mListView.setAdapter(new CustomArrayAdapter(getActivity(), items));
}
This code will run "setCompoundDrawables()" for each row displayed in the ListView.
Im absolutely new in android studio, and Im trying to create custom listview, the problem is that it creating wrong list, as
Name
Name
------
Surname
Surname
-----
... and repeating
what I want to do is:
Name
Surname
-------
My code is:
protected void onResume() {
DBHelper db = new DBHelper(this);
ArrayList<String> names = new ArrayList<String>();
for (int i = 0; i < db.getAllContacts().size(); i++) {
names.add(db.getAllContacts().get(i).getName());
names.add(db.getAllContacts().get(i).getEmail());
}
ArrayAdapter adapter = new custom_row(this, names);
listView_allContacts.setAdapter(adapter);
super.onResume();
}
What is wrong here? Thanks in advance!
My custom_row code:
public class custom_row extends ArrayAdapter {
public custom_row(Context context, ArrayList<String> names) {
super(context, R.layout.custom_cell, names);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater peoInf = LayoutInflater.from(getContext());
View customView = peoInf.inflate(R.layout.custom_cell, parent , false);
String singlePeople = getItem(position);
TextView name_text = (TextView) customView.findViewById(R.id.name_text);
TextView email_text = (TextView) customView.findViewById(R.id.email_text);
name_text.setText(singlePeople);
email_text.setText(singlePeople);
return customView;
}
}
You need two different ArrayList one for the Names and one for the email.
try this
ArrayList<String> list;
public custom_row(Context context, ArrayList<String> names, ArrayList<String> email) {
super(context, R.layout.custom_cell, names);
list = email;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater peoInf = LayoutInflater.from(getContext());
View customView = peoInf.inflate(R.layout.custom_cell, parent , false);
String singlePeople = getItem(position);
String email = list.get(position);
TextView name_text = (TextView) customView.findViewById(R.id.name_text);
TextView email_text = (TextView) customView.findViewById(R.id.email_text);
name_text.setText(singlePeople);
email_text.setText(email);
return customView;
}
I am creating a check list for marking "students" present or absent.
I have a listView creating successfully with their details and to the right; an ImageView "X". When you tap the row the "X" changes to a tick! And it successfully changes other things that I need for this program to work.
My problem "was", when I would scroll one of the "ticked" rows out of view it would revert back to an "X". I have solved this using a View Holder.
My current problem is that, for example, in a List of 6 students. If I "tick" student 1, student 5 will also be changed to a tick (despite being out of view). And if I tick student 2, student 6 will be ticked. This works vice versa also. I know the onClick code is not executing for the "randomly changed rows" because despite being ticked these rows are not tagged as "ticked". It is very strange to me, here is my code:
ListView listView = (ListView) findViewById(R.id.student_listview);
listView.setBackgroundColor(Color.TRANSPARENT);
listView.setCacheColorHint(Color.TRANSPARENT);
listView.setAdapter(new MySimpleArrayAdapter(this, studentIDarr));
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
ImageView img = (ImageView) view.findViewById(R.id.icon);
if(img.getTag().toString().equals("checked")) {
checkListArr.set(position, "Absent");
img.setImageResource(R.drawable.remove);
img.setTag("unchecked");
} else {
checkListArr.set(position, "Present");
img.setImageResource(R.drawable.checked);
img.setTag("checked");
}
}
});
And the Adapter with ViewHolder...
static class ViewHolder {
TextView fname;
TextView sname;
TextView ID;
TextView email;
ImageView status;
}
public class MySimpleArrayAdapter extends ArrayAdapter<String> {
private final Context context;
private final ArrayList<String> values;
public MySimpleArrayAdapter(Context context, ArrayList<String> values) {
super(context, R.layout.list_class_item, values);
this.context = context;
this.values = values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
vi = inflater.inflate(R.layout.list_student_item, null);
holder = new ViewHolder();
holder.fname = (TextView) vi.findViewById(R.id.tvFname);
holder.sname = (TextView) vi.findViewById(R.id.tvSname);
holder.ID = (TextView) vi.findViewById(R.id.tvStudentID);
holder.email = (TextView) vi.findViewById(R.id.tvEmail);
holder.status= (ImageView) vi.findViewById(R.id.icon);
if(checkListArr.get(position).toString().equals("Absent")) {
holder.status.setTag("unchecked");
} else if (checkListArr.get(position).toString().equals("Present")) {
holder.status.setTag("checked");
}
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
if(checkListArr.get(position).toString().equals("Absent")) {
holder.status.setTag("unchecked");
} else if (checkListArr.get(position).toString().equals("Present")) {
holder.status.setTag("checked");
}
holder.ID.setText(values.get(position));
holder.fname.setText(fNameArray.get(position));
holder.sname.setText(sNameArray.get(position));
holder.email.setText(emailArray.get(position));
return vi;
}
}
Somebody please point out the obvious flaw in my code! Thankyou for reading!
You are misunderstanding how a ListView recycles previous views. You need to store these changes inside an underlying data model.
public class Model {
private String fname;
private String sname;
private String email;
private boolean selected;
public Model(String fname, String sname, String email) {
this.fname = fname;
selected = false;
//etc
}
public String getName() {
return fname;
}
public void setName(String fname) {
this.fname = fname;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
//etc
}
Adapter:
private final List<Model> list;
private final Activity context;
public InteractiveArrayAdapter(Activity context, List<Model> list) {
super(context, R.layout.yourlayout, list);
this.context = context;
this.list = list;
}
static class ViewHolder {
protected TextView text;
protected CheckBox checkbox;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
view = inflator.inflate(R.layout.rowbuttonlayout, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) view.findViewById(R.id.label);
viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check);
viewHolder.checkbox
.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
Model element = (Model) viewHolder.checkbox
.getTag();
element.setSelected(buttonView.isChecked());
}
});
view.setTag(viewHolder);
viewHolder.checkbox.setTag(list.get(position));
} else {
view = convertView;
((ViewHolder) view.getTag()).checkbox.setTag(list.get(position));
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.text.setText(list.get(position).getName());
holder.checkbox.setChecked(list.get(position).isSelected());
return view;
}
No need for ViewHolder.
convertView is already inflated view. Inflate when it's value is null. Then find your views and set the proper values according to position in either new or convertView.
Implement SparseBooleanArray in the adapter with methods for toggling <int Position, boolean Absence> values. It's optimized HashMap like class for holding and inserting such values. AdapterView<?> will give you adapter to interact with it.
ListView also will give you access to a similar array with all selected views for processing and saving.
I am required to create a project similar to that of the ff. pic. (My apologies for the use a siily pic, I have to make do with what is readily-available).
I wouldn't say what I did was the best approach in making a 'dynamic' ListView but I'd be glad just to make what I currently have work. Anyway my implementation is as follows:
public class MultiLineListViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayList<SearchResults> searchResults = getSearchResults();
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new MyCustomBaseAdapter(this, searchResults));
}
private ArrayList<SearchResults> getSearchResults() {
ArrayList<SearchResults> results = new ArrayList<SearchResults>();
SearchResults sr1 = new SearchResults();
sr1.setName("Name 1");
sr1.setPhone("12345");
SearchResults.setIcon(R.drawable.pic_one);
results.add(sr1);
sr1 = new SearchResults();
sr1.setName("Name 2");
sr1.setPhone("123456");
SearchResults.setIcon(R.drawable.pic_two);
results.add(sr1);
...
return results;
}
}
public class SearchResults {
private String name;
private String phone;
public static ArrayList<Integer> iconsList = new ArrayList<Integer>();
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public static void setIcon(int i) {
iconsList.add(i);
}
public String getName() {
return this.name;
}
public String getPhone() {
return this.phone;
}
}
public class MyCustomBaseAdapter extends BaseAdapter {
private ArrayList<SearchResults> searchArrayList;
private LayoutInflater layoutInflater;
private Context context;
public MyCustomBaseAdapter(Context context, ArrayList<SearchResults> results) {
this.searchArrayList = results;
//this.context = context;
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return searchArrayList.size();
}
#Override
public Object getItem(int position) {
return searchArrayList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
ImageView imageView;
if(convertView == null) {
convertView = layoutInflater.inflate(R.layout.custom_row_view, null);
holder = new ViewHolder();
holder.txtName = (TextView) convertView.findViewById(R.id.name);
holder.txtPhone = (TextView) convertView.findViewById(R.id.phone);
imageView = new ImageView(context);
convertView.setTag(holder);
}
else {
//imageView = (ImageView) convertView;
holder = (ViewHolder) convertView.getTag();
}
imageView = new ImageView(context);
imageView.setImageResource(SearchResults.iconsList.get(position));
holder.txtName.setText(searchArrayList.get(position).getName());
holder.txtPhone.setText(searchArrayList.get(position).getPhone());
return convertView;
}
class ViewHolder {
TextView txtName;
TextView txtPhone;
}
The above code was successful in displaying the Name and Phone lines in the ListView, it's the addition of the image that's giving me the 'Force Close'.
Take note of the commented line imageView = (ImageView) convertView;
I have reduced it down to that particular line in that removing the comments causes the app to crash.
Note: Whenever you get a force close that you're asking about in a StackOverflow question, please post the full stack trace.
You're always creating a new ImageView when in fact you probably shouldn't need to create one at all. It should be defined in your custom item layout (which you should probably post as well in this case) just like the TextViews. You can use findViewById the same way and get it.
kabuko is right on both counts. (UPDATE: also his comment below should not be ignored.) I'm guessing that your layout/custom_row_view is a LinearLayout, which would mean that the commented line attempts to coerce a LinearLayout to an ImageView, which yes would raise an exception. Also, your ViewHolder class is superfluous, just define those TextViews as variables in your getView() like so...
// Assuming that layout/custom_row_view is a LinearLayout...
getView(yadda) {
LinearLayout row;
ImageView imageView;
TextView txtName, txtPhone;
if (convertView == null) {
row = // inflate a new one
} else {
row = // get the old one
}
// fetch the views
imageView = row.findViewById(...);
txtName = row.findViewById(...);
txtPhone = row.findViewById(...);
// fill the views
imageView.setImageResource(...);
txtName.setContent(...);
txtPhone.setContent(...);
return row;
}
Ciao.
Can someone tell me how should am i going to create my listview which look similar [here][1].
Problem: How am i going to fulfill the sort of look and feel in my codes which has an icons, file name, and file size yet at the same time looking clean and simple on each file object as shown in the example in the link [here][2]??
Can someone guide on this matter because i'm rather new in android/java... Thanks
Refer to following url for how to implement custom listview
Update
public static class VideoInfo {
public String name = "";
public String size= "";
public String path = "";//add any more variables of which you want info
}
then where you are creating arraylist i.e getVideoFiles() create object of this class
Declare ArrayList<VideoInfo> videoItems;
private void getVideoFiles(File[] videoList)
{
videoItems = new ArrayList<VideoInfo>();
for (int i = 0; i < videolist.length; i++)
{
File mfile = videoList[i];
String path = mfile.getPath();
path = path.substring(path.lastIndexOf("/", path.indexOf("."));
VideoInfo model = new VideoInfo();
model.name = path;
model.size = mfile.length();
videoItems.add(model);
}
setListAdapter(new ListViewAdapter(this, R.layout.row, videoItems));
}
now in your adapter pass this object of arraylist and how you set variables in listview is already given in link above
Problem 2: And why is my listview
having error when the directory is
empty despite having this to handle
the "empty" in my xml
did you remember to name your listview?:
#id/android:list
And if you could for further helper PLEASE clear up problem 1, so its more clear and concise what you want.
UPDATE
The ID of the listview should be: #id/android:list
The ID of the texview should be: #id/android:empty
You haven't create custom adapter to incorporate the layout into codes listview.
Here is something you can use
private class ListViewAdapter extends ArrayAdapter<Object> {
private ArrayList<Object> items;
public ListViewAdapter(Context context, int textViewResourceId,
ArrayList<Object> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Object info = items.get(position);
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
if (info != null) {
ImageView imView = (ImageView) v.findViewById(R.id.icon);
TextView txtname =(TextView) v.findViewById(R.id.toptext);
TextView txtAddr = (TextView) v.findViewById(R.id.bottomtext);
//set image and set text as you like
}
return v;
}
}
Hope this can help.
First Create the Layout that you want to display for each row in your list then Extend the BaseAdapter class to Customize your Lists. This class contains getView method to replicate the rows in your lists as many times as you want to.
class HighScoreAdapter extends BaseAdapter
{
/* layout inflater to convert your XML layout to View to be rendered in your Each row of Lists.*/
private LayoutInflater minflator = LayoutInflater.from(HighScoreList.this);
ViewHolder holder;
#Override
public Object getItem(int position)
{
return position;
}
#Override
public long getItemId(int position)
{
return position;
}
boolean toRemove = false;
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
/* first time is null */
if (convertView == null )
{
convertView = minflator.inflate(R.layout.highscorelist, null);
holder = new ViewHolder();
holder.rank = (TextView) convertView.findViewById(R.id.user_id);
holder.username = (EditText) convertView.findViewById(R.id.user_nameedit);
holder.time = (TextView) convertView.findViewById(R.id.user_time);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.rank.setText(String.valueOf(position+1)+".");
/* returns the view for next row as layout will be same i.e. this increases the your list's scrolling and working faster even though your list contains thousands of Entry*/
return convertView;
}
/* this class is to make reference to your child views of your layout by using this you can set your child views properties and Listeners acording to your need.*/
class ViewHolder
{
TextView rank;
EditText username;
TextView time;
}
}
I hope this solves your problem completely.. :)
Add this method to your code and if you get any error or Exception please let me know.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Object info = items.get(position);
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
if (info != null) {
ImageView imView = (ImageView) v.findViewById(R.id.icon);
TextView txtname =(TextView) v.findViewById(R.id.toptext);
TextView txtAddr = (TextView) v.findViewById(R.id.bottomtext);
//set image and set text as you like
imview.setImageBitmap(IMAGE YOU WANT TO SET AAS ICON);
txtname.setText(FILENAME YOU WANT TO SET);
txtadr,setText(FILESIZE YOU WANT TO SET);
// Better you take each info filename,filesize and icon in a arraylist and set them
}
return v;
}