Search code examples
androidandroid-fragmentsandroid-activityandroid-recyclerviewupdates

How do I update data displayed by recyclerview which is contained in a fragment from the main activity?


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!


Solution

  • 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.