Paging Library & Room : Content not loading when Recyclerview scroll - android

I'm trying to implement endless scrolling with recyclerview and Room using Paging Library in my application According to this article: https://developer.android.com/topic/libraries/architecture/paging/#database-examples
Some of the data is loaded at initialization but when user scroll, nothing happens, the rest of the data is not fetched
My Dao
#Dao
public interface MessageDao {
.
.
.
#Query("SELECT * FROM messages ORDER BY created DESC")
DataSource.Factory<Integer, Message> getPagedMessage();
}
ViewModel
public class SmsViewModel extends AndroidViewModel {
private LiveData<PagedList<Message>> liveData;
public SmsViewModel(#NonNull Application application) {
super(application);
DataSource.Factory<Integer, Message> factory = AppDatabase.getInstance(getApplication()).messageDao().getPagedMessage();
PagedList.Config config = (new PagedList.Config.Builder())
.setPageSize(10)
.setInitialLoadSizeHint(20)
.setPrefetchDistance(5)
.setEnablePlaceholders(false)
.build();
liveData = new LivePagedListBuilder<>(factory, config).build();
}
public LiveData<PagedList<Message>> getLiveData() {
return liveData;
}
}
Adapter class
public abstract class BasePagedAdapter<T> extends PagedListAdapter<T, BasePagedAdapter.MyViewHolder> {
private OnClickListener onClickListener;
private OnLongClickListener onLongClickListener;
public BasePagedAdapter(DiffUtil.ItemCallback<T> callback) {
super(callback);
}
public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public void setOnLongClickListener(OnLongClickListener onLongClickListener) {
this.onLongClickListener = onLongClickListener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, getLayoutIdForType(viewType), parent, false);
return new MyViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull BasePagedAdapter.MyViewHolder holder, int position) {
T t = getDataAtPosition(position);
holder.bind(t);
}
public abstract T getDataAtPosition(int position);
abstract #LayoutRes
public int getLayoutIdForType(int viewType);
class MyViewHolder extends RecyclerView.ViewHolder {
private final ViewDataBinding binding;
MyViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
itemView.setOnClickListener(v -> {
if (onClickListener != null) {
onClickListener.onClick(getAdapterPosition());
}
});
itemView.setOnLongClickListener(view -> {
if(onLongClickListener != null) {
onLongClickListener.onLongClick(getAdapterPosition());
return true;
}
return false;
});
}
void bind(T obj) {
binding.setVariable(BR.obj, obj);
binding.executePendingBindings();
}
}
public interface OnClickListener {
void onClick(int position);
}
public interface OnLongClickListener {
void onLongClick(int position);
}
}
Activity
public class SmsActivity extends AppCompatActivity {
#BindView(R.id.lyt_empty)
View lytEmpty;
#BindView(R.id.progressBar)
ProgressBar progressBar;
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
PagedList<Message> messages;
BasePagedAdapter<Message> adapter = new BasePagedAdapter<Message>(DIFF_CALLBACK) {
#Override
public Message getDataAtPosition(int position) {
if (messages != null) {
return messages.get(position);
}
return null;
}
#Override
public int getLayoutIdForType(int viewType) {
return R.layout.item_sms_list;
}
};
SmsViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ButterKnife.bind(this);
viewModel = ViewModelProviders.of(this).get(SmsViewModel.class);
adapter.setOnClickListener(position -> {
Intent intent = new Intent(this, SmsDetailActivity.class);
intent.putExtra(SmsDetailActivity.EXTRA_SMS, messages.get(position));
startActivity(intent);
});
recyclerView.setAdapter(adapter);
recyclerView.setNestedScrollingEnabled(false);
loadData();
}
private void loadData() {
viewModel.getLiveData().observe(this, messages -> {
progressBar.setVisibility(View.GONE);
this.messages = messages;
if (messages != null && messages.size() > 0) {
adapter.submitList(messages);
recyclerView.setVisibility(View.VISIBLE);
lytEmpty.setVisibility(View.GONE);
} else {
recyclerView.setVisibility(View.GONE);
lytEmpty.setVisibility(View.VISIBLE);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_sms_list, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return true;
}
private static DiffUtil.ItemCallback<Message> DIFF_CALLBACK =
new DiffUtil.ItemCallback<Message>() {
#Override
public boolean areItemsTheSame(Message oldItem, Message newItem) {
return oldItem.uid == newItem.uid;
}
#Override
public boolean areContentsTheSame(Message oldItem, #NonNull Message newItem) {
return oldItem.equals(newItem);
}
};
}
There are currently 25 elements in the message table but only 20 are displayed at initialization. When I scroll the Recyclerview nothing happens the other 5 elements are not displayed

You need to do like this, I think.
recyclerView.setNestedScrollingEnabled(true);
credit
See here:
https://github.com/googlesamples/android-architecture-components/issues/215#issuecomment-394849133

Related

Recyclerview-selection select sometimes 2 items when I long click

I created a recyclerview with a StaggeredGridLayoutManager as LayoutManager.
I wanted to implement a multiple selection thanks to the recyclerview-selection library but when I make a long click on a certain item, it sometimes selects 2. (I am using a DB to retrieve the data)
This is my main code:
GridRecyclerView notesRecyclerView = binding.noteRecyclerView;
notesRecyclerView.setHasFixedSize(true);
adapter = new NoteAdapterTest();
notesRecyclerView.setAdapter(adapter);
SelectionTracker<Long> tracker = new SelectionTracker.Builder<>("mySelection", notesRecyclerView,
new StableIdKeyProvider(notesRecyclerView),
new NoteDetailsLookup(notesRecyclerView),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything()).build();
adapter.setSelectionTracker(tracker);
My Adapter:
private SelectionTracker<Long> selectionTracker;
private static final DiffUtil.ItemCallback<Note> DIFF_CALLBACK = new DiffUtil.ItemCallback<Note>() {
#Override
public boolean areItemsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getContent().equals(newItem.getContent());
}
};
public NoteAdapterTest() {
super(DIFF_CALLBACK);
setHasStableIds(true);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
AdapterNoteBinding binding = AdapterNoteBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new NoteHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Note note = getItem(position);
((NoteHolder) holder).bind(note, selectionTracker.isSelected((long) position));
}
public static class NoteHolder extends RecyclerView.ViewHolder {
private final AdapterNoteBinding binding;
public NoteHolder(#NonNull AdapterNoteBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public final void bind(Note note, boolean isActive) {
Context context = binding.getRoot().getContext();
itemView.setActivated(isActive);
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteTitle.setText(note.getTitle());
}
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return getItemId();
}
};
}
}
#Override
public long getItemId(int position) {
return position;
}
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
}
My lookup:
public class NoteDetailsLookup extends ItemDetailsLookup<Long> {
private final RecyclerView recyclerView;
public NoteDetailsLookup(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
#Nullable
#Override
public ItemDetails<Long> getItemDetails(#NonNull MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder instanceof NoteAdapterTest.NoteHolder) {
return ((NoteAdapterTest.NoteHolder) viewHolder).getItemDetails();
}
}
return null;
}
}
How do you know that 2 items are selected? What the selection tracker returns?
Try to set background tint in your view holder always:
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.white)); // your default color
}
It may be just a view state issue.

