I am new for Android development.
I was trying to implement a custom ArrayAdapter to accept custom object in Android Studio.
I have referenced the source code from some tutorial, but my screen output was improper that the custom object only fill in the TextView which link with the textViewResourceId of ArrayAdapter , but not the custom object's properties fill in all EditText in the layout appropriately.
I tried to remove textViewResourceId parameter in the constructor of ArrayAdapter to fix the problem, but Android Studio returned error - You must supply a resource ID for a TextView
I want the properties of custom object fill in all EditText and no error message be returned, can anyone give me a hint?
Here is my layout:
row_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/row_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<EditText
android:id="#+id/row_no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="1dp"
android:layout_gravity="center_horizontal"
android:background="#color/colorWhite"/>
<EditText
android:id="#+id/row_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="1dp"
android:layout_gravity="center_horizontal"
android:background="#color/colorWhite"/>
</LinearLayout>
Here is my custom object:
Row.java
public class Row
{
public int RowNo;
public String RowText;
public Row(int no, String text)
{
this.RowNo = no;
this.RowText = text;
}
}
Here is my custom ArrayAdapter:
RowAdapter.java
public class RowAdapter extends ArrayAdapter<Row> {
private final Activity _context;
private final ArrayList<Row> rows;
static class ViewHolder
{
EditText RowNo;
EditText RowText;
}
public RowAdapter(Activity context, ArrayList<Row> rows)
{
super(context,R.layout.row_layout, R.id.row_id ,rows);
this._context = context;
this.rows = rows;
}
public View GetView(int position, View convertView, ViewGroup parent){
ViewHolder holder = null;
if(convertView == null)
{
LayoutInflater inflater = _context.getLayoutInflater();
convertView = inflater.inflate(R.layout.row_layout,parent,false);
holder = new ViewHolder();
holder.RowNo = (EditText)convertView.findViewById(R.id.row_no);
holder.RowText = (EditText)convertView.findViewById(R.id.row_text);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.RowNo.setText(rows.get(position).RowNo);
holder.RowText.setText(rows.get(position).RowText);
return convertView;
}
}
Here is my Activity class:
RowActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.row_list_page);
listView = (ListView)findViewById(R.id.list_view);
ArrayList<Row> rows = new ArrayList<Row>();
rows.add(new Row(1,"Test"));
rows.add(new Row(2,"Test"));
rows.add(new Row(3"Test"));
RowAdapter adapter = new RowAdapter(this,rows);
listView.setAdapter(adapter);
}
Here is working adapter code
public class RowAdapter extends ArrayAdapter<Row> {
private final Activity _context;
private final ArrayList<Row> rows;
public class ViewHolder
{
EditText RowNo;
EditText RowText;
}
public RowAdapter(Activity context, ArrayList<Row> rows)
{
super(context,R.layout.row_layout, R.id.row_id ,rows);
this._context = context;
this.rows = rows;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder = null;
if(convertView == null)
{
LayoutInflater inflater = _context.getLayoutInflater();
convertView = inflater.inflate(R.layout.row_layout,parent,false);
holder = new ViewHolder();
holder.RowNo = (EditText)convertView.findViewById(R.id.row_no);
holder.RowText = (EditText)convertView.findViewById(R.id.row_text);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.RowNo.setText(""+rows.get(position).RowNo);
holder.RowText.setText(rows.get(position).RowText);
return convertView;
}
}
Your getting exception at holder.RowNo.setText(rows.get(position).RowNo);
so replace it with
holder.RowNo.setText(""+rows.get(position).RowNo);
Change with below code:-
RowAdapter.java
public class RowAdapter extends BaseAdapter {
private final Context _context;
private final ArrayList<Row> rows;
public RowAdapter(Context context, ArrayList<Row> rows)
{
this._context = context;
this.rows = rows;
}
#Override
public int getCount() {
return rows.size();
}
#Override
public Object getItem(int position) {
return rows;
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent){
LayoutInflater inflater = (LayoutInflater) _context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView=inflater.inflate(R.layout.row_layout, null,true);
EditText RowNo = (EditText) rowView.findViewById(R.id.row_no);
EditText RowText = (EditText) rowView.findViewById(R.id.row_text);
RowNo.setText(rows.get(position).RowNo);
RowText.setText(rows.get(position).RowText);
return rowView;
}
}
public View getView (int position, View convertView, ViewGroup parent)
This method is called to get view object.
In your code there is GetView, not getView
change your
public View GetView(int position, View convertView, ViewGroup parent)
with
#Override
public View getView(int position, View convertView, ViewGroup parent)
and
holder.RowNo.setText(rows.get(position).RowNo);
with
holder.RowNo.setText(""+rows.get(position).RowNo);
Related
I am using GridView to display a list but the grid return just one row .
How can I display all items in gridView?
My code :
<GridView
android:id="#+id/slide_item_rc_latest"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:stretchMode="columnWidth"
android:numColumns="2"
/>
And my Adapter :
public class latestProductAdapter extends BaseAdapter {
private List<SlideItem> slideItems;
private Context mContext;
public latestProductAdapter(Context c, List<SlideItem> slideItems) {
mContext = c;
this.slideItems = slideItems;
}
#Override
public int getCount() {
return slideItems.size();
}
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
if (convertView == null) {
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.slid_item, parent, false);
...
}
return convertView;
}
}
In my activivty :
latestProductAdapter = new latestProductAdapter(getContext(), productListLatest);
LatestProductsGridView.setAdapter(latestProductAdapter);
Most possible reason could be you might be using grid view inside scroll view.
So remove scroll view or use other xml file to declare.
I'm trying to implement a selection activity for a given list of items. Each item is checkable, so I have an item with a TextView and a CheckBox. I implemented a ListView for displaying all the options and a Spinner for showing only the "Top Ten" choices, as a subset of the same list. For now I'm showing all the items in both ListView and Spinner.
I want for the items in the ListView to update when the user selects an item in the Spinner (Note: The reverse path works fine, as the Spinner grabs the updated ArrayList each time it dropsdown).
I tried to implement setOnItemSelectedListener for my Spinner, and to call notifyOnDataSetChanged() for my ListViewAdapter inside the Listener. But the Listener is only called on collapse and I get a weird (maybe unrelated) warning message.
The onItemSelectedListener for the Spinner only runs when the Spinner gets collapsed. But notifyOnDataSetChanged() seems to ignore the checked status of the items as a change. How can I make the first option run everytime I check an item and have the change get properly received by the ListAdapter?
Here's the Activity.java code:
public class TriageReasonActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_triage_reason);
final String[] select_qualification = {
"Select Qualification", "10th / Below", "12th", "Diploma", "UG",
"PG", "Phd"};
Spinner spinner = (Spinner) findViewById(R.id.top_reasons_spinner);
ListView symptoms_list = (ListView) findViewById(R.id.view_list_symptoms);
ArrayList<Symptoms> listVOs = new ArrayList<>();
for (int i = 0; i < select_qualification.length; i++) {
Symptoms reason = new Symptoms();
reason.setTitle(select_qualification[i]);
reason.setSelected(false);
listVOs.add(reason);
}
SymptomsListAdapter mListAdapter = new SymptomsListAdapter(this, 0,
listVOs);
SymptomsSpinnerAdapter mSpinnerAdapter = new SymptomsSpinnerAdapter(this, 0,
listVOs);
symptoms_list.setAdapter(mListAdapter);
spinner.setAdapter(mSpinnerAdapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
Log.i("Item selected", "but not cahnged");
symptoms_list.invalidateViews();
mListAdapter.notifyDataSetInvalidated();
}
#Override
public void onNothingSelected(AdapterView<?> parentView) {
Log.i("Not item selected", "but actually it did");
}
});
}
The SpinnerCustom Adapter code:
public class SymptomsSpinnerAdapter extends ArrayAdapter<Symptoms>{
private Context mContext;
private ArrayList<Symptoms> listState;
private SymptomsSpinnerAdapter myAdapter;
private boolean isFromView = false;
/*#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
//mNotifyOnChange = true;
}*/
public SymptomsSpinnerAdapter(Context context, int resource, List<Symptoms> objects) {
super(context, resource, objects);
this.mContext = context;
this.listState = (ArrayList<Symptoms>) objects;
this.myAdapter = this;
}
#Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
public View getCustomView(final int position, View convertView,
ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
LayoutInflater layoutInflator = LayoutInflater.from(mContext);
convertView = layoutInflator.inflate(R.layout.item_reasons, null);
holder = new ViewHolder();
holder.mTextView = (TextView) convertView.findViewById(R.id.text);
holder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTextView.setText(listState.get(position).getTitle());
// To check weather checked event fire from getview() or user input
isFromView = true;
holder.mCheckBox.setChecked(listState.get(position).isSelected());
isFromView = false;
if ((position == 0)) {
holder.mCheckBox.setVisibility(View.INVISIBLE);
} else {
holder.mCheckBox.setVisibility(View.VISIBLE);
}
holder.mCheckBox.setTag(position);
holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag();
if (!isFromView) {
listState.get(position).setSelected(isChecked);
}
}
});
return convertView;
}
#Override
public int getCount() {
return listState.size();
}
#Override
public Symptoms getItem(int position) {
if( position < 1 ) {
return null;
}
else {
return listState.get(position-1);
}
}
#Override
public long getItemId(int position) {
return 0;
}
private class ViewHolder {
private TextView mTextView;
private CheckBox mCheckBox;
}
}
Here's the (almost identical) ListAdapter:
public class SymptomsListAdapter extends BaseAdapter implements ListAdapter {
private Context mContext;
private ArrayList<Symptoms> listState;
private boolean isFromView = false;
public SymptomsListAdapter(Context context, int resource, List<Symptoms> objects) {
this.mContext = context;
this.listState = (ArrayList<Symptoms>) objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
public View getCustomView(final int position, View convertView,
ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
LayoutInflater layoutInflator = LayoutInflater.from(mContext);
convertView = layoutInflator.inflate(R.layout.item_reasons, null);
holder = new SymptomsListAdapter.ViewHolder();
holder.mTextView = (TextView) convertView.findViewById(R.id.text);
holder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
convertView.setTag(holder);
} else {
holder = (SymptomsListAdapter.ViewHolder) convertView.getTag();
}
holder.mTextView.setText(listState.get(position).getTitle());
// To check weather checked event fire from getview() or user input
isFromView = true;
holder.mCheckBox.setChecked(listState.get(position).isSelected());
isFromView = false;
if ((position == 0)) {
holder.mCheckBox.setVisibility(View.INVISIBLE);
} else {
holder.mCheckBox.setVisibility(View.VISIBLE);
}
holder.mCheckBox.setTag(position);
holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag();
if (!isFromView) {
listState.get(position).setSelected(isChecked);
}
}
});
return convertView;
}
#Override
public int getCount() {
return listState.size();
}
#Override
public Symptoms getItem(int position) {
if( position < 1 ) {
return null;
}
else {
return listState.get(position-1);
}
}
#Override
public long getItemId(int position) {
return 0;
}
private class ViewHolder {
public TextView mTextView;
public CheckBox mCheckBox;
}
}
And here's the warning I'm getting:
W/art: Before Android 4.1, method int android.support.v7.widget.DropDownListView.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView
EDIT: Adding the layouts and the model class in case they may cause an issue:
Activity Layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="demo.hb.activity.visit.TriageReasonActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textFontWeight="6dp"
android:textSize="30sp"
android:layout_margin="20dp"
android:textAlignment="center"
android:textColor="#000000"
android:text="What is the reason for your visit?" />
<Spinner
android:id="#+id/top_reasons_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:drawable/btn_dropdown"
android:spinnerMode="dropdown"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="end">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/view_list_symptoms"
android:layout_above="#+id/next_btn"
android:layout_alignParentTop="true"/>
</RelativeLayout>
</LinearLayout>
</FrameLayout>
Item layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="text"
android:textAlignment="gravity" />
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true" />
</RelativeLayout>
Model Class:
public class Symptoms {
private String title;
private boolean selected;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
The reason that nothing is changing is because you haven't implemented the method to handle the data set changes. You need to handle how the data is reloaded in your adapter:
public class SymptomsListAdapter extends BaseAdapter implements ListAdapter {
...
public void refreshData(ArrayList<Symptoms> objects){
this.listState = (ArrayList<Symptoms>) objects;
notifyDataSetChanged();
}
...
}
This link does a great job of explaining how the notifyDataSetInvalidated() works (or in your case, why it's not working).
I'm trying to implement button on each row in ListView, but I saw many topics and I don't succeeded to add code to mine. Here is my MainActivity :
public class MainActivity extends AppCompatActivity {
private ArrayAdapter<String> itemsAdapter;
private ArrayList<String> items;
private ImageButton formButton;
private ListView lvMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
commonFunction();
}
public void commonFunction() {
lvMain = (ListView) findViewById(R.id.lvMain);
items = new ArrayList<String>();
readItems();
itemsAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items);
lvMain.setAdapter(itemsAdapter);
formButton = (ImageButton) findViewById(R.id.btnPlus);
formButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setLayoutActivity();
}
});
}
}
Here is my activity_main.xml :
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/lvMain"
android:layout_above="#+id/btnPlus" />
<ImageButton
android:layout_width="match_parent"
android:layout_height="65dp"
android:id="#+id/btnPlus"
android:layout_alignParentBottom="true"
app:srcCompat="#mipmap/ic_plus_foreground" />
Does someone any idea how to do ?
Create custom adapter with custom row layout file and add button on that row file and bind it to List/Recycler view. So it will inflate in all row.
Add below code in row_list.xml file.
<ImageButton
android:layout_width="match_parent"
android:layout_height="65dp"
android:id="#+id/btnPlus"
android:layout_alignParentBottom="true"
app:srcCompat="#mipmap/ic_plus_foreground" />
CustomAdapter.java
public class CustomAdapter extends BaseAdapter {
private ArrayList data;
private static LayoutInflater inflater = null;
/************* CustomAdapter Constructor *****************/
public CustomAdapter(ArrayList d) {
/********** Take passed values **********/
data = d;
/*********** Layout inflater to call external xml layout () ***********/
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/******** What is the size of Passed Arraylist Size ************/
public int getCount() {
if (data.size() <= 0)
return 1;
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
/********* Create a holder Class to contain inflated xml file elements *********/
public static class ViewHolder {
public ImageButton button;
}
/****** Depends upon data size called for each row , Create each ListView row *****/
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
vi = inflater.inflate(R.layout.row_list, null);
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.button = (ImageView) vi.findViewById(R.id.btnPlus);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else
holder = (ViewHolder) vi.getTag();
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Button click
}
});
return vi;
}}
Hope it will solve problem.
Happy coding!!
Crate custom adapter and bind it with the ListView.
Inflate custom layout for each row in the adapter.
You can put your button in the custom layout file.
public class CustomAdapter extends ArrayAdapter<String> {
private Context context;
private int singleRowLayoutId;
public CustomAdapter(Context context, int singleRowLayoutId, String []titles, String []desc, int []images ) {
super(context, singleRowLayoutId,titles);
this.context=context;
this.singleRowLayoutId=singleRowLayoutId; //custom row layout for list view item
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View row=convertView;
CustomViewHolder holder=null;
if(row==null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//view object of single row of list view
row = inflater.inflate(singleRowLayoutId, parent, false);
//by using holder, we make sure that findViewById() is not called every time.
holder=new CustomViewHolder(row);
row.setTag(holder);
}
else
{
holder=(CustomViewHolder)row.getTag();
}
return row;
}
private class CustomViewHolder {
private ImageButton mbtn;
CustomViewHolder(View view)
{
mbtn=(ImageButton)view.findViewById(R.id.btn);
mbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//handle button click here
}
});
}
}
I think that this is what you need - create a separate layout for the rows in the list view. Then create a custom adapter, where you are adding this layout as row layout and then set this adapter to your list view. Here are the details with examples:
Android custom Row Item for ListView
I have set up a GridView Successfully, however I want to be able to change the Images within the the GridView dependent upon a variable. For example:
int a = 2
if (a == 2){
Integer[] images = {
R.drawable.image1
R.drawable.image3
}
} else {
Integer[] images = (
R.drawable.image2
R.drawable.image4
}
}
However I can't find a way to do this. The code I am working with at the moment is:
GridView cardList = (GridView) findViewById(R.id.cardList);
cardList.setAdapter(new ImageAdapter(this));
cardList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(Create.this, "" + position,
Toast.LENGTH_SHORT).show();
}
});
}
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return images.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(215, 215));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(images[position]);
return imageView;
}
private Integer[] images = {
R.drawable.iamge1, R.drawable.image2
};
}
Try something like this:
The activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridView = (GridView) findViewById(R.id.gridView);
int images[] = {R.drawable.ic_action_name};
ImageAdapter imageAdapter = new ImageAdapter(getApplicationContext(),images);
gridView.setAdapter(imageAdapter);
}
}
Your adapter class:
public class ImageAdapter extends BaseAdapter {
private int images[];
Context context;
public ImageAdapter(Context context, int images[]){
this.context = context;
this.images = images;
}
private class Holder{
ImageView imageView;
}
#Override
public int getCount(){
return images.length;
}
#Override public Object getItem(int position){
return null;
}
#Override
public long getItemId(int position){
return 0;
}
#Override
public View getView(final int position, View view, ViewGroup parent){
//All of this Holder stuff is so that the View doesn't jump around wildly
final Holder holder;
LayoutInflater inflater;
if (view == null){
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.singleimage,null);
holder = new Holder();
holder.imageView = (ImageView) view.findViewById(R.id.singleImage);
view.setTag(holder);
}
else {
holder = (Holder) view.getTag();}
holder.imageView.setImageResource(images[position]);
return view;
}
}
You also need a simple xml file for the single image in the gridView. Name this "singleimage.xml"
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/singleImage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Activity xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ri.emily.testapp.MainActivity">
<GridView
android:layout_width="match_parent"
android:id="#+id/gridView"
android:layout_height="match_parent" />
</RelativeLayout>
I'm basically trying to display multiple views via the same ListView adapter. However, the adapter ends up generating multiple duplicates and crashes sometimes as well with a NullPointer. My guess is that I have implemented the adapter all wrong. Here's my complete code:
The item could either be a photo or a text.
Adapter:
public class FeedAdapter extends BaseAdapter {
static private Activity activity;
private static LayoutInflater inflater = null;
ArrayList<ActivityTable> actList = new ArrayList<ActivityTable>();
Holder holder;
public FeedAdapter(Activity a, ArrayList<ActivityTable> actList) {
activity = a;
this.actList = actList;
}
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final ActivityTable act = actList.get(position);
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
if (act.getType().equals("text")) {
convertView = inflater.inflate(R.layout.feed_single_text, null);
holder = new Holder();
//More code that Set the caption to the holder
convertView.setTag(holder);
}
if (act.getType().equals("photo")) {
convertView = inflater.inflate(R.layout.feed_single_picture, parent, false);
holder = new Holder();
holder.media = (ImageView) convertView.findViewById(R.id.postphoto);
//More code that Set the photo to the holder
convertView.setTag(holder);
}
} else {
holder = (Holder) convertView.getTag();
}
return convertView;
}
public static class Holder {
ImageView media;
TextView caption;
}
}
Am I inflating multiple views in the same adapter the wrong way? Can anyone point out the error?
You have 2 diffrent layout for each row so I think you should add
#Override
public int getViewTypeCount() {
return 2;
}
to your listview adapter
In your code, try to initial your LayoutInflater inside the constructor of your adapter
public FeedAdapter(Activity a, ArrayList<ActivityTable> actList) {
...
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
And also you should optimize your ListView performance
Here is my experience
It is good to have these 3 in place.
#Override
public int getCount() {
return actList().size();
}
#Override
public Object getItem(int position) {
return actList().get(position);
}
#Override
public long getItemId(int position) {
return position;
}
Here is the important part,
first you have to tell the adapter how many type,
and then you have to tell the adapter how to determine the type.
Here I tell type View Type = 2
#Override
public int getViewTypeCount() {
return 2;
}
and Here I tell the adapter How I put the type number into the array
I use setType = 0 || set Type = 1
personal preference here: I like to use int instead of String
#Override
public int getItemViewType(int position) {
return act.get(position).getType();
}
and then later at the getView
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
int listViewItemType = getItemViewType(position);
if (v == null) {
..whatevever you doing to make v not null
}
if (listViewItemType == 0) {
//Do something
}else if(listViewItemType == 1){
// Do something different
}
return v;
}
Yes you will get duplicate Item, Because Convertview is reusing. Once convertview is created that view using if you scroll.
So better use single layout and with both Image and text. Based type hide any one.
xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/ImgFeed"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtCaption"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
try this you are using ImageView insted of TextView
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final ActivityTable act = actList.get(position);
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.feed_layout, null);
holder = new Holder();
holder.caption = (TextView) convertView.findViewById(R.id.txtCaption);
holder.media = (ImageView) convertView.findViewById(R.id.ImgFeed);
if (act.getType().equals("text")) {
holder.media.setVisibility(View.GONE)
}
else if (act.getType().equals("photo")) {
holder.caption.setVisibility(View.GONE)
}
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
return convertView;
}