How to use notifyDataSetChanged from a different class ? - java

I have an Http call class I made to send some images. I want these requests to be done in the background so the user can continue doing whatever they want in the app.
When the call has finished executing, I want to remove the Item from the listview and update it.
Here is my code.
public HttpCall(ArrayList<Jobs> jobs, ArrayList<GridImages> images, ArrayList<GridImages> signatures, Context context,TwoColumnListAdapter customadapter) {
this.jobs = jobs;
this.images = images;
this.signatures = signatures;
this.context = context;
listView = (SwipeMenuListView) ((Activity) context).findViewById(R.id.listView);
adapter = customadapter;
mydb = new Database_Helper(context);
}
public void SendALL(){
//Calls the http requests and then executes the update for the listview.
listView.post(new Runnable() {
#Override
public void run() {
// Intent intent = new Intent(context,AddJob.class);
// context.startActivity(intent);
listView.setAdapter(adapter);
adapter.remove(jobs.get(0));
adapter.notifyDataSetChanged();
}
});
}
I could start a new Intent and then it will show that it updated the listview as the AddJob class makes a call to the database and updates the listview.

Related

Calling the Refresh() method which is in the main activity from another activity [duplicate]

This question already has answers here:
How to refresh listview in fragment when onbackpressed()
(4 answers)
Closed 1 year ago.
I'm developing an android app using java, but I have the problem below.
I have the main activity where there is a button "add" and a listview. When I click the add button, it will open another activity where I can add items to the listview. After adding this item, when I click the back button from the second activity, I want that the Refresh() method from the main activity to be executed to add this item directly to the listview in the main activity. I can't find a way to solve it. I tried to make this method as static but lot of errors appear, and the all the app is stopped. Also I tried to create new instance of the main activity in the onBackPressed() method of the second activity, but the app has also stopped. Anyone can help me to solve this problem?
Thank you.
Read this: https://developer.android.com/training/basics/intents/result then add a call to your refresh method in your MainActivity after you get a result back indicating that the second activity is done.
I believe that the following working example shows how you can accomplish what you want :-
MainActivity (the initial activity) :-
public class MainActivity extends AppCompatActivity {
public static final int ACTIVITY1_REQUEST_CODE = 999;
public static final String EXTRA_MYARRAY = "extra_myarray";
private Button next;
private ListView listView;
ArrayAdapter<String> adapter;
ArrayList<String> myarray = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
next = this.findViewById(R.id.next);
listView = this.findViewById(R.id.listview);
// Prepare the Button's onClickListener to start the other activity
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(view.getContext(),Activity2.class);
// prepare to pass the data to the other activity
i.putExtra(EXTRA_MYARRAY,myarray);
// Start the other activity
startActivityForResult(i,ACTIVITY1_REQUEST_CODE);
}
});
// Prepare the data
myarray.add("a");
myarray.add("b");
myarray.add("c");
// Output data to the log (to show what happens)
refresh(myarray,"INITIAL", false);
}
// Prepare to receive and handle the modified data when returning from other activity
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTIVITY1_REQUEST_CODE && resultCode == RESULT_OK) {
myarray.clear();
for(String s: data.getStringArrayListExtra(EXTRA_MYARRAY)) {
myarray.add(s);
}
refresh(data.getStringArrayListExtra(EXTRA_MYARRAY),"RESUMED",true);
}
}
/**
* Refresh
* #param modifiedData The modified data to be applied (see modify) as an ArrayList<String>
* #param tagExtra String used to indicate where the refresh was called from
* #param modify flag to indicate whether or not to rebuild the data
* if coming from the this activity then clear and add would
* empty the array and add nothing
*/
private void refresh(ArrayList<String> modifiedData, String tagExtra, boolean modify) {
if (modify) myarray.clear();
for(String s: modifiedData) {
if (modify) myarray.add(s);
Log.d("MA_" + tagExtra,"Value is " + s);
}
refreshListView();
}
private void refreshListView() {
if (adapter == null) {
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,myarray);
listView.setAdapter(adapter);
} else {
adapter.notifyDataSetChanged();
}
}
}
Activity2 the invoked/2nd activity (which modifies the list and returns that modified list to the parent when the button is clicked) :-
public class Activity2 extends AppCompatActivity {
private Button finish;
private ArrayList<String> myarray = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_2);
finish = this.findViewById(R.id.finish);
// Prepare the Button's onCLickListener
finish.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent();
// Prepare to return the data
i.putExtra(MainActivity.EXTRA_MYARRAY,myarray);
// Indicate that all is OK
setResult(RESULT_OK,i);
// Finish this activity and thus pass control back to the parent activity
finish();
}
});
// Modify the data
myarray = this.getIntent().getStringArrayListExtra(MainActivity.EXTRA_MYARRAY);
myarray.add("d");
}
}
Comments should explain the code
Note this method does use the deprecated (startActivityForResult) so you may wish to consider looking at Getting a result from an activity
Result
When run the App displays :-
Clicking the NEXT button takes you to the 2nd Activity :-
Clicking the FINISH button (the activity adds a new element) returns to the MainActivity which is now :-
i.e. the new element is displayed accordingly in the ListView