How to sort a Recyclerview?

I am trying to sort the list of custom objects and display in Recyclerview. After displaying the items in recyclerview. I am sorting the list in the Activity and calling adapter.notifyDataSetChanged() but its not working.
Code in MainActivity.java:
Collections.sort(beerDataList, new Comparator<BeerData>() {
#Override
public int compare(BeerData o1, BeerData o2) {
return o1.getAbv().compareToIgnoreCase(o2.getAbv());
}
});
adapter.notifyDataSetChanged();
Adapter code:
public class BeerListAdapter extends RecyclerView.Adapter<BeerListAdapter.MyViewHolder> {
private static ClickListener clickListener;
private Context context;
private LayoutInflater inflater;
private List<BeerData> beerDataList;
private List<BeerData> copyList;
public BeerListAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
beerDataList = new ArrayList<>();
copyList = new ArrayList<>();
}
public void setList(List<BeerData> beerDataList) {
if (this.beerDataList.isEmpty()) {
this.beerDataList.addAll(beerDataList);
copyList.addAll(beerDataList);
}
notifyDataSetChanged();
}
public void filter(String query) {
beerDataList.clear();
if (query.isEmpty()) {
beerDataList.addAll(copyList);
} else {
for (BeerData beerData : copyList) {
if (beerData.getName().replaceAll("[\\s]", "").toLowerCase().contains(query.replaceAll("[\\s]", "").toLowerCase())) {
beerDataList.add(beerData);
}
}
}
notifyDataSetChanged();
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_beer_row, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.beerName.setText(beerDataList.get(position).getName());
holder.styleName.setText(beerDataList.get(position).getStyle());
holder.alcoholContent.setText("Alcohol Content: " + beerDataList.get(position).getAbv());
holder.quantity.setText(beerDataList.get(position).getQuantity().toString());
}
#Override
public int getItemCount() {
return beerDataList.size();
}
public void setOnItemClickListener(ClickListener clickListener) {
BeerListAdapter.clickListener = clickListener;
}
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView beerName, alcoholContent, styleName, quantity;
ImageView add, minus;
MyViewHolder(View itemView) {
super(itemView);
beerName = itemView.findViewById(R.id.beerName);
alcoholContent = itemView.findViewById(R.id.alcoholContent);
styleName = itemView.findViewById(R.id.beerStyle);
quantity = itemView.findViewById(R.id.quantity);
add = itemView.findViewById(R.id.add);
minus = itemView.findViewById(R.id.minus);
add.setOnClickListener(this);
minus.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.add:
clickListener.onAddClick(getAdapterPosition(), v);
break;
case R.id.minus:
clickListener.onMinusClick(getAdapterPosition(), v);
break;
}
}
}
public interface ClickListener {
void onAddClick(int position, View v);
void onMinusClick(int position, View v);
}
}
Here is my MainActivity:
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener, LoginResultCallBack {
private static final String TAG = MainActivity.class.getSimpleName();
#Inject
MyApplication application;
LoginViewModel loginViewModel;
#BindView(R.id.beerListRecyclerView)
RecyclerView recyclerView;
#BindView(R.id.progressBar)
ProgressBar progressBar;
private BeerListAdapter adapter;
private List<BeerData> beerDataList;
private Menu menu;
private String sortType = "unsorted";
private Disposable networkDisposable;
List<BeerData> originalList = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
((MyApplication) getApplication()).getComponent().inject(this);
loginViewModel = ViewModelProviders.of(this,new LoginViewModelFactory(application,this)).get(LoginViewModel.class);
adapter = new BeerListAdapter(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new BeerListAdapter.ClickListener() {
#Override
public void onAddClick(int position, View v) {
addItem(position);
}
#Override
public void onMinusClick(int position, View v) {
removeItem(position);
}
});
}
public void fetchData() {
progressBar.setVisibility(View.VISIBLE);
loginViewModel.fetchData();
loginViewModel.result.observe(this, new Observer<NetworkResponse>() {
#Override
public void onChanged(#Nullable NetworkResponse networkResponse) {
progressBar.setVisibility(View.GONE);
assert networkResponse != null;
if (networkResponse.getPostData() != null) {
beerDataList = new ArrayList<>();
originalList = networkResponse.getPostData();
beerDataList = networkResponse.getPostData();
Log.e(TAG, "Response: " + networkResponse.getPostData().get(0).getAbv());
progressBar.setVisibility(View.GONE);
adapter.setList(networkResponse.getPostData());
} else {
Log.e(TAG, "Response: " + networkResponse.getError().getLocalizedMessage());
progressBar.setVisibility(View.GONE);
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
this.menu = menu;
MenuItem searchItem = menu.findItem(R.id.searchBar);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setQueryHint("Search Beer");
searchView.setOnQueryTextListener(this);
searchView.setIconified(false);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.cart:
Log.e(TAG, "cart clicked");
break;
case R.id.sort:
if (beerDataList != null) {
if (sortType.equalsIgnoreCase("unsorted")) {
Log.e(TAG,beerDataList.size()+" Size");
Collections.sort(beerDataList, new Comparator<BeerData>() {
#Override
public int compare(BeerData o1, BeerData o2) {
return o1.getAbv().compareToIgnoreCase(o2.getAbv());
}
});
adapter.notifyDataSetChanged();
sortType = "ascend";
menu.getItem(2).setTitle(R.string.unsorted);
} else if (sortType.equalsIgnoreCase("ascend")) {
Log.e(TAG,beerDataList.size()+" Size");
Collections.sort(beerDataList, new Comparator<BeerData>() {
#Override
public int compare(BeerData o1, BeerData o2) {
return o2.getAbv().compareToIgnoreCase(o1.getAbv());
}
});
adapter.notifyDataSetChanged();
sortType = "descend";
menu.getItem(2).setTitle(R.string.view_original);
} else {
Log.e(TAG,beerDataList.size()+" Size");
beerDataList.clear();
beerDataList.addAll(originalList);
adapter.notifyDataSetChanged();
sortType = "unsorted";
menu.getItem(2).setTitle(R.string.sort);
}
}
break;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onQueryTextSubmit(String query) {
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
adapter.filter(newText);
return true;
}
#Override
protected void onResume() {
super.onResume();
networkDisposable = ReactiveNetwork.observeNetworkConnectivity(application)
.subscribeOn(Schedulers.io())
.filter(ConnectivityPredicate.hasState(NetworkInfo.State.CONNECTED))
.filter(ConnectivityPredicate.hasType(ConnectivityManager.TYPE_WIFI))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Connectivity>() {
#Override
public void accept(final Connectivity connectivity) {
// do something
if (connectivity.isAvailable()) {
fetchData();
} else {
Toast.makeText(MainActivity.this, R.string.internet_issue, Toast.LENGTH_LONG).show();
}
}
});
}
private void addItem(int position) {
int quantity = beerDataList.get(position).getQuantity();
quantity++;
beerDataList.get(position).setQuantity(quantity);
menu.getItem(1).setIcon(ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_cart_full));
adapter.notifyDataSetChanged();
}
private void removeItem(int position) {
if (beerDataList.get(position).getQuantity() > 0) {
int quantity = beerDataList.get(position).getQuantity();
quantity--;
if (quantity == 0)
menu.getItem(1).setIcon(ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_cart_empty));
beerDataList.get(position).setQuantity(quantity);
adapter.notifyDataSetChanged();
}
}
private void safelyDispose(Disposable... disposables) {
for (Disposable subscription : disposables) {
if (subscription != null && !subscription.isDisposed()) {
subscription.dispose();
}
}
}
#Override protected void onPause() {
super.onPause();
safelyDispose(networkDisposable);
}
#Override
public void onSuccess(String message) {
}
#Override
public void onError(String message) {
}
#Override
public void showProgress() {
}
#Override
public void hideProgress() {
}
}
What am I doing wrong here ?
You're not passing the sorted list to your adapter. You're just calling notifyDataSetChanged() which just tells the adapter that there is new data in its class and to use the baerDataList that is currently set within the class to refresh the list. Try this in your main activity.
Collections.sort(beerDataList, new Comparator<BeerData>() {
#Override
public int compare(BeerData o1, BeerData o2) {
return o1.getAbv().compareToIgnoreCase(o2.getAbv());
}
});
adapter.setBeerDataList(beerDataList);
And setBeerDataList() in your adapter class.
private List<BeerData> beerDataList;
public void setBeerDataList(List<BeerData> sortedBeerDataList){
this.beerDataList = sortedBeerDataList;
notifyDataSetChanged();
}
Put sort function in MainActivity to BeerListAdapter:
private void sortBeer() {
Collections.sort(beerDataList, new Comparator<BeerData>() {
#Override
public int compare(BeerData o1, BeerData o2) {
return o1.getAbv().compareToIgnoreCase(o2.getAbv());
}
});
notifyDataSetChanged();
}
then call adapter.sortBeer() when do sort action

