0

I am using an SQLite db to populate a RecyclerView. I want to make the RecyclerView update real-time. But I am failing. I have tried using "notifyDataSetChanged()" but it doesn't work.

Can you please also tell me how can I refresh the RecyclerView by making a "new adapter".

Which one is better "notifyDataSetChanged();" or any other?

Here is my RecyclerViewAdapter

package e.wolverine2.thewalkingapp;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;


  public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {

    Context context;
    List<World> worldList = new ArrayList<>();


    public RecyclerViewAdapter() {
        //Default Constructor
    }

    public RecyclerViewAdapter(Context context, List<World> worldList) {
        this.context = context;
        this.worldList = worldList;
    }


    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;

        view = LayoutInflater.from(context).inflate(R.layout.single_row, parent, false);
        MyViewHolder viewHolder = new MyViewHolder(view);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerViewAdapter.MyViewHolder holder, int position) {

        holder.textView_Latitude.setText("Latitude : " + String.valueOf(worldList.get(position).getLatitiude()));
        holder.textView_Longitude.setText("Longitude : " + String.valueOf(worldList.get(position).getLongitude()));
        holder.textView_Time.setText("Time : " + worldList.get(position).getDate());
        holder.textView_Location.setText("Location : \n" + "NEW LOCATION!");

    }

    @Override
    public int getItemCount() {
        return worldList.size();

    }

    public static class MyViewHolder extends RecyclerView.ViewHolder {


        TextView textView_Latitude;
        TextView textView_Longitude;
        TextView textView_Time;
        TextView textView_Location;
        CardView cardView;


        public MyViewHolder(View view) {
            super(view);

            textView_Latitude = (TextView) view.findViewById(R.id.textView_Latitude);
            textView_Longitude = (TextView) view.findViewById(R.id.textView_Longitude);
            textView_Time = (TextView) view.findViewById(R.id.textView_Time);
            textView_Location = (TextView) view.findViewById(R.id.textView_Location);
            cardView = (CardView) view.findViewById(R.id.cardView);
        }

    }


    public void updateData(List<World> newWorldList) {

        if (newWorldList != null && newWorldList.size() > 0) {


            worldList.clear();
            worldList.addAll(newWorldList);

            new MainActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    notifyDataSetChanged();
                    Toast.makeText(context, "updateData Called!", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

**EDIT :

adapter.updateData(List);

gets called in "locationChanged(double, double)" method.

MainActivity.class :**

public class MainActivity extends AppCompatActivity implements Tab1.OnFragmentInteractionListener, Tab2.OnFragmentInteractionListener, Tab3.OnFragmentInteractionListener {


    DBHelper helper;
    World world;
    Location location;
    GPSTracker tracker;
    RecyclerViewAdapter adapter;

    private Tab1 tab1;

    public static final String BROADCAST_ACTION = "e.wolverine2.thewalkingapp";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 123);

        MessageReciever reciever = new MessageReciever(new Message());

        Intent intent = new Intent(this, MyService.class);
        intent.putExtra("reciever", reciever);
        startService(intent);

        tracker = new GPSTracker(getApplicationContext());
        location = tracker.getLocation();
        helper = new DBHelper(getApplicationContext());
        tab1 = new Tab1();
        adapter = new RecyclerViewAdapter(getApplicationContext(), helper.getList());

        final TabLayout tabLayout = (TabLayout) findViewById(R.id.myTabLayout);
        tabLayout.addTab(tabLayout.newTab().setText("LOCATIONS"));
        tabLayout.addTab(tabLayout.newTab().setText("TOTAL DISTANCE"));
        tabLayout.addTab(tabLayout.newTab().setText("CALS"));

        tabLayout.getTabAt(0).setIcon(R.drawable.ic_list);
        tabLayout.getTabAt(1).setIcon(R.drawable.ic_person_pin);
        tabLayout.getTabAt(2).setIcon(R.drawable.ic_fitness_excercise);


        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        //onError();

        final ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        final PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
        viewPager.setAdapter(pagerAdapter);


        viewPager.setOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

                try {


                    if (tab.getPosition() == 0) {
                        adapter.updateData(new DBHelper(getApplicationContext()).getList());
                    }
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "Main Activity " + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
        });

    }


    public void locationChanged(double longi, double lati) {

        final Location location = new Location("");

        location.setLatitude(lati);
        location.setLongitude(longi);
        world = new World();

        String timeStamp = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(new Date());

        world.setLongitude(location.getLongitude());
        world.setLatitiude(location.getLatitude());
        world.setDate(timeStamp);
        world.setTime(timeStamp);
        world.setLocation("Anonymous");


        helper.addRow(world);
        adapter.updateData(new DBHelper(getApplicationContext()).getList());


    }

    @Override
    public void onFragmentInteraction(Uri uri) {
    }

    public class Message {

        public void displayMessage(int resultCode, Bundle resultData) {


            try {
                double longi = resultData.getDouble("longitude");
                double lati = resultData.getDouble("latitude");

                locationChanged(longi, lati);
            } catch (Exception e) {
                Toast.makeText(MainActivity.this, "TOASTY X : " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }


        }

    }


    public void onError() {
        helper.onDropTable();
        Toast.makeText(this, "TABLE DROPED!", Toast.LENGTH_SHORT).show();
    }
}
VoidMain
  • 957
  • 1
  • 8
  • 12

5 Answers5

0

I think you just need to notify adapter that worldList is changed.

public void updateData(List<World> newWorldList) {

    if (newWorldList != null && newWorldList.size() > 0) {


                worldList.clear();
                worldList.addAll(newWorldList);
                notifyDataSetChanged();

            }
        }
DimDim
  • 371
  • 1
  • 18
  • @VoidMain How is it the same? In your post you're creating an instance of MainActivity and then doing `notifydatasetchanged()` in `runOnUiThread()`. You're not supposed to create an instance of main activity and invoke runonuithread like that – denvercoder9 Mar 16 '18 at 19:32
  • I mean either way it has the same outcome, Ive tried both. notifyDataSetChange() gets called either way. but no change in the RecyclerView, – VoidMain Mar 16 '18 at 19:48
  • How you reach RecyclerView adapter? You may create an adapter instance that is not added to RecyclerView. – DimDim Mar 16 '18 at 19:57
0

You don't actually need to create an instance of MainActivity just put like this

public void updateData(List<World> newWorldList) {

            if (newWorldList != null && newWorldList.size() > 0) {
                worldList.clear();
                worldList.addAll(newWorldList);
                notifyDataSetChanged();       
            }
}

It will make your adapter refresh but you need to call this method from the activity. For this it the class where you have the adapter set you call this method like this

adapter.updateData(list); //where adapter is the instance of your adapter and list is the new data list

EDIT

I think the method is working the problem may be with the dataset

this line states that the new list must contain more than 0 elements and if it doesn't the logic with notifydatasetchanged won't be called. Please make sure in debug you're getting a new list which is not null and has elements

if (newWorldList != null && newWorldList.size() > 0)
Rainmaker
  • 10,294
  • 9
  • 54
  • 89
0

@VoidMain, Please have look at the below code and try to implement on your code.

public void updateData(List<World> newWorldList) {
    if(newWorldList == null || newWorldList.size() == 0)
        return;
    if(worldList != null && worldList.size() > 0)
        data.clear();
    worldList.addAll(newWorldList);
    notifyDataSetChanged();
}
Maulik Sinroja
  • 191
  • 1
  • 6
  • 17
0

Above the line

adapter = new RecyclerViewAdapter(getApplicationContext(), helper.getList());

do something like:

RecyclerView rv = (RecyclerView)findViewById(R.id.recycler_view); // <- substitute 'recycler_view' for the actual id

and below the first line I mentioned, do:

rv.setAdapter(adapter);

Chisko
  • 3,092
  • 6
  • 27
  • 45
0

Make sure you have implemented the Adapter in a correct way. You can have guideline with this:

public class YourAdapter extends RecyclerView.Adapter<YourAdapter.ViewHolder>{

   private final Context mContext;
   private List<YourItem> mItemList;

   public YourAdapter(Context context){
      this.mContext = context;
   }

   // Called each time you must refresh or set the list of items in your recycler view...
   public void setItemList(List<YourItem> itemList){
      this.mItemList = itemList;
      notifyDataSetChanged();
   }


   @Override
   public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

      Context context         = parent.getContext();
      LayoutInflater inflater = LayoutInflater.from(context);

      int layoutId  = R.layout.list_item_layout;

      View view = inflater.inflate(layoutId, parent, false);
      return new ViewHolder(view);
   }

   @Override
   public void onBindViewHolder(final ViewHolder holder, int position) {
      YourItem item = mItemList.get(position);
      // Render "item" values in the respective textviews, etc.
   }

   // Very important. Must check the null (just in case). Good practice.
   @Override
   public int getItemCount() {
      return mItemList != null ? mItemList.size() : 0;
   }

   public class MoviesAdapterViewHolder extends RecyclerView.ViewHolder {
      ...
   }
}

And in your fragment(onCreateView) or activity (onCreate), just do this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    // Setting adapter
    layoutManager = new GridLayoutManager(this, 2); // You can use any LayoutManager.
    mBinding.moviesRecyclerView.setLayoutManager(layoutManager);
    mBinding.moviesRecyclerView.setHasFixedSize(true);
    adapter = new MoviesAdapter(this,this);
    mBinding.moviesRecyclerView.setAdapter(adapter);

    ...

}

When you have the data ready to load to the recycler view:

// This will change the list in the adapter and "refresh" it. Remember that "setItemList" in the adapter has notifyDatasetChanged() inside.
adapter.setItemList(moviesList);