infinite while loop, while AsyncTask is not finish

I have a SplashScreen Activity which call Asynctask Class to get information in internet.
I want to wait while my Asynctask is not finish (time during on internet speed connection)
My activity:
public static boolean test = true;
[...]
final Liste en_ce_moment = new Liste("En ce moment au cinéma", nowMovie);
mesListes.add(en_ce_moment);
//call my Asynctask file
fetchNowMovie process = new fetchNowMovie();
process.execute();
while(test)
{
}
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
My Asynctask:
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
SplashScreenActivity.test = false;
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Logically, the boolean became false in onPostExecute so the while loop stop and the intent have to start but the while loop never stop...
Let's do what you want to do in a safe way by using simple interface logic:
So we added our simple interface and we re-define your MyAsnycTask class's constructor like so:
public class MyAsnycTask extends AsyncTask
{
OnTaskFinished listener;
// Our simple interface
public interface OnTaskFinished {
void TimeToNextActivity();
}
// Your MyAsnycTask class constructor
public MyAsnycTask(OnTaskFinished l) {
listener = l;
}
. . .
As a last line of code in onPostExecute(), we're done whatever we're doing. So tell this via our listener:
listener.TimeToNextActivity();
To use our interface that we added earlier, your Activity must implements it. So we implements it. And in implemented method, we go to next Activity with Intent:
public class MyActivity extends Activity
implements MyAsnycTask.OnTaskFinished
{
#Override
public void TimeToNextActivity()
{
// Here go to next activity
Intent i = new Intent(this, MainActivity.class);
startActivity(i);
}
As we modified our MyAsnycTask class's constructor, we must initialize it like this:
MyAsnycTask process = new MyAsnycTask(this);
process.execute();
This looks like a problem hiding inside another problem, but to get through the first-level issue you could try using CountDownLatch instead of a static boolean:
CountDownLatch latch = new CountDownLatch(1);
fetchNowMovie process = new fetchNowMovie(latch);
process.execute();
latch.await();
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
You'll have to accept the latch as part of your AsyncTask's constructor:
private final CountDownLatch latch;
public fetchNowMovie(CountDownLatch latch) {
this.latch = latch;
}
// ...
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
latch.countDown();
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Try to use very simple library:
https://github.com/Arasthel/AsyncJobLibrary
Just start your Splash activity:
Intent i = new Intent(context, SplashScreenActivity.class);
startActivity(i);
In "onCreate" method of Splash activity do, what you need in background and after mission complete, do on main thread (update list):
AsyncJob.doInBackground(new AsyncJob.OnBackgroundJob() {
#Override
public void doOnBackground() {
//load from local DB or http-request:
List<Movie> movieList = fetchNowMovie();
//You can convert movieList to Json string, for example and save in SharedPreferences. Or you can use local DB for saving new movies.
// Send the result to the UI thread and show it
AsyncJob.doOnMainThread(new AsyncJob.OnMainThreadJob() {
#Override
public void doInUIThread() {
Intent i = new Intent(splashScreenContext, MainActivity.class);
startActivity(i);
//load movies from shared preferences or local Database in MainActivity (onCreate)
}
});
}
});
Connect library (build.gradle in app-project directory):
dependencies {
...
implementation 'com.arasthel:asyncjob-library:1.0.3'
...
}

How to detect each RecyclerView item after it is displayed

I want to detect each item in my RecylerView after it is displayed to the user.
Basically, I am trying to play a sound on each item after it is loaded on the screen.
But I am not able to detect whenever each item is loaded on the screen! Is there any method I have to call to detect each item rendered
E.g 1st RecyclerView item displayed -> play sound
2st RecyclerView item displayed -> play sound...
My Adapter class looks like this -
public class AdapterListAnimation extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Multiples> items = new ArrayList<>();
private Context ctx;
private OnItemClickListener mOnItemClickListener;
private int animation_type = 0;
.........
.........
I am calling this initComponent() method from onCreated() method. Can you give advice on what should I do to achieve my goal as described above?
private void initComponent() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
items = DataGenerator.getPeopleData(this,of,value);
setAdapter();
/* MediaPlayer mp=MediaPlayer.create(this, R.raw.sword);
if (mp.isPlaying()) {
mp.stop();
mp.release();
mp = MediaPlayer.create(this, R.raw.sword);
} mp.start();*/
}
private void setAdapter() {
// Set data and list adapter
mAdapter = new AdapterListAnimation(this, items, animation_type);
recyclerView.setAdapter(mAdapter);
// on item list clicked
mAdapter.setOnItemClickListener(new AdapterListAnimation.OnItemClickListener() {
#Override
public void onItemClick(View view, com.math.multiplication.model.Multiples obj, int position) {
Snackbar.make(parent_view, "Item " + obj.first + " clicked", Snackbar.LENGTH_SHORT).show();
}
});
}
you need to override onViewAttachedToWindow and onViewDetachedFromWindow. but for detecting holer type you need getItemViewType() just like that:
public class PostAdapter extends RecyclerView.Adapter {
#Override
public int getItemViewType(int position) {
switch (types.get(position)){
case 1:
return 1;
case 2:
return 2;
default:
return position;
}
}
#Override
public void onViewAttachedToWindow(#NonNull RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (holder.getItemViewType() == 1){
//play song
}
}
#Override
public void onViewDetachedFromWindow(#NonNull RecyclerView.ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
if (holder.getItemViewType() == 1){
//pause song
}
}
You need to add listener for TTS. Then update your RecyclerView to show right image when speech starts and ends.
I've created a test project to show how it can be implemented. Here you can see how it works. Here is my github repository.
Here is main part of my MainActivity class.
private void initTts() {
tts = new TextToSpeech(this, this);
tts.setLanguage(Locale.US);
tts.setOnUtteranceProgressListener(new MyListener());
}
#Override
public void onInit(int status) {
playSound(0);
}
private void playSound(int index) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, String.valueOf(index));
tts.speak(data.get(index), TextToSpeech.QUEUE_ADD, hashMap);
}
class MyListener extends UtteranceProgressListener {
#Override
public void onStart(String utteranceId) {
int currentIndex = Integer.parseInt(utteranceId);
mMainAdapter.setCurrentPosition(currentIndex);
handler.post(new Runnable() {
#Override
public void run() {
mMainAdapter.notifyDataSetChanged();
}
});
}
#Override
public void onDone(String utteranceId) {
int currentIndex = Integer.parseInt(utteranceId);
mMainAdapter.setCurrentPosition(-1);
handler.post(new Runnable() {
#Override
public void run() {
mMainAdapter.notifyDataSetChanged();
}
});
if (currentIndex < data.size() - 1) {
playSound(currentIndex + 1);
}
}
#Override
public void onError(String utteranceId) {
}
}
You can simply use onViewAttachedToWindow(VH) in your adapter.
See https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onViewAttachedToWindow(VH)
Update:
As you know RecyclerView will be call OnBindViewHolder only once for each item.
RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined.
So you can use onViewAttachedToWindow
(onViewAttachedToWindow) Called when a view created by this adapter has been attached to a window.
This can be used as a reasonable signal that the view is about to be seen by the user. If the adapter previously freed any resources in onViewDetachedFromWindow those resources should be restored here.
You can use it like this:
public class Adapter extends RecyclerView.Adapter<MyViewHolder> {
// rest of your code
#Override
public void onViewAttachedToWindow(MyViewHolder holder) {
super.onViewAttachedToWindow(holder);
// play your sound
}
}
Hope it helps!
If I understood your problem correctly, you want to play a sound when an item in the RecyclerView is loaded.
Hence I would like to think of a workaround here. You might consider having an item added after the sound for the previous item has been played.
I would like to provide a pseudo code for what I am trying to explain. You might consider using the MediaPlayer.OnCompletionListener for listening if your sound has stopped playing and then add the next item to the RecyclerView and then call notifyDataSetChanged on your adapter to load the next item in your RecyclerView.
Hence you might want the following modification in your adapter first.
// Declare a function in your adapter which adds new item in the RecyclerView
public void addItem(Multiples item) {
items.add(item);
notifyItemInserted(items.size());
}
Now in your Activity where you set up the RecyclerView, you need to have the following arrays.
ArrayList<Multiples> allItems = new ArrayList<Multiples>(); // This list will contain all the data
int soundToBePlayed = -1; // Initialize the value with -1
ArrayList<Integer> soundList = getAllSoundsInAList();
Modify your functions as follows.
private void initComponent() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
allItems = DataGenerator.getPeopleData(this,of,value);
setAdapter();
}
private void setAdapter() {
mAdapter = new AdapterListAnimation(this, animation_type); // Do not pass the items list
recyclerView.setAdapter(mAdapter);
// .. Your item click listener goes here
}
As the items was removed from the constructor, you need to modify the constructor of your adapter as well. Just remove the variable which takes the items. Because we will be adding elements to the items later.
Then you need to write another function which needs to be called in your onCreate function of your activity after your call your initComponent function. The function should look like the following.
private void addItemInRecyclerViewAndPlaySound() {
soundToBePlayed++;
adapter.addItem(allItems.get(soundToBePlayed));
recyclerView.scrollToPosition(adapter.getItemCount() - 1);
Integer soundId = soundList.get(soundToBePlayed);
MediaPlayer mp = MediaPlayer.create(this, soundId);
if (mp.isPlaying()) {
mp.stop();
mp.release();
mp = MediaPlayer.create(this, soundId);
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
// Call this function again to add next item
// and play the next sound at the same time
if(soundToBePlayed < allItems.size() - 1)
addItemInRecyclerViewAndPlaySound();
}
});
}
mp.start();
}
I have not tested the code, however, I think you get the idea already. This can be implemented gracefully using a interface inside the adapter as well. The imeplemntation technique is your choice.

