I'm creating a chat feature for an application and it works super fine. But I would like to show the user that message has been sent or it still wating for the server's response.
Fields:
List<ChatMessage> chatMessages;
ChatAdapter chatAdapter;
RecyclerView chatRecyclerView;
ImageButton submitMessageBtn;
this how I send a message on my ChatActivity class:
public void submitMessage(final String messageType, final byte[] message){
final ChatMessageResponse messageObject = new ChatMessageResponse();
new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
messageObject.setMessage( message);
messageObject.setYours(true);
messageObject.setUserNickname(getNickname());
messageObject.setCreationDate(DateTime.now().withZone(DateTimeZone.UTC));
messageObject.setType(messageType);
AddMessage(messageObject);
}
#Override
protected Void doInBackground(Void... voids) {
try {
chatClient.chat().sendMessage(eventId, messageType, message);
runOnUiThread(new Runnable() {
#Override
public void run() {
// Update message on the list after has been sent to server
}
});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
public void AddMessage(ChatMessage message)
{
chatMessages.add(message);
chatAdapter.notifyDataSetChanged();
chatRecyclerView.scrollToPosition(chatMessages.size() -1);
}
When message is immediatly added to the adapter it should look like this:
my ChatAdapter class is setup like this:
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
private static final int VIEW_TYPE_MESSAGE_THIS_USER = 0;
private static final int VIEW_TYPE_MESSAGE_OTHER_USER = 1;
private final Activity activity;
public List<ChatMessage> chats=new ArrayList<>();
ArrayList<String> usercolor=new ArrayList<>();
Context mContext;
View view;
public ChatAdapter(List<ChatMessage> chats, Context mContext, Activity activity) {
this.chats = chats;
this.mContext = mContext;
this.activity = activity;
}
#Override
public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
mContext = parent.getContext();
if (viewType == VIEW_TYPE_MESSAGE_OTHER_USER) {
view = View.inflate(mContext, R.layout.message_item_left, null);
} else if (viewType == VIEW_TYPE_MESSAGE_THIS_USER){
view = View.inflate(mContext, R.layout.message_item, null);
}
return new ChatViewHolder(view,(View.OnLongClickListener)activity);
}
#Override
public void onBindViewHolder(final ChatViewHolder holder, int position){
final ChatMessageResponse m = (ChatMessageResponse) chats.get(position);
if (getItemViewType(position) == VIEW_TYPE_MESSAGE_OTHER_USER){
holder.bindToView1(m);
} else if (getItemViewType(position) == VIEW_TYPE_MESSAGE_THIS_USER)
{
holder.bindToView(m);
}
}
#Override
public int getItemCount() {
return chats.size();
}
#Override
public int getItemViewType(int position) {
return chats.get(position).isYours() ? VIEW_TYPE_MESSAGE_THIS_USER : VIEW_TYPE_MESSAGE_OTHER_USER;
}
}
When the server's response is positive the views in the ChatViewHolder (that I don't show the code because is too long) should change visibility state
Someone told me to get a referece for the view and change it on the activity's asynctask or create a Callback listener for my adapter.
But I have no Idea how to do either one of then any help is appreciated.
Are you familiar with the use of "Callbacks" or "Interfaces"? You can create an interface and implement it in your activity. Pass the callback by parameters in the "AsyncTask" and use it there.
//Interface class
/**
* Created by gmora
*/
public interface IProcess {
void updateAdapter(String result);
}
On Activity:
public class YourActivity extends AppCompatActivity {
private IProcess mProcess;
private Adapter mRecyclerAdapter;
private RecyclerView mRecyclerView;
private List<ChatMessage> chats; //update chats on activity and refresh your adapter
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mProcess = new IProceso() {
#Override
public void updateAdapter(String pException) {
//update chats ... and update mAdater.notifyDataChange()...
// or mRecyclerView.setAdapter(new Adpater.... with new list chats)..
}
};
mRecyclerView = find....
// etc....
mRecyclerAdapter = new RecyclerAdapter( chats, ...);
mRecyclerView.setAdapter(mRecyclerAdapter);
}
}
Finally on AsyncTask... create a external class from AsyncTask please!
/**
* Created by gmora.
*/
public class YourAsyncTaskClass extends AsyncTask<String, Void, String > {
private IProcess iProcess;
public StarSearchPrinterTask(IProcess pIProcess) {
this.iProcess= pIProcess;
}
#Override
protected void onPreExecute() {
//loading... its optional
}
#Override
protected String doInBackground(String... interfaceType) {
// execute webservice or api and get results..
return results;
}
#Override
protected void onPostExecute(String results) {
mIProceso.updateAdapter(results);
}
}
Related
PreAdmissionList.java
public class PreAdmissionList extends Fragment implements View.OnClickListener, AdapterApprovalList.OnItemClickListener {
private BasicInfoViewModel basicInfoViewModel;
private AdapterApprovalList adapterApprovalList;
private RecyclerView rvApprovalList;
public PreAdmissionList() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_pre_admission_list, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.fab_add_new_admission).setOnClickListener(this);
rvApprovalList = view.findViewById(R.id.rv_approval_list);
basicInfoViewModel = new ViewModelProvider(requireActivity()).get(BasicInfoViewModel.class);
basicInfoViewModel.init();
basicInfoViewModel.getApprovalList().observe(getViewLifecycleOwner(), new Observer<List<ModelBasicInfo>>() {
#Override
public void onChanged(List<ModelBasicInfo> modelBasicInfos) {
adapterApprovalList.notifyDataSetChanged();
}
});
initRecyclerView();
}
private void initRecyclerView() {
adapterApprovalList = new AdapterApprovalList(this,basicInfoViewModel.getApprovalList().getValue());
rvApprovalList.setHasFixedSize(true);
rvApprovalList.setLayoutManager(new LinearLayoutManager(getContext()));
rvApprovalList.setAdapter(adapterApprovalList);
}
}
AdapterApprovalList.java
public class AdapterApprovalList extends RecyclerView.Adapter<AdapterApprovalList.ALViewHolder>{
private Context context;
private OnItemClickListener onItemClickListener;
private List<ModelBasicInfo> modelBasicInfoList;
public AdapterApprovalList(OnItemClickListener onItemClickListener,List<ModelBasicInfo> modelBasicInfoList) {
this.onItemClickListener = onItemClickListener;
this.modelBasicInfoList=modelBasicInfoList;
}
#NonNull
#Override
public ALViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.template_approval_list_item,parent,false);
return new ALViewHolder(view,onItemClickListener);
}
#Override
public void onBindViewHolder(#NonNull ALViewHolder holder, int position) {
ModelBasicInfo basicInfo = modelBasicInfoList.get(position);
StringBuilder fullName = new StringBuilder();
fullName.append(basicInfo.getFirstName()).append(" ");
fullName.append(basicInfo.getMiddleName()).append(" ");
fullName.append(basicInfo.getLastName()).append(" ");
holder.fullName.setText(fullName);
holder.id.setText("RKC00"+String.valueOf(basicInfo.getId()));
}
#Override
public int getItemCount() {
return modelBasicInfoList.size();
}
static class ALViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
OnItemClickListener mOnItemClickListener;
TextView fullName,id;
public ALViewHolder(#NonNull View itemView,OnItemClickListener mOnItemClickListener) {
super(itemView);
this.mOnItemClickListener = mOnItemClickListener;
fullName = itemView.findViewById(R.id.tv_text_full_name);
id = itemView.findViewById(R.id.tv_text_approval_id);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mOnItemClickListener.onApprovalItemClick(getAbsoluteAdapterPosition());
}
}
public interface OnItemClickListener{
void onApprovalItemClick(int position);
}
}
BasicInfoViewModel.java
public class BasicInfoViewModel extends ViewModel {
private BasicInfoRepo basicInfoRepo;
private MutableLiveData<List<ModelBasicInfo>> approvalList;
public void init(){
if(approvalList != null){
return;
}
basicInfoRepo = BasicInfoRepo.getInstance();
approvalList = basicInfoRepo.getApprovalList();
}
public LiveData<List<ModelBasicInfo>> getApprovalList(){
return approvalList;
}
public void insertBasicInfo(ModelBasicInfo modelBasicInfo){
basicInfoRepo.insertData(modelBasicInfo);
}
public void updateApprovalStatus(int id){
basicInfoRepo.updateStatus(id);
}
}
BasicInfoRepo.java
public class BasicInfoRepo {
private static BasicInfoRepo instance;
static ConnectionClass connectionClass = new ConnectionClass();
private List<ModelBasicInfo> approvalList = new ArrayList<>();
public static BasicInfoRepo getInstance(){
if(instance== null){
instance = new BasicInfoRepo();
}
return instance;
}
public MutableLiveData<List<ModelBasicInfo>> getApprovalList(){
loadApprovalList();
MutableLiveData<List<ModelBasicInfo>> mList = new MutableLiveData<>();
mList.setValue(approvalList);
return mList;
}
private void loadApprovalList() {
LoadApprovalList loadApprovalList = new LoadApprovalList();
loadApprovalList.execute();
}
public void insertData(ModelBasicInfo modelBasicInfo){
InsertBasicInfo insertBasicInfo = new InsertBasicInfo();
insertBasicInfo.execute(modelBasicInfo);
}
public void updateStatus(int id){
UpdateBasicInfo updateBasicInfo = new UpdateBasicInfo();
updateBasicInfo.execute(id);
}
private static class InsertBasicInfo extends AsyncTask<ModelBasicInfo,Integer,String>{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(ModelBasicInfo... model) {
String result = null;
// Log.i("Testing db",lists[0].get(0).getFirstName());
try{
Connection connection = connectionClass.CONN();
if(connection==null){
result = "Error in connection !!!";
}else{
//Date object
Date date= new Date();
//getTime() returns current time in milliseconds
long time = date.getTime();
//Passed the milliseconds to constructor of Timestamp class
Timestamp ts = new Timestamp(time);
PreparedStatement ps = connection.prepareStatement("insert into PreAdmissionDetails values(?,?,?,?,?,?,?,?,?,?)");
ps.setString(1,model[0].getFirstName());
ps.setString(2,model[0].getMiddleName());
ps.setString(3,model[0].getLastName());
ps.setString(4,model[0].getMotherName());
ps.setDate(5, java.sql.Date.valueOf(model[0].getDateOfBirth()));
ps.setString(6,model[0].getMobileNo());
ps.setInt(7,0);
ps.setInt(8,0);
ps.setBoolean(9,false);
ps.setTimestamp(10, ts);
ps.executeUpdate();
result = "Submitted Successfully !!!";
}
}catch (Exception ex){
Log.e("sqlerror",ex.toString());
result=ex.getMessage();
}
Log.e("sqlerror","result : "+result);
return result;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
private static class UpdateBasicInfo extends AsyncTask<Integer,Integer,String>{
#Override
protected String doInBackground(Integer... integers) {
String result = null;
try{
Connection connection = connectionClass.CONN();
if(connection==null){
result = "Error in connection !!!";
}else{
PreparedStatement ps = connection.prepareStatement("UPDATE PreAdmissionDetails SET STATUS=? WHERE id=?");
ps.setInt(1,0);
ps.setInt(2,integers[0]);
ps.executeUpdate();
result = "Updated Successfully !!!";
}
}catch (Exception ex){
Log.e("sqlerror",ex.toString());
result=ex.getMessage();
}
Log.e("sqlerror","result : "+result.toString());
return result;
}
}
private class LoadApprovalList extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... voids) {
String result = null;
try{
Connection connection = connectionClass.CONN();
if(connection==null){
result = "Error in connection !!!";
}else{
PreparedStatement ps = connection.prepareStatement("select * from preadmissiondetails");
ResultSet rs = ps.executeQuery();
approvalList.clear();
while (rs.next()) {
approvalList.add(new ModelBasicInfo(rs.getInt(1),
rs.getString(2),
rs.getString(3),
rs.getString(4),
rs.getString(5),
rs.getString(6),
rs.getString(7),
rs.getInt(8),
rs.getInt(9),
rs.getBoolean(10)));}
result = "Fetched Successfully !!!";
}
}catch (Exception ex){
Log.e("sqlerror",ex.toString());
result=ex.getMessage();
}
Log.e("sqlerror","result : "+result.toString());
return null;
}
}
}
Problem is here that when I open the app no items in the recycler view, Firsty I thought may be slow internet it will fetch data in after some time but it doesn't show data. But when I navigate to some other fragments and return to the preadmission list it shows data.
Your problem is here:
public MutableLiveData<List<ModelBasicInfo>> getApprovalList(){
loadApprovalList();
MutableLiveData<List<ModelBasicInfo>> mList = new MutableLiveData<>();
mList.setValue(approvalList);
return mList;
}
loadApprovalList() launches an AsyncTask, which is an asynchronous operation (in other words, it takes time to produce a result). The getApprovalList() method doesn't just halt right there and wait for loadApprovalList() to complete. It continues right along and returns an empty list the first time it executes. But by the second time it executes, approvalList now has a value because the AsyncTask has completed. So it returns the correct data that second time it executes. It executes a second time when you return to your Fragment because the init block in your ViewModel is executing a second time at that point.
The solution is to make approvalList a LiveData. That way, when the AsyncTask updates approvalList, your ViewModel can observe the change. Your ViewModel should observe approvalList in your Repository, just like how your Fragment is observing the getApprovalList() method in your ViewModel.
I am working with MVVM. Main screen shows movie's posters only during debugging (and not during regular run).
The problem is in observation of RecyclerView population. There is Observer in MainActivity. I expect that notifyDataSetChanged method will cause
posters to appear after receiving data from the API, but it doesn't happen.
My cleaned code related to this issue only is available in https://github.com/RayaLevinson/Test
I am missing some important point related to Observer. Please help me! Thank you.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recycler_view_movie);
mMainActivityViewModal = ViewModelProviders.of(this).get(MainActivityViewModel.class);
mMainActivityViewModal.init();
mMainActivityViewModal.getMovies().observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
mAdapter.notifyDataSetChanged();
}
});
initRecyclerView();
}
private void initRecyclerView() {
mAdapter = new RecyclerViewAdapter(this, mMainActivityViewModal.getMovies().getValue());
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
mRecyclerView.setAdapter(mAdapter);
}
MovieRepository.java
public class MovieRepository {
private static final String TAG = "MovieRepository";
private static String mSortBy = "popular";
private static MovieRepository instance;
private List<Movie> movies = new ArrayList<>();
public static MovieRepository getInstance() {
if (instance == null) {
instance = new MovieRepository();
}
return instance;
}
public MutableLiveData<List<Movie>> getMovies() {
setMovies();
MutableLiveData<List<Movie>> data = new MutableLiveData<List<Movie>>();
data.setValue(movies);
return data;
}
private void setMovies() {
Context context = GlobalApplication.getAppContext();
if (NetworkUtils.isNetworkAvailable(context)) {
movies.clear();
new MovieRepository.FetchMoviesTask().execute(mSortBy);
} else {
alertUserAboutNetworkError();
}
}
private void alertUserAboutNetworkError() {
Context context = GlobalApplication.getAppContext();
// Toast.makeText(context, R.string.networkErr, Toast.LENGTH_LONG).show();
}
private class FetchMoviesTask extends AsyncTask<String, Void, List<Movie>> {
#Override
protected List<Movie> doInBackground(String... params) {
if (params.length == 0) {
return null;
}
String sortBy = params[0];
Log.d(TAG, "In doInBackground " + sortBy);
URL moviesRequestUrl = NetworkUtils.buildUrl(sortBy);
try {
String jsonWeatherResponse = NetworkUtils.getResponseFromHttpUrl(moviesRequestUrl);
return MovieJsonUtils.getMoviesDataFromJson(jsonWeatherResponse);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(List<Movie> parsedMoviesData) {
if (parsedMoviesData != null) {
for (Movie movie : parsedMoviesData) {
movies.add(movie);
Log.d(TAG, "In onPostExecute " + " movie was added");
}
}
}
}
}
MainActivityViewModel.java
public class MainActivityViewModel extends ViewModel {
private MutableLiveData<List<Movie>> mMovies;
private MovieRepository mMoviewRepository;
public void init() {
if (mMovies != null) {
return;
}
mMoviewRepository = MovieRepository.getInstance();
mMovies = mMoviewRepository.getMovies();
}
public LiveData<List<Movie>> getMovies() {
return mMovies;
}
}
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private static final String TAG = "RecyclerViewAdapter";
private final Context mContext;
private List<Movie> mMovies;
public RecyclerViewAdapter(Context mContext, List<Movie> movies) {
this.mMovies = movies;
this.mContext = mContext;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder called");
Picasso.get()
.load(mMovies.get(holder.getAdapterPosition()).getPosterPath())
.placeholder(R.mipmap.ic_launcher)
.into(holder.image);
}
#Override
public int getItemCount() {
return mMovies.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
final ImageView image;
final LinearLayout parentLayout;
private ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
parentLayout = itemView.findViewById(R.id.parent_layout);
}
}
public void update(List<Movie> movies) {
mMovies.clear();
mMovies.addAll(movies);
notifyDataSetChanged();
}
}
Your MovieRepository#getMovies() executes the Livedata.setValue() before the AsyncTask finishes. You can see that in your debug output.
What you have to do is to call postValue() (cause your on not on the mainthread) in your onPostExecute() method. Then you have to call mAdapter.update() from the onChanged() method.
Also I would recommend to refactor your ViewModel a little bit. Remove the call to the repository from your init() method and create a new method that only calls the load function from the repo. So if you later on would like to support things like endless scrolling, this will help you a lot.
Just a matter of opinion, but i like to create my observables inside my ViewModel and not in the Repository and pass it along as parameter. Thats how it could look like:
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
...
viewModel = ViewModelProviders.of(this).get(YOUR_VIEW_MODEL.class);
viewModel.init();
viewModel.getItemsObservable().observe(this, new Observer<List<Item>>() {
#Override
public void onChanged(#Nullable List<Item> items) {
// Add/replace your existing adapter
adapter.add/replaceItems(items);
// For better performance when adding/updating elements you should call notifyItemRangeInserted()/notifyItemRangeChanged(). For replacing the whole dataset notifyDataSetChanged() is fine
adapter.notifyDataSetChanged();
// Normally i would put those calls inside the adapter and make appropriate methods but for demonstration.
}
});
initRecyclerView();
viewModel.loadItems()
}
ViewModel
public void init(){
repository = Repository.getInstance();
}
public void loadItems(){
repository.loadItems(getItemsObservable());
}
public LiveData<List<Item>> getItemsObservable() {
if (items == null) {
items = new MutableLiveData<>();
}
return items;
}
Repository
public void loadItems(LiveData<List<Item>> liveData){
List<Item> data = remote.getDataAsync(); // get your data asynchronously
liveData.postValue(data); // call this after you got your data, in your case inside the onPostExecute() method
}
I'm parsing website title from URL, and set this title to RecyclerView item. When I call constructor, but I must use AsyncTasck for correctly work Jsoup. How can I set received data to RecyclerView?
I can do public MyAsyncTasck instead of private, but i don't now if it is right.
My MainActivity.java:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "mLog";
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
#BindView(R.id.imageView)
ImageView imageView;
private RecyclerView.Adapter adapter;
private List<ListItem> listItems;
ListItem item;
String URL = "https://sinoptik.ua/%D0%BF%D0%BE%D0%B3%D0%BE%D0%B4%D0%B0-
%D0%B2%D0%B8%D0%BD%D0%BD%D0%B8%D1%86%D0%B0";
Document document;
// Received data from Jsoup
String head = "something";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
listItems = new ArrayList<>();
item = new ListItem(head, "desc1");
listItems.add(item);
adapter = new MyRecViewAdapter(listItems, this);
recyclerView.setAdapter(adapter);
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
try {
document = Jsoup.connect(URL).get();
head = document.title();
} catch (IOException e ) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
// ??????
}
}
}
And MyRecViewAdapter.java:
public class MyRecViewAdapter extends RecyclerView.Adapter<MyRecViewAdapter.ViewHolder>{
private List<ListItem> listItems;
private Context context;
public MyRecViewAdapter(List<ListItem> listItems, Context context) {
this.listItems = listItems;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
//return our view
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
ListItem listItem = listItems.get(position);
holder.tvHead.setText(listItem.getHead());
holder.tvDesc.setText(listItem.getDesc());
}
#Override
public int getItemCount() {
return listItems.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvHead;
public TextView tvDesc;
public ViewHolder(View itemView) {
super(itemView);
tvHead = (TextView) itemView.findViewById(R.id.tv_head);
tvDesc = (TextView) itemView.findViewById(R.id.tv_desc);
}
}
}
And the ListItem.java
public class ListItem {
private String head;
private String desc;
public ListItem(String head, String desc) {
this.head = head;
this.desc = desc;
}
public String getHead() {
return head;
}
public String getDesc() {
return desc;
}
}
You need to set the data got from your API call using the AsyncTask into your listItems which are being represented in your RecyclerView. Then you need to call notifyDataSetChanged in order to show the newly updated the in your RecyclerView.
So you need to modify the doInBackground function in your AsyncTask like the following.
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
try {
document = Jsoup.connect(URL).get();
listItems.clear();
ListItem item = new ListItem(document.title, document.desc);
listItems.add(item);
adapter.notifyDataSetChanged();
} catch (IOException e ) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
// ??????
}
}
hope you fine and well,
i have the following main class :
public class MainActivity extends AppCompatActivity {
Activity activity;
ViewPager viewPager;
CustomAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=(ViewPager)findViewById(R.id.view_pager);
adapter=new CustomAdapter(this);
viewPager.setAdapter(adapter);
ConnectionAsyncTask connectionAsyncTask = new
ConnectionAsyncTask(MainActivity.this);
connectionAsyncTask.execute("http://www.mocky.io/v2/570d3677270000f600dc29b6");
}
public void showUploader()
{
// findViewById(R.id.progressBar1).setVisibility(View.VISIBLE);
}
public void hideUploader()
{
//findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
public void DisplyOnTextView(List< Student > students) {
List <Student> my = students ;
}
}
when i run the app, this main activity will use another class to read json data from link as follows:
public class StudentJasonParser {
public static List<Student> getObjectFromJason(String jason)
{
List<Student> students;
try {
JSONArray jsonArray = new JSONArray(jason);
students = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++)
{
JSONObject jsonObject = new JSONObject();
jsonObject= (JSONObject) jsonArray.get(i);
Student student = new Student();
student.setID(jsonObject.getInt("id"));
student.setName(jsonObject.getString("name"));
student.setUrl(jsonObject.getString("url"));
student.setDes(jsonObject.getString("des\n"));
student.setRate(jsonObject.getDouble("rate"));
student.setLon(jsonObject.getDouble("lon"));
student.setLat(jsonObject.getDouble("lat"));
students.add(student);
}
} catch (JSONException e) {
e.printStackTrace();
return null;
}
return students;
}
}
now this class will return the data to the following class :
public class ConnectionAsyncTask extends AsyncTask<String,String,String> {
Activity activity;
public ConnectionAsyncTask(Activity activity) {
this.activity=activity;
}
#Override
protected void onPreExecute() {
//((MainActivity)activity).DisplyOnTextView();
((MainActivity)activity).showUploader();
}
#Override
protected String doInBackground(String... params) {
String content =HttpManager.getData(params[0]);
return content;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String s) {
((MainActivity)activity).hideUploader();
List<Student> students= StudentJasonParser.getObjectFromJason(s);
if (students != null) {
((MainActivity) activity).DisplyOnTextView(students);
}
}
}
this line : ((MainActivity)activity).DisplyOnTextView(students);
will return the fetched data to the main class in the following function (mentioned in the main class ! )
public void DisplyOnTextView(List< Student > students) {
List <Student> my = students ;
}
now what i want is to pass this list to the following class in order to use it in the imageView and textView in the viewPager instead of the pre-defined data in the class :
public class CustomAdapter extends PagerAdapter {
private int[] images = {R.drawable.sample_0,R.drawable.sample_1};
private Context ctx;
private LayoutInflater LayoutInflater;
public CustomAdapter(Context ctx)
{
this.ctx=ctx;
}
#Override
public int getCount() {
return images.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view==(LinearLayout)object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = LayoutInflater.inflate(R.layout.slide_layout,container,false);
ImageView imageView =(ImageView) view.findViewById(R.id.image_view);
TextView textView = (TextView)view.findViewById(R.id.image_count);
imageView.setImageResource(images[position]);
textView.setText(""+position);
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
any idea ?!
thanks, regards.
what i want is to pass this list to the following class in order to use it in the imageView and textView in the viewPager
Then simply pass in the list as a parameter to the adapter and add a member variable for it. The usage of this adapter is at the bottom of this post, because I want to mention some other stuff.
class CustomAdapter extends PagerAdapter {
private Context ctx;
private List<Student> data;
public CustomAdapter(Context ctx, List<Student> students) {
this.ctx = ctx;
this.data = students;
}
If you want to use that data variable in the instantiateItem method, then you can do Student s = this.data.get(position); and use the various methods on the Student object to load the TextView or ImageView.
Please note that you will need an image loading library (Picasso, Glide, Fresco, etc.) to load a URL into an ImageView. While on the topic of libraries, though, you will save yourself much development time by looking into Gson for JSON parsing and Retrofit or Volley for HTTP network calls with JSON data.
As for your usage of the AsyncTask, passing around the Activity variable is bad practice. Try to use an asynchronous callback to the Activity instead.
public interface AsyncResponse<T> {
void onResponse(T response);
}
public class ConnectionAsyncTask extends AsyncTask<String, Void, List<Student>> {
private AsyncResponse<List<Student>> callback;
public ConnectionAsyncTask(AsyncResponse<List<Student>> callback) {
this.callback = callback;
}
#Override
protected List<User> doInBackground(String... params) {
String url = params[0];
final List<Student> students = new ArrayList<Student>();
// TODO: JSON stuff
return students;
}
#Override
protected void onPostExecute(List<Student> result) {
if (this.callback != null) {
this.callback.onResponse(result);
} else {
Log.w("ConnectionAsyncTask", "Ignoring result");
}
}
}
public class SampleViewPagerActivity extends Activity {
private ViewPager pager;
private PagerAdapter adapter;
private ArrayList<Student> students;
private ProgressDialog progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. Inflate a layout
setContentView(R.layout.viewpager_activity);
// 2. Initialize the views
this.pager = (ViewPager) findViewById(R.id.pager);
this.progress = new ProgressDialog(this);
this.progress.setTitle("Loading");
this.progress.setMessage("Please wait");
// 3. Populate the views with data
this.students = new ArrayList<Student>();
this.adapter = new CustomAdapter(this, students);
this.pager.setAdapter(adapter);
// This code runs later, after 'execute' is called and the response is returned
ConnectionAsyncTask task = new ConnectionAsyncTask(new AsyncResponse<List<Student>>() {
#Override
public void onResponse(List<Student> response) {
students.clear();
students.addAll(response);
adapter.notifyDataSetChanged();
progress.hide();
}
});
// Optionally show some progress while waiting
this.progress.show();
// TODO: Use real URL
task.execute("http://www.somesite.com/data");
}
}
I am working on a RecyclerView which must be Draggable & swipeable. Everything works perfect.
The Data is getting Fetched in one class called ExerciseDataProvider & the RV code is another Fragment RecyclerListViewFragment.
The problem is that i can't notify Data changed from the FetchExercise on postExecute method. So the Data's are not getting populated in the RV.
Please Guide me in a Right Direction.
ACTIVITY
public class DraggableSwipeableExampleActivity extends AppCompatActivity {
private static final String FRAGMENT_TAG_DATA_PROVIDER = "data provider";
private static final String FRAGMENT_LIST_VIEW = "list view";
private static final String FRAGMENT_TAG_ITEM_PINNED_DIALOG = "item pinned dialog";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(new ExampleDataProviderFragment(), FRAGMENT_TAG_DATA_PROVIDER)
.commit();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new RecyclerListViewFragment(), FRAGMENT_LIST_VIEW)
.commit();
}
}
public AbstractDataProvider getDataProvider() {
final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DATA_PROVIDER);
return ((ExampleDataProviderFragment) fragment).getDataProvider();
}
DATA PROVIDER
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
public ExerciseDataProvider() {
new FetchExercise().execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Void> {
#Override
protected Void doInBackground(Void... params) {
final int viewType = 0;
final int swipeReaction = RecyclerViewSwipeManager.REACTION_CAN_SWIPE_UP | RecyclerViewSwipeManager.REACTION_CAN_SWIPE_DOWN;
String url = "https://gist.githubusercontent.com/fake/cb9aa5494e7ee36ac3ca/raw/a4abfd19368063/exercise.JSON";
Log.d("Path", url);
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
String jsonData = response.body().string();
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
final long id = i;
JSONObject jsonObject = jsonArray.getJSONObject(i);
String exercise_name = jsonObject.getString("name");
int exercise_duration = jsonObject.getInt("duration");
mData.add(new ConcreteData(id, viewType, exercise_name, exercise_duration, swipeReaction));
Log.d("exercise_name", exercise_name);
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
#Override
public int getCount() {
return mData.size();
}
#Override
public Data getItem(int index) {
if (index < 0 || index >= getCount()) {
throw new IndexOutOfBoundsException("index = " + index);
}
return mData.get(index);
}
#Override
public int undoLastRemoval() {
if (mLastRemovedData != null) {
int insertedPosition;
if (mLastRemovedPosition >= 0 && mLastRemovedPosition < mData.size()) {
insertedPosition = mLastRemovedPosition;
} else {
insertedPosition = mData.size();
}
mData.add(insertedPosition, mLastRemovedData);
mLastRemovedData = null;
mLastRemovedPosition = -1;
return insertedPosition;
} else {
return -1;
}
}
#Override
public void moveItem(int fromPosition, int toPosition) {
if (fromPosition == toPosition) {
return;
}
final ConcreteData item = mData.remove(fromPosition);
mData.add(toPosition, item);
mLastRemovedPosition = -1;
}
#Override
public void removeItem(int position) {
//noinspection UnnecessaryLocalVariable
final ConcreteData removedItem = mData.remove(position);
mLastRemovedData = removedItem;
mLastRemovedPosition = position;
}
public static final class ConcreteData extends Data {
private final long mId;
private final String mText;
private final int mViewType;
private final int mDuration;
private boolean mPinned;
ConcreteData(long id, int viewType, String text, int duration, int swipeReaction) {
mId = id;
mViewType = viewType;
mText = text;
mDuration = duration;
}
#Override
public int getViewType() {
return mViewType;
}
#Override
public int getDuration() {
return mDuration;
}
#Override
public long getId() {
return mId;
}
#Override
public String toString() {
return mText;
}
#Override
public String getText() {
return mText;
}
#Override
public boolean isPinned() {
return mPinned;
}
#Override
public void setPinned(boolean pinned) {
mPinned = pinned;
}
}
}
RecyclerListViewFragment
public class RecyclerListViewFragment extends Fragment {
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.Adapter mAdapter;
private RecyclerView.Adapter mWrappedAdapter;
private RecyclerViewDragDropManager mRecyclerViewDragDropManager;
private RecyclerViewSwipeManager mRecyclerViewSwipeManager;
private RecyclerViewTouchActionGuardManager mRecyclerViewTouchActionGuardManager;
public RecyclerListViewFragment() {
super();
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_list_view, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//noinspection ConstantConditions
mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
mLayoutManager = new LinearLayoutManager(getContext());
// touch guard manager (this class is required to suppress scrolling while swipe-dismiss animation is running)
mRecyclerViewTouchActionGuardManager = new RecyclerViewTouchActionGuardManager();
mRecyclerViewTouchActionGuardManager.setInterceptVerticalScrollingWhileAnimationRunning(true);
mRecyclerViewTouchActionGuardManager.setEnabled(true);
// drag & drop manager
mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
(NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z3));
// swipe manager
mRecyclerViewSwipeManager = new RecyclerViewSwipeManager();
//adapter
final MyDraggableSwipeableItemAdapter myItemAdapter = new MyDraggableSwipeableItemAdapter(getDataProvider());
myItemAdapter.setEventListener(new MyDraggableSwipeableItemAdapter.EventListener() {
#Override
public void onItemRemoved(int position) {
((DraggableSwipeableExampleActivity) getActivity()).onItemRemoved(position);
}
#Override
public void onItemViewClicked(View v, boolean pinned) {
onItemViewClick(v, pinned);
}
});
mAdapter = myItemAdapter;
mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(myItemAdapter); // wrap for dragging
mWrappedAdapter = mRecyclerViewSwipeManager.createWrappedAdapter(mWrappedAdapter); // wrap for swiping
final GeneralItemAnimator animator = new SwipeDismissItemAnimator();
animator.setSupportsChangeAnimations(false);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter
mRecyclerView.setItemAnimator(animator);
// additional decorations
//noinspection StatementWithEmptyBody
if (supportsViewElevation()) {
// Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
} else {
mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z1)));
}
mRecyclerView.addItemDecoration(new SimpleListDividerDecorator(ContextCompat.getDrawable(getContext(), R.drawable.list_divider_h), true));
mRecyclerViewTouchActionGuardManager.attachRecyclerView(mRecyclerView);
mRecyclerViewSwipeManager.attachRecyclerView(mRecyclerView);
mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
}
#Override
public void onPause() {
mRecyclerViewDragDropManager.cancelDrag();
super.onPause();
}
#Override
public void onDestroyView() {
if (mRecyclerViewDragDropManager != null) {
mRecyclerViewDragDropManager.release();
mRecyclerViewDragDropManager = null;
}
if (mRecyclerViewSwipeManager != null) {
mRecyclerViewSwipeManager.release();
mRecyclerViewSwipeManager = null;
}
if (mRecyclerViewTouchActionGuardManager != null) {
mRecyclerViewTouchActionGuardManager.release();
mRecyclerViewTouchActionGuardManager = null;
}
if (mRecyclerView != null) {
mRecyclerView.setItemAnimator(null);
mRecyclerView.setAdapter(null);
mRecyclerView = null;
}
if (mWrappedAdapter != null) {
WrapperAdapterUtils.releaseAll(mWrappedAdapter);
mWrappedAdapter = null;
}
mAdapter = null;
mLayoutManager = null;
super.onDestroyView();
}
private void onItemViewClick(View v, boolean pinned) {
int position = mRecyclerView.getChildAdapterPosition(v);
if (position != RecyclerView.NO_POSITION) {
((DraggableSwipeableExampleActivity) getActivity()).onItemClicked(position);
}
}
public AbstractDataProvider getDataProvider() {
return ((DraggableSwipeableExampleActivity) getActivity()).getDataProvider();
}
public void notifyItemChanged(int position) {
mAdapter.notifyItemChanged(position);
}
public void notifyItemInserted(int position) {
mAdapter.notifyItemInserted(position);
mRecyclerView.scrollToPosition(position);
}
}
To update recyclerView from onPostExecute in a data provider class, your onPostExecute should have access to context where your recyclerView is defined.
Since your FetchExercise async task is defined inside ExerciseDataProvider class, try passing activity context to ExerciseDataProvider's constructor and then pass it on to FetchExercise async task as described here: getting context in AsyncTask
public class MyCustomTask extends AsyncTask<Void, Void, Long> {
private Context mContext;
public MyCustomTask (Context context){
mContext = context;
}
protected void onPostExecute(Long result) {
//use mContext to update recycler view
}
}
}
Use the context to update the recyclerView.
UPDATE
Step 1
Define an interface that will notify your activity of data set change inside a class that initialises your data provider class and pass activity context to constructor of data provider class.
public class ExampleDataProviderFragment extends Fragment {
private AbstractDataProvider mDataProvider;
//Define an interface that will notify your activity of data set change
public interface EventListener {
void onNotifyDataSetChanged();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Pass activity context to ExerciseDataProvider
mDataProvider = new ExerciseDataProvider(getActivity());
}
public AbstractDataProvider getDataProvider() {
return mDataProvider;
}
}
Step 2
Add context parameter to ExerciseDataProvider's constructor and use it to notify activity that implements your interface to notify dataset change.
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
//Add context parameter to constructor
public ExerciseDataProvider(Context context) {
//Pass context to async task
new FetchExercise(context).execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Integer> {
Context mContext;
public FetchExercise(Context context) {
mContext = context;
}
#Override
protected Integer doInBackground(Void... params) {
...
return 1;
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
//Typecast context to interface defined above
//and notify dataset changes by calling its method
ExampleDataProviderFragment.EventListener eventListener = (ExampleDataProviderFragment.EventListener)mContext;
eventListener.onNotifyDataSetChanged();
}
}
}
Step 3
Implement above defined interface in your activity class and notify recyclerview adapter inside it
public class DraggableSwipeableExampleActivity extends AppCompatActivity
implements ExampleDataProviderFragment.EventListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
//implement interface method and notify recyclerview of changes
#Override
public void onNotifyDataSetChanged() {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_LIST_VIEW);
// you might need to change visibility of `mWrappedAdapter` in the fragment that defines it or create a getter for it so that you can access it here
((RecyclerListViewFragment) fragment).mWrappedAdapter.notifyDataSetChanged();
}
...
}
I think #random is correct you should be notifying your Recycle view on post execute.
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
or if you have done something in your async task to add/delete something in the data set you would do:
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyItemRemoved(itemposition); // or item added
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
Hope it helps !