Synchronizing of threading - java

I have a fragment class that describes RecyclerView. It takes array for creating elements. The array is forming by parsing JSON. When I use good internet connection everything is OK, and I can see desirable list of items. But using low-speed connection my UI is empty.
I realize that there are some problems with threads, but I haven't enough knowledge to fix my problem.
Here is a code:
public class ListVideo extends Fragment {
private int loadLimit = 9;
private RecyclerView recyclerView;
private RecyclerAdapter adapter;
private LinearLayoutManager linearLayoutManager;
final OkHttpClient client = new OkHttpClient();
List<VideoData> videoList;
List<String> videoDataList;
JSONArray json_array_list_of_videos;
int counter = 0;
int offset;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.listvideofragment, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
videoList = new ArrayList<>();
videoDataList = new ArrayList<>();
recyclerView = (RecyclerView) view.findViewById(R.id.list);
loadData(offset);
createRecycleView();
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(
linearLayoutManager) {
#Override
public void onLoadMore(int offset) {
// do somthing...
loadMoreData(offset);
}
});
}
private void loadMoreData(int offset) {
loadLimit += 10;
loadData(offset);
adapter.notifyDataSetChanged();
}
private void loadData(final int offset) {
try {
Request request = new Request.Builder()
.url("http://video.motti.be/api/video.getVideoList?offset=" +
offset
+ "&limit=20")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException throwable) {
throwable.printStackTrace();
}
#Override
public void onResponse(Response response) throws IOException {
try {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
String json_string_obj = response.body().string();
JSONObject url = new JSONObject(json_string_obj);
json_array_list_of_videos = url.getJSONArray("data");
System.out.println(json_array_list_of_videos.toString());
for (int y = 0; y <= 9; y++) {
if (json_array_list_of_videos.get(y).toString().equals("A9knX0GXrg")) {
videoDataList.add("6kS9Tt1e47g");
} else {
videoDataList.add(json_array_list_of_videos.get(y).toString());
System.out.println("++++++" + json_array_list_of_videos.get(y).toString());
}
}
for (int i = counter; i <= loadLimit; i++) {
if (videoDataList == null) {
return;
} else {
VideoData next_queue_id = new VideoData(videoDataList.get(i));
videoList.add(next_queue_id);
counter++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
public void createRecycleView() {
adapter = new RecyclerAdapter(videoList, getContext());
linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
}
I understand, that I get Response later then new adapter creates.For the lack of knowledge, as I sad, I have no idea how to make thread with onResponse method to wait.
Hope that you won't find this question as dull or stupid and will help me.
Thank you in advance!

You need to notify the adapter after you modify its list (videoList).
Currently the loadMoreData(int offset) method doesn't guaranty that because the loadData(offset); method can return before the list is modified (the request is processed asynchronously).
What you could do is this:
Remove the adapter.notifyDataSetChanged(); statement from the loadMoreData(int offset) method and add it to the onResponse(Response response) method.
Example:
#Override
public void onResponse(Response response) throws IOException {
try {
...
for (int i = counter; i <= loadLimit; i++) {
if (videoDataList == null) {
return;
} else {
VideoData next_queue_id = new VideoData(videoDataList.get(i));
videoList.add(next_queue_id);
counter++;
}
}
ListVideo.this.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
This approach can create other problems because the videoList can be modified by multiple threads at the same time. You need to find a way to synchronize the access to this list or use a thread safe list.

Related

RecyclerView empty after data update

I am developing Android app which obtains information about restaurants from server and shows them in RecyclerView. When first package of information is obtained from server, everything works as expected, but, when I change search criteria and request new package of information from server, RecyclerView becomes blank. I used Toasts to debug what is coming from server and I am convinced that data is properly formatted. Also, variables that are used for accepting the data are also properly handled in code, according to my observations. Do you maybe know why my RecyclerView is empty when second package of data should be shown? Here is the code.
AfterLoginActivity.java
public class AfterLoginActivity extends AppCompatActivity {
/* interface main elements */
LinearLayout afterLoginLayout;
LinearLayout restaurantListLayout;
EditText restaurantEditText;
Button findRestaurantButton;
LoadingDialog loadingDialog;
AlphaAnimation loadingAnimation;
RecyclerView restaurantsRecyclerView;
int recycler_set = 0;
Button signOutButton;
GoogleSignInClient mGoogleSignInClient;
MyAdapter myAdapter = null;
/* server-client communication data */
public static String UploadUrl = "https://gdedakliknem.com/klopator.php";
public static String[] received;
String restaurants[] = new String[40];
String logos_as_strings[] = new String[40];
Bitmap logos[] = new Bitmap[40];
int num_of_elements = 0;
int data_received = 0;
/* user data */
String person_email;
String person_name;
String restaurant;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_after_login);
/* interface main elements */
afterLoginLayout = findViewById(R.id.afterLoginLayout);
restaurantListLayout = findViewById(R.id.restaurantListLayout);
restaurantEditText = findViewById(R.id.restaurantEditText);
findRestaurantButton = findViewById(R.id.findRestaurantButton);
restaurantsRecyclerView = findViewById(R.id.restaurantsRecyclerView);
signOutButton = findViewById(R.id.signOutButton);
loadingAnimation = new AlphaAnimation(1F, 0.8F);
loadingDialog = new LoadingDialog(AfterLoginActivity.this);
/* UPDATING INTERFACE ELEMENTS */
/* execution thread */
final Handler handler = new Handler();
final int delay = 2000; // 1000 milliseconds == 1 second
handler.postDelayed(new Runnable() {
public void run() {
/* check if recycler view is set */
if(recycler_set == 0){
/* if not, check if there is data to fil it with */
if(data_received == 1){
/* convert received strings to images */
for(int i = 0; i < num_of_elements; i++){
logos[i] = stringToBitmap(logos_as_strings[i]);
}
/* fill interface elements */
loadingDialog.dismissDialog();
myAdapter = new MyAdapter(AfterLoginActivity.this, restaurants, logos, num_of_elements);
restaurantsRecyclerView.setAdapter(myAdapter);
restaurantsRecyclerView.setLayoutManager(new LinearLayoutManager(AfterLoginActivity.this));
afterLoginLayout.setVisibility(View.GONE);
restaurantListLayout.setVisibility(View.VISIBLE);
recycler_set = 1;
}
}
handler.postDelayed(this, delay);
}
}, delay);
/* catch restaurant name from user's entry */
findRestaurantButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
restaurant = restaurantEditText.getText().toString();
if(!restaurant.isEmpty()){
view.startAnimation(loadingAnimation);
loadingDialog.startLoadingDialog();
sendRequest();
} else{
Toast.makeText(AfterLoginActivity.this, "Unesite naziv restorana!", Toast.LENGTH_LONG).show();
}
}
});
/* enable signing out */
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
signOutButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.signOutButton:
signOut();
break;
}
}
});
/* obtaining email */
GoogleSignInAccount acct = GoogleSignIn.getLastSignedInAccount(this);
if (acct != null) {
person_email = acct.getEmail();
person_name = acct.getDisplayName();
}
}
#Override
public void onBackPressed() {
afterLoginLayout.setVisibility(View.VISIBLE);
restaurantsRecyclerView.setVisibility(View.GONE);
data_received = 0;
recycler_set = 0;
}
private void signOut() {
mGoogleSignInClient.signOut()
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Toast.makeText(AfterLoginActivity.this, "Signed out", Toast.LENGTH_LONG).show();
finish();
}
});
}
public void sendRequest(){
StringRequest stringRequest = new StringRequest(Request.Method.POST, UploadUrl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
String Response = jsonObject.getString("response");
if (Response.equals("Restaurant not found")){
loadingDialog.dismissDialog();
Toast.makeText(getApplicationContext(), "Uneti restoran ne postoji u sistemu! Proverite da li ste dobro napisali naziv", Toast.LENGTH_LONG).show();
} else{
received = Response.split(";");
if (received.length > 0){
data_received = 1;
num_of_elements = received.length / 2;
//Toast.makeText(getApplicationContext(), "num of elements: " + num_of_elements, Toast.LENGTH_LONG).show();
for(int i = 0; i < num_of_elements; i++){
logos_as_strings[i] = received[i*2];
restaurants[i] = received[i*2+1];
//Toast.makeText(getApplicationContext(), "restaurants: " + restaurants, Toast.LENGTH_LONG).show();
}
} else{
loadingDialog.dismissDialog();
Toast.makeText(getApplicationContext(), "Greška u prijemu", Toast.LENGTH_LONG).show();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(final VolleyError error) {
//volleyError = error;
}
})
{
#Override
protected Map<String, String> getParams() throws AuthFailureError {
//Toast.makeText(getApplicationContext(), "get params", Toast.LENGTH_LONG).show();
Map<String, String> params = new HashMap<>();
params.put("control", "find_restaurant");
params.put("restaurant", restaurant);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(AfterLoginActivity.this);
requestQueue.add(stringRequest);
}
public static Bitmap stringToBitmap(String encodedString) {
try {
byte[] encodeByte = Base64.decode(encodedString, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
} catch (Exception e) {
e.getMessage();
return null;
}
}
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
String restaurants[];
Bitmap logos[];
Context context;
int num_of_elements;
public MyAdapter(Context ct, String rests[], Bitmap lgs[], int num){
context = ct;
restaurants = rests;
logos = lgs;
num_of_elements = num;
Toast.makeText(context, Integer.toString(restaurants.length), Toast.LENGTH_LONG).show();
for(int i = 0; i < restaurants.length; i++){
Toast.makeText(context, restaurants[i], Toast.LENGTH_SHORT).show();
}
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.restaurant_item_layout, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyAdapter.MyViewHolder holder, int position) {
holder.restaurantNameTextView.setText(restaurants[position]);
holder.restaurantLogoImageView.setImageBitmap(logos[position]);
holder.restaurantItemLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, AfterPickingRestaurantActivity.class);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
logos[position].compress(Bitmap.CompressFormat.PNG, 50, bs);
intent.putExtra("byteArray", bs.toByteArray());
intent.putExtra("picked_restaurant_name", restaurants[position]);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
/*
int i = 0;
if (restaurants != null){
while(restaurants[i] != null){
i++;
}
} else{
i = restaurants.length;
}
return i;
*/
return num_of_elements;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView restaurantNameTextView;
ImageView restaurantLogoImageView;
LinearLayout restaurantItemLayout;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
restaurantNameTextView = itemView.findViewById(R.id.restaurantNameTextView);
restaurantLogoImageView = itemView.findViewById(R.id.restaurantLogoImageView);
restaurantItemLayout = itemView.findViewById(R.id.restaurantItemLayout);
}
}
Add this lines after getting new data
myAdapter.notifyDataSetChanged()
Your Adapter Already set with RecyclerView in OnCreate Method.
so when'er you click on findRestaurantButton you just get Resturent data from server but collected data not pass in adapter so thats why to getting blank adapter..
Just put this line in your onResponse after set data in array..
myAdapter.notifyDataSetChanged()
I found out where is the mistake. If you take a look at the section where overriding of back button click is happening, I by mistake set to GONE recyclerView itself:
restaurantsRecyclerView.setVisibility(View.GONE);
instead of LinearLayout in which recyclerView is contained. Desired action was:
restaurantListLayout.setVisibility(View.GONE);
P.S. Everything works without calling notifyDataSetChanged(), I just create a new instance of myAdapater each time when I receive new data. I hope Garbage Collector is doing its job :)
Thanks everyone on help!

Recyclerview and OkHttp request synchronisation

I'm trying to fetch some data from LastFm API, process it, and display it in a recycler view. My problem is that because of the way my code is structured I think that I initialize my recycler view before data is fetched so nothing is being shown until I reopen my fragment and I don't know how to fix that.
Here is my fragment code:
public class TopArtistsFragment extends Fragment {
private RecyclerView recyclerView;
private ArtistRecyclerViewAdapter adapter;
private TopArtistsViewModel mArtistsViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mArtistsViewModel = new ViewModelProvider(this).get(TopArtistsViewModel.class);
mArtistsViewModel.init();
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
adapter.notifyDataSetChanged();
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_top_artists, container, false);
recyclerView = view.findViewById(R.id.artistRecyclerView);
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
}
private void initRecyclerView() {
adapter = new ArtistRecyclerViewAdapter(mArtistsViewModel.getArtists().getValue(), getActivity());
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}
}
This is my ViewModel:
public class TopArtistsViewModel extends ViewModel {
private MutableLiveData<ArrayList<ArtistData>> mArtists;
private ArtistDataRepository mRepo;
public void init() {
if(mArtists != null)
return;
mRepo = ArtistDataRepository.getInstance();
mArtists = mRepo.getArtists();
}
public LiveData<ArrayList<ArtistData>> getArtists() {
return mArtists;
}
}
And this is my repository where the work is being done:
public class ArtistDataRepository {
private static ArtistDataRepository instance;
private ArrayList<ArtistData> dataSet = new ArrayList<>();
public static ArtistDataRepository getInstance() {
if(instance == null)
instance = new ArtistDataRepository();
return instance;
}
public MutableLiveData<ArrayList<ArtistData>> getArtists() {
setArtists();
MutableLiveData<ArrayList<ArtistData>> data = new MutableLiveData<>();
data.setValue(dataSet);
return data;
}
private void setArtists () {
fetchData();
}
private void fetchData () {
final String requestUrl = "https://ws.audioscrobbler.com/2.0/?method=chart.gettopartists&api_key=2124b8e156db20ac7a5035fad9c01b8e&format=json";
final OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestUrl).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
Log.d("NETWORK FAILURE", "Artist fetch");
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if(response.isSuccessful())
parseData(response.body().string());
else Log.d("OKHTTP", "Response not successful");
}
});
}
private void parseData (String queryResponse) {
try {
JSONArray artistResponse = new JSONObject(queryResponse).getJSONObject("artists").getJSONArray("artist");
for(int i = 0; i < artistResponse.length(); i++) {
JSONObject artist = artistResponse.getJSONObject(i);
dataSet.add(new ArtistData(artist.getString("mbid"), artist.getString("playcount"), artist.getString("listeners"),
artist.getString("name"), artist.getJSONArray("image").getJSONObject(0).getString("#text")));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
I'm just a beginner so right now I'm basically trying to merge code from different tutorials into one. That's why some things you see here probably don't make sense, so please do tell me what I can fix.
In order to tell the Recycleview that the data has come you need to get the Adapter and set its data then call adapter.notifyDataSetChanged();
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
--> adapter.setData(artistData); // Something like that
adapter.notifyDataSetChanged();
}
});
Got through this link
first try to learn how to fetch data using retrofit and you won't to parse data their is a library GSON and Moshi which will do all your work you just need to use POJO. Just go through this example
So to answer the question, part of the solution is, like someone suggested, to add adapter.setData() function to my adapter and call it in onChanged().
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
--> adapter.setData(artistData); // Something like that
adapter.notifyDataSetChanged();
}
});
The second part was adding data.postValue() in parseData function in my repo
private void parseData (String queryResponse) {
try {
JSONArray artistResponse = new JSONObject(queryResponse).getJSONObject("artists").getJSONArray("artist");
for(int i = 0; i < artistResponse.length(); i++) {
JSONObject artist = artistResponse.getJSONObject(i);
dataSet.add(new ArtistData(artist.getString("mbid"), artist.getString("playcount"), artist.getString("listeners"),
artist.getString("name"), artist.getJSONArray("image").getJSONObject(0).getString("#text")));
}
data.postValue(dataSet); //THIS WAS ADDED
} catch (JSONException e) {
e.printStackTrace();
}
}
This probably isn't the optimal solution for what I'm doing here, but it works as intended.

