I have two lists that use the same extended BaseAdapter in the same activity layout, but only one updates when I call notifyDataSetChanged (I call it for both of course). I’ve been searching for days and cannot find the problem.
The activity gets the paired and available Bluetooth devices and lists them separately, I used the BluetoothChat sample, but I needed custom rows and had to change it some.
The main problem is commented with “This only works when the other (just below) is commented“, Ctrl+F to get there faster.
Here’s my special BaseAdapter (might be useless, but I post it anyway):
/**
* Device list base adapter to show the devices in a custom ListView.
*/
public class DeviceListBaseAdapter extends BaseAdapter
{
private static ArrayList<Device> deviceArrayList;
private LayoutInflater mInflater;
public DeviceListBaseAdapter(Context context, ArrayList<Device> results)
{
deviceArrayList = results;
mInflater = LayoutInflater.from(context);
}
public int getCount()
{
return deviceArrayList.size();
}
public Object getItem(int position)
{
return deviceArrayList.get(position);
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if(convertView == null)
{
convertView = mInflater.inflate(R.layout.device_row_view, null);
holder = new ViewHolder();
holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
holder.tvAddress = (TextView) convertView.findViewById(R.id.tvAddress);
holder.tvSignal = (TextView) convertView.findViewById(R.id.tvSignal);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.tvName.setText(deviceArrayList.get(position).getName());
holder.tvAddress.setText(deviceArrayList.get(position).getAddress());
if(!deviceArrayList.get(position).getSignal().equals("0"))
{
holder.tvSignal.setText(deviceArrayList.get(position).getSignal() + "dBm");
}
return convertView;
}
static class ViewHolder
{
TextView tvName;
TextView tvAddress;
TextView tvSignal;
}
}
And the activity that uses it:
public class DeviceSelectActivity extends Activity
{
private ArrayList<Device> devAvailableList, devPairedList;
private DeviceListBaseAdapter devAvailableListAdapter, devPairedListAdapter;
private ListView devAvailableListView, devPairedListView;
private Button bFindDevices;
private BluetoothAdapter bluetoothAdapter;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.device_select);
// Setup Bluetooth devices lists with custom rows
devPairedListView = (ListView) findViewById(R.id.lvPairedDevices);
devPairedList = new ArrayList<Device>();
devPairedListAdapter = new DeviceListBaseAdapter(this, devPairedList);
devPairedListView.setAdapter(devPairedListAdapter);
// I commented this to see the behavior with only the first list
// devAvailableListView = (ListView) findViewById(R.id.lvAvailableDevices);
// devAvailableList = new ArrayList<Device>();
// devAvailableListAdapter = new DeviceListBaseAdapter(this, devAvailableList);
// devAvailableListView.setAdapter(devAvailableListAdapter);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Register a receiver to handle Bluetooth actions
registerReceiver(Receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(Receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
startDiscovery();
}
public void startDiscovery()
{
// Show search progress
bFindDevices.setText(R.string.searching);
bFindDevices.setEnabled(false);
// Remove title for available devices
findViewById(R.id.tvAvailableDevices).setVisibility(View.GONE);
// Get a set of currently paired devices
devPairedList.clear();
devPairedListAdapter.notifyDataSetChanged();
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if(pairedDevices.size() > 0)
{
findViewById(R.id.tvPairedDevices).setVisibility(View.VISIBLE);
for(BluetoothDevice device : pairedDevices)
{
devPairedList.add(new Device(device.getName(), device.getAddress(), (short) 0));
// This only works when the other (just below) is commented
devPairedListAdapter.notifyDataSetChanged();
}
}
// devAvailableList.clear();
// devAvailableListAdapter.notifyDataSetChanged();
bluetoothAdapter.startDiscovery();
}
// add found device to the devices list
private final BroadcastReceiver Receiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action))
{
// Found a device in range
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Device foundDevice = new Device(device.getName(), device.getAddress(), intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE));
// If it's not a paired device add it to the list
if(device.getBondState() != BluetoothDevice.BOND_BONDED)
{
// devAvailableList.add(foundDevice);
// Signal list content change
// devAvailableListAdapter.notifyDataSetChanged();
// Make the available devices title visible
findViewById(R.id.tvAvailableDevices).setVisibility(View.VISIBLE);
}
}
else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action))
{
// When finished (timeout) remove the progress indicator
bFindDevices.setText(R.string.search);
bFindDevices.setEnabled(true);
}
}
};
}
I commented everything from the available devices list to make sure the paired devices list gets populated. By guess I’d say there’s probably some lost reference going on, but as I dont fully understand how lists works yet I can’t find it.
The thing is that when I try to populate both lits and when calling .add() the devices actually get added to the lists (.size() returns the right number), but the first one doesn’t get refreshed.
Solution
When making such re-usable classes as
DeviceListBaseAdapterremember to never set local variables tostatic. Whatstaticdoes is to share this particular variable between all instances of that object, thus I had 2 instances ofDeviceListBaseAdapter, but only onedeviceArrayListand that’s why only one list worked at a time. Beware of fast copy-pastes.