I can't wrap my head around how to update the data being displayed by a RecyclerView when the RecyclerView is in a Fragment. I have an ArrayList of Strings which is where the members I'm trying to display are stored. If I predefined the list in either the Fragment script OR the main-activity in which the Fragment is hosted, it'll get displayed on the RecyclerView just fine. But then I try to update the list at run time and I am totally lost. How do I update the list at run time??
In my application, I'm trying to scan for BT devices in the area and display the results of the scan onto this RecyclerView. I know the scan portion is working fine, I can get an ArrayList of Strings containing the MAC addresses of all scanned devices in the area. I tried passing the updated data to the Fragment script via a setter, but it crashes when it reaches that point in the code.
//This is what I use to define each device
public class Device{
private String deviceName;
public Device(String name){
this.deviceName = name;
}
}
//----------------------------------------
//This is the adapter for the RecyclerView
public class deviceAdapter extends RecyclerView.Adapter<deviceAdapter.ViewHolder>{
private List<Device> deviceList;
public deviceAdapter(List<Device> deviceList){
this.deviceList= deviceList;
}
//>>>>Data is set here<<<<
public void setData(List<Device> deviceList) {
this.deviceList = deviceList;
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView deviceTextView;
public Button connectButton;
public ViewHolder(View itemView){
super(itemView);
deviceTextView = (TextView) itemView.findViewById(R.id.deviceDescription);
connectButton = (Button)itemView.findViewById(R.id.connectButton);
}
}
@Override
public deviceAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View deviceItemLayout = inflater.inflate(R.layout.deviceitem, parent, false);
ViewHolder viewHolder = new ViewHolder(deviceItemLayout);
return viewHolder;
}
@Override
public void onBindViewHolder(deviceAdapter.ViewHolder viewHolder, int position) {
viewHolder.deviceTextView.setText(deviceList.get(position).getName());
}
@Override
public int getItemCount() {
return data.length;
}
//---------------------------------------
//Fragment that contains the RecyclerView
public class HomeFragment extends Fragment {
private List<Device> deviceList = new ArrayList<>();
//>>>> Adapter is created here<<<<
deviceAdapter adapter = new deviceAdapter(deviceList);
RecyclerView rvDevices;
public HomeFragment(List<Device> deviceList){
this.deviceList = deviceList;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
rvDevices = (RecyclerView) rootView.findViewById(R.id.rvDevices);
rvDevices.setLayoutManager(new LinearLayoutManager(getContext()));
rvDevices.addItemDecoration(new DividerItemDecoration(rvDevices.getContext(), DividerItemDecoration.VERTICAL));
//>>>>Adapter is set here<<<<
rvDevices.setAdapter(adapter);
rvDevices.setItemAnimator(new DefaultItemAnimator());
return rootView;
}
public void updateFragmentData(List<Device> deviceL){
this.deviceList = deviceL;
if(adapter != null){
adapter.setData(deviceList);
adapter.notifyDataSetChanged();
}
}
}
//-------------------------------------------------
//my main-activity (relevant parts)
public class Interface extends AppCompatActivity {
//Located at onCreate()
homeFragment = new HomeFragment(scannedDevicesMAC);
fragment = homeFragment
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab){
switch(tab.getPosition()){
case 0:
fragment = homeFragment;
break;
case 1:
fragment = new JavaFragment();
break;
case 2:
fragment = new AndroidFragment();
break;
}
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frameLayout, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
}
//...
});
final BroadcastReceiver mReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent incoming)
{
String iAction = incoming.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(iAction)){
BluetoothDevice tmpScannedDevices = incoming.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String tmpMACAddress = tmpScannedDevices.getAddress();
int tmpRSSI = incoming.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
String tmpDeviceName = tmpScannedDevices.getName();
scannedDevicesBTOs.add(tmpScannedDevices);
scannedDevicesMAC.add(new Device(tmpMACAddress));
//This is the list I'm trying to display
scannedDevicesList.add("Name: " + tmpDeviceName + " || Address: " + tmpMACAddress + " || RSSI:" + tmpRSSI);
if(fragment != null){
homeFragment.updateFragmentData(scannedDevicesMAC);
}
}
}
};
public void scanDevices(){
if(mBTAdapter.isEnabled()){
scannedDevicesList.clear();
scannedDevicesMAC.clear();
scannedDevicesBTOs.clear();
if(mBTAdapter.isDiscovering()){
mBTAdapter.cancelDiscovery();
}
Log.d("scanDevices|scanDevices", "Scanning for BT devices");
mBTAdapter.startDiscovery();
}
}
As mentioned, I expect the RecyclerView that's held in a Fragment to display the contents of an ArrayList of Strings which is being updated from main-activity. I'm at the end of my line, been trying to figure this out for a few days!
In your Fragment
create the function below
public void updateFragmentData(Device[] data){
this.data = data;
if(adapter != null){
adapter.setData(data);
adapter.notifyDataSetChanged();
}
}
and then from your Activity
call
if(fragment != null){
fragment.updateFragmentData(data);
}
The above is a quick solution. An even better one would be to use interfaces to update the fragment
.
Updated
In your activity you should declare these three
HomeFragment homeFragment;
JavaFragment javaFragment;
AndroidFragment androidFragment;
and then in the onCreate()
initialise them
homeFragment = new HomeFragment(data);
javaFragment = new JavaFragment();
androidFragment = new AndroidFragment();
and then you need to update only the homeFragment
so the code will look like this
if(homeFragment != null){
homeFragment.updateFragmentData(data);
}
public void setData(List<Device> deviceList) {
this.deviceList.clear;
this.deviceList.addAll(deviceList);
notifyDatasetChanged();
}
So you do not need to notifyDatasetChanged in your fragment now.