Reordering issue in Staggered GridView Layout

I have an issue while using staggered GridView Layout. After I delete an element from the list, the item in the staggered gridview don't properly rearrange themselves. I mean, they leave lots of empty spaces above them.
Here are some pictures of before and after deleting,
before:
after:
one more:
But after I close the app and open it again, they reorder themselves properly.
Here is my code,
Fragment:
public class TaskListFragment extends Fragment {
public static final String TAG = TaskListFragment.class.getSimpleName();
private FragmentTaskListBinding mBinding;
private TaskViewModel viewModel;
private TaskListAdapter taskListAdapter;
private List<TaskEntity> entityList;
private StaggeredGridLayoutManager layoutManager;
public TaskListFragment() {
// Required empty public constructor
}
public static TaskListFragment instantiate() {
return new TaskListFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_task_list,
container, false);
mBinding.setFabClick(clickCallback);
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mBinding.taskListRecyclerView.setItemAnimator(new DefaultItemAnimator());
mBinding.taskListRecyclerView.setLayoutManager(layoutManager);
return mBinding.getRoot();
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewModel = ViewModelProviders.of(this)
.get(TaskViewModel.class);
subscribeUi(viewModel);
}
private void subscribeUi(TaskViewModel viewModel) {
viewModel.loadTasks().observe(this, taskEntities -> {
if (taskEntities != null) {
setListData(taskEntities);
}
});
mBinding.executePendingBindings();
}
private void setListData(List<TaskEntity> taskEntities) {
this.entityList = taskEntities;
taskListAdapter = new TaskListAdapter();
taskListAdapter.setTaskList(entityList);
mBinding.taskListRecyclerView.setAdapter(taskListAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(createHelperCallback());
itemTouchHelper.attachToRecyclerView(mBinding.taskListRecyclerView);
}
private final FabClickCallback clickCallback = new FabClickCallback() {
#Override
public void onFabClick() {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
((MainListActivity) getActivity()).start();
}
}
};
private ItemTouchHelper.SimpleCallback createHelperCallback() {
return new SwipeToDelete() {
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
viewModel.deleteTask(entityList.get(position));
entityList.remove(position);
taskListAdapter.notifyItemRemoved(position);
}
};
}
}
Adapter:
public class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {
private List<? extends Task> mTasks;
public TaskListAdapter() {
}
public void setTaskList(List<? extends Task> tasks) {
if (mTasks == null) {
mTasks = tasks;
notifyItemRangeInserted(0, mTasks.size());
} else {
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
#Override
public int getOldListSize() {
return mTasks.size();
}
#Override
public int getNewListSize() {
return tasks.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mTasks.get(oldItemPosition).getTId() ==
tasks.get(newItemPosition).getTId();
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
Task newTask = tasks.get(newItemPosition);
Task oldTask = mTasks.get(oldItemPosition);
return newTask.getTId() == oldTask.getTId()
&& Objects.equals(newTask.getTaskName(), oldTask.getTaskName())
&& Objects.equals(newTask.getTaskDescription(), oldTask.getTaskDescription());
}
});
mTasks = tasks;
result.dispatchUpdatesTo(this);
}
}
#Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TaskItemBinding mBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), R.layout.task_item,
parent, false);
return new TaskViewHolder(mBinding);
}
#Override
public void onBindViewHolder(TaskViewHolder holder, int position) {
holder.mBinding.setTask(mTasks.get(position));
}
#Override
public int getItemCount() {
return mTasks == null ? 0 : mTasks.size();
}
static class TaskViewHolder extends RecyclerView.ViewHolder {
final private TaskItemBinding mBinding;
public TaskViewHolder(TaskItemBinding taskItemBinding) {
super(taskItemBinding.getRoot());
mBinding = taskItemBinding;
}
}
}
Edit: I found the answer to my problem.
I've been creating a new adapter over and over and setting it to the recyclerview in setListData() method instead of putting them in onCreateView method. Moving those two lines of code to onCreateView fixed my issue.