Updating list view periodically

I currently have a List View that takes data from an ArrayList that gets update every certain amount of time in the MainActivity. I want to update the List View every certain amount of time, let say 2 seconds. The problem is that I have this Adapter on a fragment and if I call the notifyDataSetChanged(); from the Adapter, the View only gets update if I switch between the MainActivity and the fragment. I want that this List view refreshes every 2 seconds while I'm having the fragment in View. I have tried to run a TimerTask on the Adapter class, but I get an exception:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views.
I guess you can only update the view from the MainActivity that has the fragment attached.
This how I was running the timer:
public void setTimer(int seconds) {
timer = new Timer();
timer.schedule(new RemindTask(), seconds * 1000);
}
public class RemindTask extends TimerTask {
//Refreshing list view
public void run() {
notifyDataSetChanged();
timer.cancel();
setTimer(2);
}
}
Would it be safe to create an instance of the adapter on the MainActivity and call notifyDataSetChanged() every certain amount of time?
You can Create one broadcast receiver in Fragment. with certain action name.
Lets say "refresh_data".
Then send this broadcast from MainActivity.
Example Code.
public class MyFragment extends Fragment{
private BroadcastReceiver refreshData = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
myAdapter.notifyDataSetChanged();
}
};
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(getContext()).registerReceiver(refreshData,
new IntentFilter("refresh_Data"));
}
#Override
public void onPause() {
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(refreshData);
super.onPause();
}
}
In Activity call following method whenever your list is updated
Intent intent = new Intent("refresh_data");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