How to remove some elements If it was contains specific values inside recycle view?

I have succeeded in making more data download and then put it inside Recycleview and I succeeded in doing that and I used everything well but the problem is that I try to load some elements if they contain a value of 3, for example inside the function Loadmore I tried to make a loop and then I put the value 3 and then delete all the value equal to this number
but I have not succeeded so far please help and put the appropriate code
// my code
public class Page_6Fragment extends android.support.v4.app.Fragment implements AdapterView.OnItemSelectedListener {
TextView th, tm, tt, tapm;
Spinner spin_h, spin_m, spin_apm, spin_day;
RadioButton radioReject,radioAccipt ;
RadioGroup radioGroup;
Button buttonDialogReject,buttonDialogAccipt;
Dialog dialog;
RecyclerView recyclerView;
List<Customer> customers;
CustomerAdapter adapter;
View rootView;
String TAG = "MainActivity - ";
Context context;
API api;
Boolean acceptOrNo = true;
Context c = null;
String lock;
public int[] userLock;
public static Page_6Fragment instance;
public static Page_6Fragment newInstance() {
Page_6Fragment fragment = new Page_6Fragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.activity_customer, container, false);
this.context = getActivity();
this.instance = this;
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
customers = new ArrayList<>();
// Postion is index item inside recycleview =
adapter = new CustomerAdapter(Page_6Fragment.this,customers){
#Override
public void buttonClickEvent(int position){
// Toast.makeText(context,"posIs: "+position+" userLock.len= "+userLock.length,Toast.LENGTH_SHORT).show();
//showDialog(context,customers.get(position).user_id , Integer.parseInt(customers.get(position).id));
//---- condithion 1
if(customers.get(position).status ==0){
showDialog(context,customers.get(position).user_id , Integer.parseInt(customers.get(position).id));
}else{
Toast.makeText(context,"تم الرد على هذا المستخدم مسبقا", Toast.LENGTH_SHORT).show();
}
}
};
adapter.setLoadMoreListener(new CustomerAdapter.OnLoadMoreListener() {
#Override
public void onLoadMore() {
recyclerView.post(new Runnable() {
#Override
public void run() {
int index = customers.size();
loadMore(index);
}
});
//Calling loadMore function in Runnable to fix the
// java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling error
}
});
recyclerView.setHasFixedSize(true);
// recyclerView.addItemDecoration(new VerticalLineDecorator(2));
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);
api = ServiceGenerator.createService(API.class);
load(0);
return rootView;
}
// load data first time
private void load(int index) {
Call<List<Customer>> call = api.getCustomer(index);
call.enqueue(new Callback<List<Customer>>() {
#Override
public void onResponse(Call<List<Customer>> call, final Response<List<Customer>> response) {
// Toast.makeText(MainActivity.this, "tost "+response.body().get(0).post_writer, Toast.LENGTH_LONG).show();
//Log.i("TRUE_TRUE_","Yes "+response.body().get(2).name);
if (response.isSuccessful()) {
//Log.i("TRUE_TRUE3","Yes"+response.body().toString());
//movies.addAll(response.body());
//adapter.notifyDataChanged();
getActivity().runOnUiThread(new Runnable(){
public void run() {
// No.1 ..............
// ShowDataScreen();
// Toast.makeText( MainActivity.this, "ShowDataScreen",Toast.LENGTH_SHORT).show();
//if(customers.get()){
customers.addAll(response.body());
adapter.notifyDataChanged();
initiUserlock(customers.size());
}
});// end of No.1 UI new thread
getActivity().runOnUiThread(new Runnable() {
public void run() {//No.2
// Toast.makeText( MainActivity.this, "This is correct way",Toast.LENGTH_SHORT).show();
}
});// end of No.2 UI new thread
// Toast.makeText(MainActivity.this, "tost "+response.body().get(0).post_writer, Toast.LENGTH_LONG).show();
} else {
Log.e(TAG, " Response Error " + String.valueOf(response.code()));
}
}
#Override
public void onFailure(Call<List<Customer>> call, Throwable t) {
Log.e(TAG, " Response Error " + t.getMessage());
}
});
}
// laod more data ...................................
private void loadMore(int index) {
// add loading progress view ....
//Toast.makeText(context, "loadMore", Toast.LENGTH_LONG).show();
customers.add(new Customer("load"));
// customers.get(index).user_id =2;
adapter.notifyItemInserted(customers.size() - 1);
Call<List<Customer>> call = api.getCustomer(index);
call.enqueue(new Callback<List<Customer>>() {
#Override
public void onResponse(Call<List<Customer>> call, Response<List<Customer>> response) {
if (response.isSuccessful()) {
//Toast.makeText(context, "it is Successful", Toast.LENGTH_LONG).show();
customers.remove(customers.size() - 1);
List<Customer> result = response.body();
// Log.i("Getresult{--: ", " "+result.get());
if(result.size()>0) {
customers.addAll(result);
//add loaded data
// How to delete every item = 3 from customers list
for (int i=0; i<customers.size(); i++) {
if(customers.get(i).user_id == 3){
// customers.remove(i);
}
}
} else {
//result size 0 means there is no more data available at server
adapter.setMoreDataAvailable(false);
//telling adapter to stop calling load more as no more server data available
Toast.makeText(context,"لايوجد بيانات اخرى", Toast.LENGTH_LONG).show();
}
adapter.notifyDataChanged();
//should call the custom method adapter.notifyDataChanged here to get the correct loading status
} else {
Log.e(TAG, " Load More Response Error000 " + String.valueOf(response.code()));
}
}
#Override
public void onFailure(Call<List<Customer>> call,Throwable t) {
Log.e(TAG, " Load More Response Error_11 " + t.getMessage());
}
});
}
As you already have the new list in the result list, you can solve it by using the result List and add it to the customers List
customers.clear();
for(Customer newCustomer: result){
if(newCustomer.user_id != 3){
customers.add(newCustomer);
}
}
Second option would be to iterate over the customers list using ListIterator
ListIterator<Customer> iter = customers.listIterator();
while(iter.hasNext()){
if(iter.next().user_id == 3){
iter.remove();
}
}
Or
customers.removeIf(customer -> customer.user_id == 3);