No Adapter Attached; Skipping Layout when using retrofit

This is how I'm setting up my Recycler view in my Search Fragment. I don't understand why I keep getting the same error and what I'm missing.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search, container, false);
searchText = (EditText)view.findViewById(R.id.searchText);
searchButton = (Button)view.findViewById(R.id.searchButton);
nutritionRecyclerView = (RecyclerView)view.findViewById(R.id.nutritionRecyclerView);
layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ApiClient.getInstance().getNutritionApiAdapter()
.getSearchResults(
AppDefines.APPLICATION_ID,
AppDefines.APPLICATION_KEY,
searchText.getText().toString())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<SearchResultsModel>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResultsModel searchResultsModel) {
// On handling the http response, instantiate a new adapter with the results
adapter = new NutritionAdapter(searchResultsModel.getSearchResults());
adapter.setOnItemSelected(new NutritionAdapter.OnItemSelected() {
#Override
public void onSelected(NutritionItemModel item) {
if (onFragmentEvent != null) {
onFragmentEvent.onEvent(item);
}
}
});
// Assigning the LayoutManager to the RecyclerView
nutritionRecyclerView.setLayoutManager(layoutManager);
// Assigning the Adapter to the RecyclerView. If this isn't done, the view will not populate
nutritionRecyclerView.setAdapter(adapter);
}
});
}
});
return view;
}
Here is my ApiClient
public class ApiClient {
private static ApiClient instance;
public static ApiClient getInstance() {
if (instance == null) {
instance = new ApiClient();
}
return instance;
}
public NutritionApiAdapter getNutritionApiAdapter() {
NutritionApiAdapter api = new Retrofit.Builder()
.baseUrl(AppDefines.BASE_API_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(NutritionApiAdapter.class);
return api;
}
}
And this is my NutritionApiAdapter
public interface NutritionApiAdapter {
#GET("item")
Observable<SearchResultsModel> getSearchResults(
#Query("_app_id") String appId,
#Query("_app_key") String appKey,
#Query("q") String searchString);
}
NutritionAdpater
public class NutritionAdapter extends RecyclerView.Adapter<NutritionItemViewHolder>{
private ArrayList<NutritionItemModel> nutritionItemCollection;
private OnItemSelected onItemSelected;
public NutritionAdapter(ArrayList<NutritionItemModel> nutritionItemCollection){
this.nutritionItemCollection = nutritionItemCollection;
}
#Override
public NutritionItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_nutrition, parent, false);
return new NutritionItemViewHolder(view);
}
#Override
public void onBindViewHolder(NutritionItemViewHolder holder,int position) {
NutritionItemModel item = nutritionItemCollection.get(position);
holder.setOnRecipeItemClicked(new NutritionItemViewHolder.OnNutritionItemClicked(){
#Override
public void onClick(NutritionItemModel item) {
if(onItemSelected != null) {
onItemSelected.onSelected(item);
}
}
});
// Bind the RecipeItemModel data to the view managed by the ViewHolder
holder.bind(item);
}
#Override
public final void onViewRecycled(final NutritionItemViewHolder holder) {
super.onViewRecycled(holder);
holder.setOnRecipeItemClicked(null);
holder.unbind();
}
#Override
public int getItemCount() {return nutritionItemCollection.size();}
public void setOnItemSelected(OnItemSelected onItemSelected) {
this.onItemSelected = onItemSelected;
}
public interface OnItemSelected {
void onSelected(NutritionItemModel item);
}
}
and NutritionItemViewHolder
public class NutritionItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView foodName;
private ImageView foodThumbnail;
private NutritionItemModel item;
private TextView item_name;
private TextView brand_name;
private OnNutritionItemClicked onNutritionItemClicked;
//todo add other elements here
public NutritionItemViewHolder(final View itemView) {super(itemView);}
public final void bind(final NutritionItemModel item) {
item_name = (TextView)itemView.findViewById(R.id.item_name);
brand_name = (TextView)itemView.findViewById(R.id.brand_name);
//foodThumbnail = (ImageView)itemView.findViewById(R.id.foodThumbnail);
//todo add other element id things here
/*
//used for preloading images
Glide.with(itemView.getContext())
.load(item.getSmallImageUrls().get(0))
.into(foodThumbnail);
*/
item_name.setText(item.getFields().getItem_name());
brand_name.setText(item.getFields().getBrand_name());
}
public final void unbind() {
//todo ?
//For later with butterknife
}
#Override
public void onClick(View v) {
if(onNutritionItemClicked != null) {
onNutritionItemClicked.onClick(item);
}
}
// The setter that allows other classes to create a reference to the listener.
public void setOnRecipeItemClicked(OnNutritionItemClicked onNutritionItemClicked) {
this.onNutritionItemClicked = onNutritionItemClicked;
}
// An interface is added as an internal implementation in our ViewHolder. This will allow
// classes that instantiate a new instance of this ViewHolder to subscribe to this interface
// and listen for events.
public interface OnNutritionItemClicked {
void onClick(NutritionItemModel item);
}
}
set the layoutmanager,
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
list.setLayoutManager(llm);
list.setAdapter(adapter);
Maybe this can help you.