Periodic update items on a RecyclerView

I have an Activity with a Recycler view handling cards. I have a SwipeRefreshLayout so when I swipe it updates the RecyclerView with new content. So far, so good.
Howerver, I want to update the RecyclerView every X seconds so if, for instance, I leave the activity with the recylcerview opened and I forgot to swipe it, it would as well update by itself.
To do that, I thought something like this:
( I ommitted necessary code ).
My main Activity which contains the recyclerview schedules a Job like this:
private void scheduleJob() {
ComponentName serviceName = new ComponentName(this, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresDeviceIdle(false)
.setRequiresCharging(false)
.setPeriodic(3000)
//.setOverrideDeadline(400) // Remove comment for faster testing.
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
Toast.makeText(this,"Start", Toast.LENGTH_LONG).show();
} } }
Inside MyJobService I through a Broadcast to my Activity, to let it know it has to update the content of the RecylcerView.
The responsable of receiving the broadcast is an inner class like this:
public static class MyReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context,"event",Toast.LENGTH_SHORT).show();
}
}
Problem is on my main Activity I hold two private variable called items (which is the ArrayList ) and adapter ( which is the adapter of the recylcerview) I pass items to the adapter to update the recyclerView content.However, as I am in a static inner class I can’t access outter class variables.
Whats the correct way to do something like this? I think I am messing too much and I guess there must be an easiest and more straight forward way to accomplish what I want.
Thank you very much
Well, turns out I was complicating things too much. It can be done with a simple Handler like this:
// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
// Do something here on the main thread
Toast.makeText(getApplicationContext(),"Update",Toast.LENGTH_SHORT).show();
ItemsDS itemsDS = new ItempsDS(getApplicationContext());
items = itemsDS.getItems();
adapter.clear();
adapter.addAll(items);
handler.postDelayed(runnableCode, 1000);
}
};
There are Two ways to do this thing:
First is as mentioned by #akshayBhat, you can do it by removing static keyword from BroadcastReceiver class:
ArrayList<String> items;
Adapter adapter;
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}
Second is to pass the reference of current activity state to the BroadCast Receiver and access ClassVariables there using that refernce;
ArrayList<String> items;
Adapter adapter;
public static class MyReceiver extends BroadcastReceiver {
AboutUs mContext;
#Override
public void onReceive(Context context, Intent intent) {
mContext = (AboutUs)context;
DataSource dataSource = new DataSource(context);
mContext.items = dataSource.getItems(); // can't do this due to outter class variable
mContext.adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}

Categories

Resources