runOnUiThread not updating listview

I think ive looked at almost all threads wont seem to work.
Ive got a chat and im trying to update the list view every second im doing it on another thread however, im using runOnUiThread though it still wont update im logging the return and it seems there is a return every second with the correct data.
The code
protected void onCreate(Bundle savedInstanceState) {
........
lv = (ListView) findViewById(R.id.message_array);
returnFunc = "messages";
messages();
send = (ImageButton) findViewById(R.id.sendMessage);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
returnFunc = "btnSend";
message = (EditText) findViewById(R.id.messageEditText);
message_input = message.getText().toString();
sendMessage();
}
});
new Thread(new Runnable() {
#Override
public void run() {
boolean test = true;
while (test) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
messages();
}
}
}).start();
}
public static Handler handler = new Handler();
public void messageLoop(){
handler.postDelayed(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
messages();
handler.postDelayed(this,1000);
}
});
}
},1000);
}
the messages function which gets the data fine
public void messages() {
String urlParameters = String.format("%s", "chatid="+tempVars.chatID+"&token="+tempVars.token);
new AsyncHttpPost(this).execute("http://mylink.com/appAccess.php?action=messages&", urlParameters);
}
public void getMessages(String content){
myList = new ArrayList<Messages>();
int index = lv.getFirstVisiblePosition();
View v = lv.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - lv.getPaddingTop());
try{
JSONArray jsonarray = new JSONArray(content);
for (int i = 0; i < jsonarray.length(); i++) {
JSONObject jsonobject = jsonarray.getJSONObject(i);
boolean mine;
if(jsonobject.getString("isMine").equals("true")){
mine = true;
}else{
mine = false;
}
myList.add(new Messages(jsonobject.getString("message"),jsonobject.getString("timestamp"),jsonobject.getString("name"),mine,jsonobject.getString("id")));
}
}catch (Throwable tx){
}
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3)
{
Messages CurrentMessage = myList.get(position);
tempVars.messageID = CurrentMessage.getID();
//startActivity(new Intent("PersonalChat"));
}
});
if(arrayAdapter == null){
arrayAdapter = new CustomAdapter(this, myList);
}
lv = (ListView) findViewById(R.id.message_array);
lv.setAdapter(arrayAdapter);
arrayAdapter.notifyDataSetChanged();
if(firstView == 0){
firstView = 1;
lv.setSelection(arrayAdapter.getCount() - 1);
}else{
lv.setSelectionFromTop(index, top);
}
}
class CustomAdapter
public class CustomAdapter extends ArrayAdapter<Messages>{
Messages currentMessage;
public CustomAdapter(Context context, ArrayList<Messages> messages){
super(context,R.layout.received_message, messages);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
currentMessage = myList.get(position);
setTitle(currentMessage.getName());
if(currentMessage.isMine()){
itemView = getLayoutInflater().inflate(R.layout.sent_message,parent, false);
TextView message = (TextView) itemView.findViewById(R.id.message);
message.setText(currentMessage.getMessage());
TextView timestamp = (TextView) itemView.findViewById(R.id.date);
timestamp.setText(currentMessage.getTimestamp());
}else{
itemView = getLayoutInflater().inflate(R.layout.received_message,parent, false);
TextView message = (TextView) itemView.findViewById(R.id.message_text);
message.setText(currentMessage.getMessage());
TextView timestamp = (TextView) itemView.findViewById(R.id.timestamp);
timestamp.setText(currentMessage.getTimestamp());
}
return itemView;
}
}
Thanks in advance
More info
When i call the message function originally in onCreate it works fine
new Thread(new Runnable() {
#Override
public void run() {
boolean test = true;
while (test) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}mymessage();
}
}
}).start();
Replace your getMessages() method with below code:
public void getMessages(String content){
myList = new ArrayList<Messages>();
int index = lv.getFirstVisiblePosition();
View v = lv.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - lv.getPaddingTop());
try{
JSONArray jsonarray = new JSONArray(content);
for (int i = 0; i < jsonarray.length(); i++) {
JSONObject jsonobject = jsonarray.getJSONObject(i);
boolean mine;
if(jsonobject.getString("isMine").equals("true")){
mine = true;
}else{
mine = false;
}
myList.add(new Messages(jsonobject.getString("message"),jsonobject.getString("timestamp"),jsonobject.getString("name"),mine,jsonobject.getString("id")));
}
arrayAdapter.notifyDataSetChanged();
}catch (Throwable tx){
}
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3)
{
Messages CurrentMessage = myList.get(position);
tempVars.messageID = CurrentMessage.getID();
//startActivity(new Intent("PersonalChat"));
}
});
if(arrayAdapter == null){
arrayAdapter = new CustomAdapter(this, myList);
}
lv = (ListView) findViewById(R.id.message_array);
lv.setAdapter(arrayAdapter);
if(firstView == 0){
firstView = 1;
lv.setSelection(arrayAdapter.getCount() - 1);
}else{
lv.setSelectionFromTop(index, top);
}
}