RX Java RecycleView

I'm not familiar with rx java. Trying to use it with recycleview. For some reason my code is not working Here is my code.
Fragment with recycle view
public class CheeseListFragment extends Fragment {
private final CompositeSubscription subscriptions = new CompositeSubscription();
private PublishSubject<String> timespanSubject;
private final Func1<String, Observable<LiveInfo>> trendingSearch =
new Func1<String, Observable<LiveInfo>>() {
#Override
public Observable<LiveInfo> call(String s) {
RadioLiveInfoObservableService radioLiveInfoObservableService=ApiProvider.getInstance().getRadioObserverInfo();
return radioLiveInfoObservableService.radioInfo(Type.INTERVAL)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(trendingError)
.onErrorResumeNext(Observable.<LiveInfo>empty());
}
};
private final Action1<Throwable> trendingError = new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
Timber.e(throwable, "Failed to get trending repositories");
}
};
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
timespanSubject = PublishSubject.create();
final RecyclerView rv = (RecyclerView) inflater.inflate(
R.layout.fragment_cheese_list, container, false);
setupRecyclerView(rv);
subscriptions.add(timespanSubject
.flatMap(trendingSearch)
.map(SearchResultToRepositoryList.instance())
.subscribe(adapter));
return rv;
}
private SimpleStringRecyclerViewAdapter adapter;
private void setupRecyclerView(RecyclerView recyclerView) {
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
adapter=new SimpleStringRecyclerViewAdapter(getActivity(), new SimpleStringRecyclerViewAdapter.CurrentShowClickListener() {
#Override
public void onCurrentShowClick(Current currentShow) {
Intent intent = new Intent(CApplication.getAppContext(), CheeseDetailActivity.class);
intent.putExtra(CheeseDetailActivity.EXTRA_NAME, currentShow.getName());
CApplication.getAppContext().startActivity(intent);
}
});
recyclerView.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onChanged() {
Toast.makeText(getActivity(),"data changed",Toast.LENGTH_SHORT).show();
}
});
}
private List<String> getRandomSublist(String[] array, int amount) {
ArrayList<String> list = new ArrayList<>(amount);
Random random = new Random();
while (list.size() < amount) {
list.add(array[random.nextInt(array.length)]);
}
return list;
}
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> implements Action1<List<Current>> {
private List<Current> currentShows = Collections.emptyList();
public interface CurrentShowClickListener {
void onCurrentShowClick(Current currentShow);
}
private final CurrentShowClickListener currentShowClickListener;
private final TypedValue mTypedValue = new TypedValue();
private int mBackground;
#Override
public void call(List<Current> currentShows) {
this.currentShows = currentShows;
notifyDataSetChanged();
}
public SimpleStringRecyclerViewAdapter(Context context,CurrentShowClickListener currentShowClickListener) {
context.getTheme().resolveAttribute(R.attr.selectableItemBackground, mTypedValue, true);
mBackground = mTypedValue.resourceId;
this.currentShowClickListener = currentShowClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ListItemView view = (ListItemView)LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override public void onBindViewHolder(ViewHolder viewHolder, int i) {
viewHolder.bindTo(currentShows.get(i));
}
#Override public long getItemId(int position) {
return position;
}
#Override public int getItemCount() {
return currentShows.size();
}
public final class ViewHolder extends RecyclerView.ViewHolder {
public final ListItemView itemView;
private Current currentShow;
public ViewHolder(ListItemView itemView) {
super(itemView);
this.itemView = itemView;
this.itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
currentShowClickListener.onCurrentShowClick(currentShow);
}
});
}
public void bindTo(Current currentShow) {
this.currentShow = currentShow;
itemView.bindTo(currentShow);
}
}
}
}
SearchToResultRepositoryList
public final class SearchResultToRepositoryList implements Func1<LiveInfo, List<Current>> {
private static volatile SearchResultToRepositoryList instance;
public static SearchResultToRepositoryList instance() {
if (instance == null) {
instance = new SearchResultToRepositoryList();
}
return instance;
}
#Override public List<Current> call(LiveInfo repositoriesResponse) {
List<Current> currents=new ArrayList<>();
currents.add(repositoriesResponse.getCurrent());
return currents;
}
}
REST
public interface RadioLiveInfoObservableService {
#GET("/api/live-info/")
Observable<LiveInfo> radioInfo(
#Query("type") Type type);
}
It's just doing nothing. I tried to debug it trendingSearch.call is not called at all.
I can make it work only this way. But still i want to know how to it with subscription
RadioLiveInfoObservableService radioLiveInfoObservableService=ApiProvider.getInstance().getRadioObserverInfo();
radioLiveInfoObservableService.commits(Type.INTERVAL)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(trendingError)
.onErrorResumeNext(Observable.<LiveInfo>empty()).subscribe(new Action1<LiveInfo>() {
#Override
public void call(LiveInfo liveInfo) {
List<Current> currents=new ArrayList<Current>();
currents.add(liveInfo.getCurrent());
adapter.currentShows=currents;
adapter.notifyDataSetChanged();
rv.setAdapter(adapter);
}
});
That's a lot of code to digest looking for errors, but off the top of my head nothing will happen until timeSpanSubject.onNext() is called. I don't see this called anywhere but maybe there is some missing code you are not showing.
If there is no missing code that calls timeSpanSubject.onNext(), then you could use either a BehaviorSubject which will emit an item when first subscribed to or another Observable such as timer or interval depending on what you are trying to do. timer would subscribe to your trendingSearch Observable a single time whereas using an interval would subscribe multiple times.

Categories

Resources