Rows are repeated in ListView
I know this question has popped up several times, but for some reason I cannot get it to work. The fact that getView gets called multiple times in certain cases.
However, in the example shown here: http://android.amberfog.com/?p=296 it says that the first call for every row in the data should get a null value in currentView. This is not happening.
What is happening for me is that calls where position is 0 have currentView set to null, whereas calls where position is 1 have currentView set to the existing object.
There are 16 calls to "getView" in total, but I am getting rows duplicated once (ie, two from each row). Line 0 Line 1 Line 0 Line 1
I probably just didn't understand anything from this post.
Markup:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/title_paired_devices"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/title_paired_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp"
/>
<ListView android:id="@+id/paired_devices"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stackFromBottom="true"
android:layout_weight="1"
/>
<TextView android:id="@+id/title_new_devices"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/title_other_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp"
/>
<ListView android:id="@+id/new_devices"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stackFromBottom="true"
android:layout_weight="2"
/>
<Button android:id="@+id/button_scan"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/button_scan"
/>
</LinearLayout>
Listview string:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.
android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_selector"
android:orientation="horizontal"
android:padding="5dip" >
<!-- ListRow Left side Thumbnail image -->
<LinearLayout android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:layout_alignParentLeft="true"
android:background="@drawable/image_bg"
android:layout_marginRight="5dip">
<ImageView
android:id="@+id/list_image"
android:layout_width="50dip"
android:layout_height="50dip"
android:src="@drawable/icon"/>
</LinearLayout>
<!-- Device Address -->
<TextView
android:id="@+id/tvwDeviceAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/thumbnail"
android:layout_toRightOf="@+id/thumbnail"
android:textColor="#040404"
android:typeface="sans"
android:textSize="15dip"
android:textStyle="bold"/>
<!-- Display Name -->
<TextView
android:id="@+id/tvwDisplayName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tvwDeviceAddress"
android:textColor="#343434"
android:textSize="10dip"
android:layout_marginTop="1dip"
android:layout_toRightOf="@+id/thumbnail"/>
</RelativeLayout>
Adapter:
private class LazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<MPGDeviceDetails> data;
private LayoutInflater inflater=null;
public LazyAdapter(Activity a, ArrayList<MPGDeviceDetails> d) {
activity = a;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
Log.e("MPG","Position = " + position + ". ConvertView = " + convertView);
if(convertView==null)
vi = inflater.inflate(R.layout.device_name, null);
TextView address = (TextView)vi.findViewById(R.id.tvwDeviceAddress); // Device Address
TextView name = (TextView)vi.findViewById(R.id.tvwDisplayName); // Display name
ImageView thumb_image=(ImageView)vi.findViewById(R.id.list_image); // thumb image
address.setText(data.get(position).deviceAddress);
name.setText(data.get(position).getDisplayName());
Bitmap photo = data.get(position).getContactPhoto();
if (photo != null)
thumb_image.setImageBitmap(photo);
return vi;
}
}
Using:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the window
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.device_list);
// Set result CANCELED incase the user backs out
setResult(Activity.RESULT_CANCELED);
// Initialize the button to perform device discovery
Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doDiscovery();
v.setVisibility(View.GONE);
}
});
// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
// Find and set up the ListView for paired devices
pairedListView = (ListView) findViewById(R.id.paired_devices);
pairedDevicesList = new ArrayList<MPGDeviceDetails>();
pairedListView.setAdapter((mPairedDevicesArrayAdapter = new LazyAdapter(this,pairedDevicesList)));
pairedListView.setOnItemClickListener(mDeviceClickListener);
registerForContextMenu(pairedListView);
// Get the local Bluetooth adapter
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
// Get a set of currently paired devices
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
pairedDevicesList.add(MPGDeviceDetailsControl.getDeviceDetails(this, device.getAddress(), device.getName()));
}
}
}
MPGDeviceDetails is an object that contains all the data about a specific device.
This is the output of the "debug" command:
03-27 10:03:30.730: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.742: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@4062d608.
03-27 10:03:30.746: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.750: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@40628d78.
03-27 10:03:30.750: E/MPG(2841): Position = 0. ConvertView = android.widget.RelativeLayout@4062d608.
03-27 10:03:30.753: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@4062d608.
03-27 10:03:30.753: E/MPG(2841): Position = 0. ConvertView = android.widget.RelativeLayout@40628d78.
03-27 10:03:30.753: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@40628d78.
03-27 10:03:30.761: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@4062d608.
03-27 10:03:30.761: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.769: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@40628d78.
03-27 10:03:30.769: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.777: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.781: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@406d4af8.
03-27 10:03:30.781: E/MPG(2841): Position = 0. ConvertView = null.
03-27 10:03:30.785: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@40694970.
03-27 10:03:30.789: E/MPG(2841): Position = 0. ConvertView = android.widget.RelativeLayout@406d4af8.
03-27 10:03:30.789: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@406d4af8.
03-27 10:03:30.789: E/MPG(2841): Position = 0. ConvertView = android.widget.RelativeLayout@40694970.
03-27 10:03:30.792: E/MPG(2841): Position = 1. ConvertView = android.widget.RelativeLayout@40694970.
It looks like this:
It's funny that while all 4 buttons work, the context menu only works for the first two!
source to share
Try calling notifyDataSetChanged()
your adapter when you're done filling pairedDevicesList
:
if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
pairedDevicesList.add(MPGDeviceDetailsControl.getDeviceDetails(this, device.getAddress(), device.getName()));
}
}
((BaseAdapder)getListAdapter()).notifyDataSetChanged();
Or create an adapter after filling pairedDevicesList
.
Also try specifying that your adapter data has stable identifiers:
@Override
public boolean hasStableIds() {
return true;
}
source to share
In general, apart from regular calls to getView for each item (s), it gets called about 3 times that much because it needs to calculate the dimensions for views for the size of the scrollbar, layout, etc. As for the repeated lines, I think you should paste your code. You may be wrong.
source to share
The value currentView
should be used for optimization. Android suggests reusing views to save memory and respond faster.
When yor is currentView
not null, you can use it to display the current item.
Take a look at an example:
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
// we can't reuse view and must read it from layout
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.whatever, null);
}
// ... use v as view for row
}
source to share
You just need an clear
ArrayList before adding values to it.
here in your if
condition
if (pairedDevices.size() > 0) {
pairedDevicesList.clear();
// other code as it is
}
also you can put one log before clearing ArrayList.
Log.i("Size","ArrayList Size = "+pairedDevicesList.size());
So, you will get more ideas.
source to share