I am using a git repo called LikeButton, but the state of my button keeps jumping around in my recyclerview?

I am using a git repo called LikeButton, but the state of my button keeps jumping around in my recyclerview? Here is the repo https://github.com/jd-alexander/LikeButton. Basically when I click on a recyclerview item, it sets a textview to the word true or false based on if the user liked the post or not, and this works. However, the state of my button is doing some weird stuff, it jumps around...
Here is my Adapter, is their anything wrong with it?
public class ViewpagerAdapter extends RecyclerView.Adapter<ViewpagerAdapter.ViewDashboard>{
private LayoutInflater mLayoutInflater;
private ArrayList<QuestionData> data = new ArrayList<>();
public ViewpagerAdapter(Context context) {
mLayoutInflater=LayoutInflater.from(context);
}
public void setBloglist(ArrayList<QuestionData> listBlogs) {
this.data = listBlogs;
notifyItemRangeChanged(0,listBlogs.size());
}
#Override
public ViewDashboard onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(R.layout.customizejson, parent, false);
ViewDashboard viewholder = new ViewDashboard(view);
return viewholder;
}
#Override
public void onBindViewHolder(ViewDashboard holder, int position) {
QuestionData questionHolder = data.get(position);
holder.questionText.setText(questionHolder.getMtext());
//This sets the text, to a true or a false String
holder.mStudentVoted.setText(questionHolder.getVoters());
holder.mLikeButton.setTag(holder);
}
#Override
public int getItemCount() {
return data.size();
}
class ViewDashboard extends RecyclerView.ViewHolder {
private TextView questionText;
private LikeButton mLikeButton;
private TextView mStudentVoted;
public ViewDashboard(View itemView) {
super(itemView);
questionText = (TextView)itemView.findViewById(R.id.questionText);
mStudentVoted = (TextView)itemView.findViewById(R.id.studentVoted);
mLikeButton = (LikeButton)itemView.findViewById(R.id.like_button_viewpager);
mLikeButton.setOnLikeListener(new OnLikeListener() {
#Override
public void liked(LikeButton likeButton) {
Voting voting = new Voting(getAdapterPosition(),ViewpagerAdapter.this, questionId);
voting.onUpVote();
}
#Override
public void unLiked(LikeButton likeButton) {
Voting voting = new Voting(getAdapterPosition(),ViewpagerAdapter.this, questionId);
voting.onDownVote();
}
});
}
}
}
Voting Class
public class Voting {
private int adapterPosition;
private RecyclerView.Adapter adapter;
private String stringId;
private TextView studentVoted;
//TODO Trim Constructor
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, TextView questionId, TextView studentVoted) {
stringId = questionId.getText().toString();
this.adapter = adapter;
this.studentVoted=studentVoted;
}
public void onUpVote() {
final RequestQueue mRequestQueue = VolleySingleton.getInstance().getRequestQueue();
StringRequest postVoteUp = new StringRequest(Request.Method.PUT, PUT_VOTE_UP, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
System.out.println("Succesful Upvote The Students Value is " + studentVoted);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("failed Upvote");
}
});
mRequestQueue.add(postVoteUp);
}
public void onDownVote() {
final RequestQueue mrequestQueue = VolleySingleton.getInstance().getRequestQueue();
//TODO Delete Token(inserted for student 3 for testing purposes)
StringRequest postVoteDown = new StringRequest(Request.Method.PUT, PUT_VOTE_DOWN, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//TODO OnResponse, must setLiked(False)
//Succesful downVote The Students Value is true
//studentVoted.setText("false");
System.out.println("Succesful downVote The Students Value is "+studentVoted);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("failed downVote");
}
});
mrequestQueue.add(postVoteDown);
}
public void realTimeUpVoting(TextView textView){
String voteString= textView.getText().toString();
int voteNumber=Integer.parseInt(voteString)+1;
textView.setText("" + voteNumber);
}
public void realTimeDownVoting(TextView textView){
String voteString= textView.getText().toString();
int voteNumber=Integer.parseInt(voteString)-1;
textView.setText("" + voteNumber);
}
}
Json Request and Parsing Methods
public void JsonRequestMethod() {
mVolleySingleton = VolleySingleton.getInstance();
mRequestQueue = mVolleySingleton.getRequestQueue();
JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, URL_HOME, (String) null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
mListblogs.clear();
mListblogs = new YourTask().execute(response).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
mRequestQueue.add(request);
}
private ArrayList<QuestionData> parseJSONResponse(JSONArray response) {
if (!response.equals("")) {
try {
StringBuilder data = new StringBuilder();
for (int x = 0; x < response.length(); x++) {
JSONObject currentQuestions = response.getJSONObject(x);
JSONArray arrSubcategory = currentQuestions.optJSONArray("questions");
for (int y = 0; y < arrSubcategory.length(); y++) {
JSONObject objectSubcategory = arrSubcategory.getJSONObject(y);
String text = objectSubcategory.optString("text");
String studentId = objectSubcategory.optString("studentId");
String votes=objectSubcategory.optString("votes");
/*JSONArray cycles through the array of voters, when a user votes
their ID is added to the array.When they downvote, it is removed
*/
JSONArray voters= objectSubcategory.optJSONArray("voters");
QuestionData questionData = new QuestionData();
questionData.setMstudentId(studentId);
questionData.setMtext(text);
questionData.setVotes(votes);
questionData.setVoters(checkIfVoted(voters));
mQuestionDataArrayList.add(questionData);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return mQuestionDataArrayList;
}
private static String checkIfVoted(JSONArray jsonArray ) {
/*pass in a json Array, copy the array into ints, and if
the students Id is contained in the array return the string true
*/
int[] voteIds = new int[jsonArray.length()];
for(int i=0;i<voteIds.length;i++){
voteIds[i] = jsonArray.optInt(i);
}
for(int i=0;i<voteIds.length;i++){
if(voteIds[i]== Integer.parseInt(Login.getUserId())){
//TODO String was only used for Testing purposes, Convert to Boolean later
return "true";
}
}
return "false";
}
you are currently only updating the textview which is why your recycleview changes state when scrolling.
Should change your voting class and pass the question Data rather textview
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, TextView questionId, TextView studentVoted) {
change to
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, QuestionData questionData, TextView studentVoted) {
// make other changes for the data
and then in
public void realTimeUpVoting(QuestionData questionData){
data.votes++ //something like that. idont know your model
// now call back using interface the recyleview data changed method so it updates the count in recycleview automatically.
Edit
passing the question Data in click button
class ViewDashboard extends RecyclerView.ViewHolder {
public int position
public void onBindViewHolder(ViewDashboard holder, int position) {
holder.position = position
}
public void liked(LikeButton likeButton) {
QuestionData questionHolder = data.get(position);

Categories