Hey community I want to make a RecyclerView like in the chat applications.
I tried setStackFromEnd and it's working good.
final LinearLayoutManager llm = new LinearLayoutManager(getApplicationContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
llm.setStackFromEnd(true);
My on load more code based on this tutorial, At the end of tutorial you should see a video : Recyclerview Bottom Progress
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
So I need to make a RecyclerView like in chat applications. Stack from end is working perfect but I need to trigger load more method when I go top instead of going bottom. Also add new items on top instead of bottom. Thanks in advance.
You don't need to make use of onScrollListener, if you simply want to show a Button "Load Earlier Messages" at the top. You can simply create an xml for your load more Button, make that as the first row of your RecyclerView and create an Interface to handle click events. I am posting code for the Adpater from one of my projects. Hope that will help you in getting some idea to proceed further.
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<UserMessage> userMessagesList;
private LayoutInflater mLayoutInflater;
private static final int ROW_TYPE_LOAD_EARLIER_MESSAGES = 0;
private static final int ROW_TYPE_SENDER = 1;
private static final int ROW_TYPE_RECEIVER = 2;
private int userId;
private boolean isLoadEarlierMsgs;
private LoadEarlierMessages mLoadEarlierMessages;
public ChatListAdapter(Context context, int userId, List<UserMessage> userMessagesList) {
mContext = context;
this.userMessagesList = userMessagesList;
mLayoutInflater = LayoutInflater.from(mContext);
this.userId = userId;
mLoadEarlierMessages = (LoadEarlierMessages) mContext;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
return new LoadEarlierMsgsViewHolder(mLayoutInflater.inflate(R.layout
.row_load_earlier_messages, parent, false));
case ROW_TYPE_SENDER:
return new SenderMsgViewHolder(mLayoutInflater.inflate(R.layout.row_sender_msg,
parent, false));
case ROW_TYPE_RECEIVER:
return new ReceiverMsgViewHolder(mLayoutInflater.inflate(R.layout
.row_receiver_msg, parent, false));
default:
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
LoadEarlierMsgsViewHolder loadEarlierMsgsViewHolder =
(LoadEarlierMsgsViewHolder) holder;
if (isLoadEarlierMsgs) {
loadEarlierMsgsViewHolder.btLoadEarlierMessages
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mLoadEarlierMessages != null) {
mLoadEarlierMessages.onLoadEarlierMessages();
}
}
});
} else {
loadEarlierMsgsViewHolder.btLoadEarlierMessages.setVisibility(View.GONE);
}
break;
case ROW_TYPE_SENDER:
SenderMsgViewHolder senderMsgViewHolder = (SenderMsgViewHolder) holder;
// set data for your sender chat bubble
break;
case ROW_TYPE_RECEIVER:
ReceiverMsgViewHolder receiverMsgViewHolder = (ReceiverMsgViewHolder) holder;
// set data for your receiver chat bubble
break;
}
}
#Override
public int getItemCount() {
return userMessagesList.size() + 1;
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return ROW_TYPE_LOAD_EARLIER_MESSAGES; // row load earlier messages
} else if (userMessagesList.get(position - 1).getUser_id() == userId) {
return ROW_TYPE_SENDER; // sender row;
} else {
return ROW_TYPE_RECEIVER; // receiver row;
}
}
public interface LoadEarlierMessages {
void onLoadEarlierMessages();
}
public void setLoadEarlierMsgs(boolean isLoadEarlierMsgs) {
this.isLoadEarlierMsgs = isLoadEarlierMsgs;
}
static class LoadEarlierMsgsViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.btLoadEarlierMsgs) Button btLoadEarlierMessages;
public LoadEarlierMsgsViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class SenderMsgViewHolder extends RecyclerView.ViewHolder {
public SenderMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class ReceiverMsgViewHolder extends RecyclerView.ViewHolder {
public ReceiverMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
And finally implement the LoadEarlierMessages listener in your ChatActivity and Override the onLoadEarlierMessages() method
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatActivity extends AppCompatActivity
implements ChatListAdapter.LoadEarlierMessages {
// getting users recent messages and init RecyclerView
private void showUserMessages() {
// initialising LayoutManager for RecyclerView and setting that to RecyclerView
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setStackFromEnd(true); // to start the list from bottom
rvChatsList.setLayoutManager(mLinearLayoutManager);
// initialising RecyclerView Adapter and setting that to the RecyclerView
mChatListAdapter = new ChatListAdapter(this, userId, userMessagesList);
rvChatsList.setAdapter(mChatListAdapter);
// getting count of friend/contact messages and toggling visibility of load more button accordingly
int count = mDataBaseHandler.getCountOfMessages(contactId);
if (count != 0) {
if (count > Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(true);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
userMessagesList.addAll(mDataBaseHandler.getAllMessagesOfContact(contactId));
mChatListAdapter.notifyDataSetChanged();
}
}
#Override
public void onLoadEarlierMessages() {
ArrayList<UserMessage> tempList = mDataBaseHandler
.getPreviousMessagesOfContact(contactId, userMessagesList.size());
if (tempList.size() > 0) {
if (tempList.size() < Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(false);
}
View v = rvChatsList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
for (int i = 0; i < tempList.size(); i++) {
userMessagesList.add(0, tempList.get(i));
}
mChatListAdapter.notifyDataSetChanged();
mLinearLayoutManager.scrollToPositionWithOffset(tempList.size(), top);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
}
}
Hope this will help..!!
Related
I'm trying to build a simple activity for messaging in android.
I have a RecyclerView that list the messages and I set a ScrollListener on it.
When the firstVisibleItem is the third of the list (RecyclerView stack from end), I will make a call to load other elements.
I add this element to my array and I set a new adapter on my RecyclerView for the update.
When I set the new adapter, the list starts one more time from the last message.
So far I have tried to use scrollToPosition to go to the message where the user was before scrollListener was triggered, but still not work at all because the lastVisibleItem become 1 every time.
Is there a way to maintain the last firstVisible element and set this element as the top element after reset the Adapter?
This is the onCreateMethod.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
lastOffset = 0;
requestSize = 20;
loadMessage()
mAdapter = new ChatAdapter(this,mess);
llm = new LinearLayoutManager(this);
llm.setStackFromEnd(true);
mex_list.setLayoutManager(llm);
mex_list.setAdapter(mAdapter);
mex_list.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int lastVisible = llm.findLastVisibleItemPosition();
int firstVisible = llm.findFirstVisibleItemPosition();
lastFirstVisibleItem = firstVisibleItem; //lastFirstVisibleItem is a global variable
if(firstVisible <= 3){
if(!loading) {
loading = true;
loadMessage();
}
}
Log.d("Test Scroll", "LAST: "+ lastVisible+" FIRST: "+firstVisible);
}
});}
This is where the laodMessage() result was managed.
public void manageResult(Object pr, int caller) {
if(caller == MESSAGES_REQ){
if(mess == null)
mess = new ArrayList<>();
ArrayList<Message> tail = (ArrayList<Message>) pr;
if(tail != null && tail.size() > 0) {
lastOffset += tail.size();
mess.addAll(tail);
mAdapter = new ChatAdapter(ctx, mess);
mex_list.setAdapter(mAdapter);
}
llm.scrollToPosition(lastFirstVisibleItem);//lastFirstVisibleItem became always 1
spinner.setVisibility(View.GONE);
printEmpMex(View.GONE);
mex_list.setVisibility(View.VISIBLE);
loading = false;
}
}
This is the ChatAdapter.
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {
private ArrayList<Message> listOfMessages;
private static Context ctx;
private static final int SENT = 100;
private static final int RECIVED = 200;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public TextView hour;
public ViewHolder(View v) {
super(v);
text = (TextView) v.findViewById(R.id.text_body);
hour = (TextView) v.findViewById(R.id.time);
}
void bind(Message m){
text.setText(m.getBody());
hour.setText(formatDateAndHour(m.getDate()));
}
}
public ChatAdapter(Context ctx, ArrayList<Message> m) {
listOfMessages = m;
this.ctx = ctx;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view;
if (viewType == SENT) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_mex_sent, parent, false);
return new ViewHolder(view);
}
else if (viewType == RECIVED){
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_mex_recived, parent, false);
return new ViewHolder(view);
}
return null;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message mex = listOfMessages.get(position);
holder.bind(mex);
}
#Override
public int getItemCount() {
return listOfMessages.size();
}
#Override
public int getItemViewType(int position) {
Message m = listOfMessages.get(position);
if(m.getSender().getIdUser().equals(loggedUser.getIdUser()))
return SENT;
else
return RECIVED;
}}
I really don't know how to make it work.
Every approach is welcome.
Thanks in advance.
I was following this tutorial to implement pagination, and I was following it keenly, but I am uncertain how I should modify my current activity to implement OnLoadMoreListener. I am using an ApiService from retrofit to load data, unlike the tutorial that generates random strings.
This is how my current RecyclerView adapter looks like:
public class CuratedSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int VIEW_TYPE_HEADER = 0;
private static int VIEW_TYPE_ITEM = 1;
private static int VIEW_TYPE_LOADING = 2;
private int lastVisibleItem, totalItemCount;
private int visibleThreshold = 2;
private boolean isLoading;
private List<Object> itemList;
private OnLoadMoreListener onLoadMoreListener;
public CuratedSectionAdapter(RecyclerView recyclerView, List<Object> itemList) {
this.itemList = itemList;
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
RecyclerView itemRecyclerView;
CuratedSectionNestedAdapter nestedAdapter;
LinearLayoutManager layoutManager;
ItemViewHolder(View view) {
super(view);
itemRecyclerView = view.findViewById(R.id.recyclerView_nested);
layoutManager = new LinearLayoutManager(view.getContext(), LinearLayoutManager.HORIZONTAL, false);
}
}
private class HeaderViewHolder extends RecyclerView.ViewHolder {
TextView textViewHeader;
Typeface montserratMedium = Typeface.createFromAsset(getApplicationContext().getAssets(), "fonts/Montserrat-Medium.ttf");
HeaderViewHolder(View view) {
super(view);
textViewHeader = view.findViewById(R.id.textView_header);
textViewHeader.setTypeface(montserratMedium);
}
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public LoadingViewHolder(View view) {
super(view);
progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == VIEW_TYPE_HEADER) {
HeaderViewHolder viewHolder = (HeaderViewHolder) holder;
CuratedSectionHeader header = (CuratedSectionHeader) itemList.get(position);
viewHolder.textViewHeader.setText(header.getHeaderName());
} else if (holder.getItemViewType() == VIEW_TYPE_ITEM) {
ItemViewHolder viewHolder = (ItemViewHolder) holder;
List<CuratedSectionItem> items = (List<CuratedSectionItem>) itemList.get(position);
if (viewHolder.nestedAdapter != null) {
viewHolder.nestedAdapter.setItems(items);
} else {
viewHolder.nestedAdapter = new CuratedSectionNestedAdapter(items);
viewHolder.itemRecyclerView.setLayoutManager(viewHolder.layoutManager);
viewHolder.itemRecyclerView.setAdapter(viewHolder.nestedAdapter);
}
} else if (holder.getItemViewType() == VIEW_TYPE_LOADING) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new HeaderViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_header_row, parent, false));
} else if (viewType == VIEW_TYPE_ITEM) {
return new ItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_row, parent, false));
} else if (viewType == VIEW_TYPE_LOADING) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
return new LoadingViewHolder(view);
}
throw new RuntimeException("Adapter " + viewType + "not found");
}
#Override
public int getItemCount() {
return itemList.size();
}
#Override
public int getItemViewType(int position) {
if (itemList.get(position) instanceof CuratedSectionHeader) {
return VIEW_TYPE_HEADER;
} else {
return itemList.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
}
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.onLoadMoreListener = mOnLoadMoreListener;
}
public void setLoaded() {
isLoading = false;
}
}
The current architecture of my activity uses private classes to make API calls and load the data. At the moment, all items are loaded at once, but ideally there should be only 2 items loaded at a time. I am uncertain how I should load additional items by calling the API service again when I scroll to the bottom. At the very least, I am certain that I will have to use curatedSectionAdapter.setOnLoadMoreListener somehow.
This is how I load all of the items at the moment:
private class Sections {
List<CuratedSection> sections = new ArrayList<>();
public Thread[] thread;
private Sections() {}
public void setSections(ArrayList<CuratedSection> sections) {
this.sections = sections;
}
public void setSectionStories(String sectionId, List<CuratedSectionItem> stories) {
for(CuratedSection s : sections){
if(s.id != null && s.id.equals(sectionId)) {
s.stories = stories;
}
}
}
public void loadStories(String sessionKey) {
thread = new Thread[sections.size()];
for( int s = 0; s < sections.size(); s++) {
thread[s] = new Thread(new LoadStories(sessionKey, sections.get(s)));
thread[s].start();
}
for( int f = 0; f < sections.size(); f++) {
try {
thread[f].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
recyclerView.setLayoutManager(layoutManager);
curatedSectionAdapter = new CuratedSectionAdapter(recyclerView, this.getAdapterInfo());
recyclerView.setAdapter(curatedSectionAdapter);
}
public void loadSections(int numSections) {
swipeRefreshLayout.setRefreshing(false);
LoadSections load = new LoadSections(numSections);
load.run();
}
public List<Object> getAdapterInfo() {
List<Object> list = new ArrayList<>();
for (int i = 0; i < sections.size(); i++) {
CuratedSection section = sections.get(i);
CuratedSectionHeader header = new CuratedSectionHeader();
header.setHeaderName(section.header);
list.add(header);
list.add(section.stories);
}
return list;
}
}
private class LoadSections implements Runnable {
int numSections;
LoadSections(int numSections) {
this.numSections = numSections;
}
#Override
public void run() {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
final String sessionKey = prefs.getString("session_key", null);
Call<JsonArray> call;
call = TravelersApi.endpoint().getCuratedSections(sessionKey);
call.enqueue(new Callback<JsonArray>() {
#Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawSections = response.body();
if(rawSections.size() == 0) {
//TODO: show placeholder
return;
}
ArrayList<CuratedSection> sections = new ArrayList<>();
for(int i = 0; i < numSections; i++) {
JsonObject jSection = rawSections.get(i).getAsJsonObject();
final CuratedSection section = new CuratedSection();
section.id = jSection.get("id").getAsString();
section.header = jSection.get("section_header").getAsString();
section.topicCount = jSection.get("topic_count").getAsInt();
section.isShown = jSection.get("is_shown").getAsBoolean();
section.stories = new ArrayList<>();
sections.add(section);
}
curated.setSections(sections);
curated.loadStories(sessionKey);
spinner.clearAnimation();
spinner.setVisibility(View.GONE);
header.setVisibility(View.VISIBLE);
swipeRefreshLayout.setVisibility(View.VISIBLE);
}
#Override
public void onFailure(Call<JsonArray> call, Throwable t) {
Log.d("ERROR!", t.toString());
t.printStackTrace();
}
});
}
}
You can achieve this by directly setting scroll listener to your recycler view.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// Call Load more method here to load next page data
// Prevent multiple calls by using a boolean
isLoading=true; // boolean to Prevent multiple calls
}
}
});
OnLoadMoreListener is a interface you need to register it first.
For pagination your server should process the pagination count. So you also need the api to perform on page limit .
Needless to say, I came across a lot of proposed solutions for this particular problem. I removed scrollListener and implemented it inside my fragment, I used notifyItemRangeInserted instead of notifyDataSetChanged. The thing is, none of those solutions did work for me...I'm displaying a set of youtube videos in my app. My dataset is indeed updated; thing is when I scroll downwards, at some point the scroll position moves to the top i.e the very first item of my dataset Here's a snippet of my activity :
private void setUpRecyclerView()
{
mRecyclerView.setHasFixedSize(true);
linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold))
{
loadMorePosts();
loading = true;
}
}
});
initAdapter();
}
private void initAdapter()
{
adapter = new MyAdapter(mDataSet, getActivity());
mRecyclerView.setAdapter(adapter);
skeletonScreen = Skeleton.bind(mRecyclerView)
.adapter(adapter)
.shimmer(true)
.angle(30)
.duration(1200)
.count(10)
.load(R.layout.youtube_item)
.show();
}
private void loadVideos(final OnLoadPlayListCallback videosCallback, String pageToken)
{
Call<FullChannelPlayListData> mcallPlayList = mYouTubeService.getPlayList(
Tags.VALUE_SNIPPET,
20,
mPlayListId,
getActivity().getResources().getString(R.string.youtube_data_api_key),
pageToken
);
mcallPlayList.enqueue(new Callback<FullChannelPlayListData>() {
#Override
public void onResponse(Call<FullChannelPlayListData> call, Response<FullChannelPlayListData> response) {
if (response.code() == 200)
{
Log.d("FullPlayListSuccessful", response.message());
videosCallback.onSuccessfulRequest(response.body());
}
else Log.d("FullPlayListUnsuccess", response.message());
}
#Override
public void onFailure(Call<FullChannelPlayListData> call, Throwable t) {
videosCallback.onFailureRequest(t);
}
});
}
#Override
public void onSuccessfulRequest(FullChannelPlayListData fullChannelPlayListData)
{
Log.d(TAG, "onSuccessfulRequest Item size = "+ fullChannelPlayListData.getItems().size());
if (StringUtils.isNotEmpty(mNextPageToken))
removeFooter();
for (FullPlayListItems mItem: fullChannelPlayListData.getItems())
{
FullPlayListItems randomItem = getVideoFromDataSet(mItem.getSnippet().getResourceId().getVideoId(), mDataSet);
if (randomItem == null) {
mDataSet.add(mItem);
}
}
adapter.notifyItemRangeInserted(adapter.getItemCount(), mDataSet.size());
mRecyclerView.postDelayed(new Runnable() {
#Override
public void run() {
skeletonScreen.hide();
}
}, 2000);
mNextPageToken = fullChannelPlayListData.getNextPageToken();
setLoaded();
}
public void setLoaded() {
loading = false;
}
I could use really use some help...
UPDATE MyAdapter class
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
private List<FullPlayListItems> mDataSet;
private Context mActivity;
private static final int YOUTUBE_ITEM_VIEW_TYPE = 0;
private static final int PROGRESS_BAR_ITEM_VIEW_TYPE = 1;
public MyAdapter(List<FullPlayListItems> mDataSet, Context mActivity) {
this.mDataSet = mDataSet;
this.mActivity = mActivity;
}
public class ProgressViewHolder extends RecyclerView.ViewHolder
{
#BindView(R.id.pBarLoadMorePosts)
ProgressBar pBarLoadMore;
public ProgressViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
#Override
public int getItemViewType(int position) {
return mDataSet.get(position) != null ? YOUTUBE_ITEM_VIEW_TYPE : PROGRESS_BAR_ITEM_VIEW_TYPE;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
RecyclerView.ViewHolder viewHolder;
if (viewType == PROGRESS_BAR_ITEM_VIEW_TYPE)
{
View pBarLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.footer_loader, parent, false);
viewHolder = new ProgressViewHolder(pBarLayoutView);
}
else
{
View postItemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.youtube_item, parent, false);
viewHolder = new YoutubeItemViewHolder(postItemLayoutView);
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
int viewType = getItemViewType(position);
FullPlayListItems mVideoItem = mDataSet.get(position);
switch (viewType)
{
case PROGRESS_BAR_ITEM_VIEW_TYPE:
ProgressViewHolder progressViewHolder = (ProgressViewHolder) holder;
progressViewHolder.pBarLoadMore.getIndeterminateDrawable().setColorFilter(CoreApplication.getRes().getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN);
break;
case YOUTUBE_ITEM_VIEW_TYPE:
default:
YoutubeItemViewHolder youtubeItemViewHolder = (YoutubeItemViewHolder) holder;
youtubeItemViewHolder.mVideoTitle.setText(mVideoItem.getSnippet().getTitle());
youtubeItemViewHolder.sdvYoutubeThumbnail.setImageURI(Uri.parse(mVideoItem.getSnippet().getThumbnails().getHigh().getUrl()));
}
}
#Override
public int getItemCount() {
return mDataSet != null ? mDataSet.size() : 0;
}
public class YoutubeItemViewHolder extends RecyclerView.ViewHolder
{
TextView mVideoTitle;
SimpleDraweeView sdvYoutubeThumbnail;
public YoutubeItemViewHolder(View itemView)
{
super(itemView);
mVideoTitle = (TextView)itemView.findViewById(R.id.tv_video_title);
sdvYoutubeThumbnail = (SimpleDraweeView)itemView.findViewById(R.id.sdv_thumbnail);
}
}
}
And here is my onViewCreated:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPlayListId = getArguments().getString(Tags.YT_CHANNEL_PLAYLIST_ID);
setUpRecyclerView();
// Like I said initially mNextPageToken is empty
loadVideos(this, mNextPageToken);
}
Just notify the data change in the onSuccessfulRequest() method.
adapter.notifyDataSetChanged();
I don't know this answer is helpful to you. anyway please try,
Add this method to your MyAdapter
public void addItem(VideoSectionsActivity item){
mDataSet.add(item);
notifyItemInserted(mDataSet.size() - 1);
}
And change your onSuccessfulRequest()
#Override
public void onSuccessfulRequest(FullChannelPlayListData fullChannelPlayListData)
{
Log.d(TAG, "onSuccessfulRequest Item size = "+ fullChannelPlayListData.getItems().size());
if (StringUtils.isNotEmpty(mNextPageToken))
removeFooter();
for (FullPlayListItems mItem: fullChannelPlayListData.getItems())
{
FullPlayListItems randomItem = getVideoFromDataSet(mItem.getSnippet().getResourceId().getVideoId(), mDataSet);
if (randomItem == null) {
adapter.addItem(mItem);
}
}
mRecyclerView.postDelayed(new Runnable() {
#Override
public void run() {
skeletonScreen.hide();
}
}, 2000);
mNextPageToken = fullChannelPlayListData.getNextPageToken();
setLoaded();
}
Updated
And also rearrange your code in activity
private void setUpRecyclerView()
{
mRecyclerView.setHasFixedSize(true);
linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
initAdapter();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold))
{
loadMorePosts();
loading = true;
}
}
});
}
I have faced a problem.
I am working with recyclerview. I want the list post get updated When users scroll down and when they scroll to the left, the program call the post deletion function. And by scrolling to the right,the program calls updating post function. I wrote codes below. Could you explain with examples how I can do this? I wrote these functions but I failed.
CustomListAdapterForPostOrga
public class CustomListAdapterForPostOrgan extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Posts> post;
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public CustomListAdapterForPostOrgan(Activity activity, List<Posts> post) {
this.activity = activity;
this.post = post;
}
public CustomListAdapterForPostOrgan(List<Posts> post, RecyclerView recyclerView) {
this.post = post;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
///////////////////
#Override
public int getCount() {
return post.size();
}
#Override
public Object getItem(int location) {
return post.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v=convertView;
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.list_row_organ_news
, null);
if (imageLoader == null)
imageLoader = AppController.getInstance().getImageLoader();
NetworkImageView thumbNail = (NetworkImageView) convertView
.findViewById(R.id.organ_news__avatar_show_thumbnail);
TextView titr = (TextView) convertView.findViewById(R.id.txt_organ_news_list_organ);
TextView year = (TextView) convertView.findViewById(R.id.txt_date_posts_organ);
TextView fi = (TextView) convertView.findViewById(R.id.txt_cunter_for_organ_posts);
// getting billionaires data for the row
Posts m = post.get(position);
// thumbnail image
thumbNail.setImageUrl(m.getAvatar(), imageLoader);
// Wealth Source
year.setText("Wealth Source: " + String.valueOf(m.getPublish_at()));
titr.setText(String.valueOf(m.getTitle()));
// release year
year.setText(String.valueOf(m.getPublish_at()));
fi.setText(String.valueOf(m.getView_count()));
return convertView;
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
in main function:
private void handelpost(final List<Post> posts, Page pages) {
if (pages.getCurrentPage() > 1) {
} else {
recycler_post.setLayoutManager(new LinearLayoutManager(this));
final AdapterRcycelerViewPostOrgan
madapter = new
AdapterRcycelerViewPostOrgan(posts);
recycler_post.setAdapter(madapter);
recycler_post.setItemAnimator(new DefaultItemAnimator());
madapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
posts.add(null);
madapter.notifyItemInserted(posts.size() - 1);
handler.postDelayed(new Runnable() {
#Override
public void run() {
// remove progress item
posts.remove(posts.size() - 1);
madapter.notifyItemRemoved(posts.size());
//add items one by one
int start = posts.size();
int end = start + 20;
/* for (int i = start + 1; i <= end; i++) {
studentList.add(new Student("Student " + i, "AndroidStudent" + i + "#gmail.com"));
mAdapter.notifyItemInserted(studentList.size());
}*/
// posts.set()
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
Toast.makeText(getApplicationContext(),"ok",Toast.LENGTH_LONG).show();
}
});
}
I want to know how I can do this function on my own lists. Please help me.
In onScrolled callback you have dx and dy. If dy>0 list is being scrolled down and if dy<0 its being scrolled up. Same with dx, just different axis
What I'm trying to do is to have a recycler view with a button in it. When the layout that has the recycler is initialized it will only have the button and once is clicked an item will be added(the first item) and the button(or imageview, whatever it has a + on it) pushed to the right. I have the recyclerView and it's working(by working I mean that I have some photos that are being displayed in the right order), but I can't get the button to display. I found various links sort of similar to this, such as How to add fixed Button in RecyclerView Adapter?, but I can't figure it out.
Here is the Adapter:
public class SelectPhotoAdapter extends
RecyclerView.Adapter<SelectPhotoHolder> {// Recyclerview will extend to
// recyclerview adapter
private ArrayList<Data_Model> arrayList;
private Context context;
private boolean hasLoadButton = true;
private final int IMAGES = 0;
private final int LOAD_MORE = 1;
public SelectPhotoAdapter (Context context,
ArrayList<Data_Model> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
public boolean isHasLoadButton() {
return hasLoadButton;
}
public void setHasLoadButton(boolean hasLoadButton) {
this.hasLoadButton = hasLoadButton;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if (hasLoadButton) {
return arrayList.size() + 1;
} else {
return arrayList.size();
}
}
#Override
public int getItemViewType(int position) {
if (position < getItemCount()) {
return IMAGES;
} else {
return LOAD_MORE;
}
}
// #Override
// public int getItemCount() {
// return (null != arrayList ? arrayList.size() : 0);
//
// }
#Override
public void onBindViewHolder(SelectPhotoHolder holder, int position) {
SelectPhotoHolder mainHolder = holder;// holder
if(position >= getItemCount()) {
mainHolder.addPhoto.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//dostuff
}
});
} else {
final Data_Model model = arrayList.get(position);
Bitmap image = BitmapFactory.decodeResource(context.getResources(),
model.getImagePath());// This will convert drawbale image into
mainHolder.imageview.setImageBitmap(image);
}
}
#Override
public SelectPhotoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == IMAGES) {
return new SelectPhotoHolder((LayoutInflater.from(parent.getContext()).inflate(R.layout.selectphotolist, parent, false)),IMAGES);
} else if (viewType == LOAD_MORE) {
return new SelectPhotoHolder((LayoutInflater.from(parent.getContext()).inflate(R.layout.addphoto, parent, false)),LOAD_MORE);
} else {
return null;
}
}
}
If I use the getItemCount() which is commented I get the photos, but no button and if I use the uncommented getItemCount() which I took from the link provided it will crash. I also haven't figured out where to use isHasLoadButton() or setHasLoadButton methods. Could anyone point me in the right direction ?
If you need me to post anymore files let me know. Thank you.
The file which uses the recyclerView:
public class SelectPhotoDialogFragment extends DialogFragment {
private RecyclerView mRecyclerView;
private SelectPhotoAdapter adapter;
// this method create view for your Dialog
public static final Integer[] IMAGES= {R.drawable.first,R.drawable.second,R.drawable.third,R.drawable.picc};
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
// request a window without the title
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//inflate layout with recycler view
View v = inflater.inflate(R.layout.select_photo, container, false);
mRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
int recyclerHeight = mRecyclerView.getHeight();
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
ArrayList<Data_Model> arrayList = new ArrayList<>();
for (int i = 0; i < IMAGES.length; i++) {
arrayList.add(new Data_Model(IMAGES[i]));
}
adapter = new SelectPhotoAdapter(getActivity(), arrayList);
mRecyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.notifyDataSetChanged();// Notify the adapter
return v;
}
}
To explain again: The only thing that I'm seeing are the images(first,second,third,picc) and no button after, but they are displayed correctly. What I would like to happen is to have only the button as an item, and when I click on it to have a picture inserted before and it moving to the right(as you can see I have a horizontal orientation). This is what I've been trying to do with 2 ViewTypes, but I haven't figured it out.
The Holder:
public class SelectPhotoHolder extends RecyclerView.ViewHolder {
// View holder for gridview recycler view as we used in listview
private final int IMAGES = 0;
private final int LOAD_MORE = 1;
public ImageView imageview;
public Button addPhoto;
public SelectPhotoHolder(View view,int ViewType) {
super(view);
// Find all views ids
if(ViewType == IMAGES) {
this.imageview = (ImageView) view
.findViewById(R.id.selectPhoto);
}
else
this.addPhoto = (Button) view.findViewById(R.id.load_more);
}
}
new Holder output:
01-31 14:12:47.131 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.141 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.141 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.151 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.231 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
#Override
public int getItemViewType(int position) {
if (position < getItemCount()-1) {
return IMAGES;
} else {
return LOAD_MORE;
}
}
public SelectPhotoHolder(View view,int ViewType) {
super(view);
// Find all views ids
if(ViewType == IMAGES) {
this.imageview = (ImageView) view
.findViewById(R.id.selectPhoto);
Log.e("test-exist", "IMAGES" + (imageview == null));
}else{
this.addPhoto = (Button) view.findViewById(R.id.load_more);
Log.e("test-exist", "LOAD_MORE" + (addPhoto == null));
}
}
#Override
public int getItemCount() {
if (hasLoadButton) {
return arrayList==null? 1 :arrayList.size() + 1;
} else {
return arrayList==null? 0 :arrayList.size();
}
}
if(position >= (arrayList==null? 0 :arrayList.size())) {
try{
mainHolder.addPhoto.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//dostuff
}
});
}catch(Throwable e){
Log.e("test-erorr","current position:"+position);
}
} else {
final Data_Model model = arrayList.get(position);
Bitmap image = BitmapFactory.decodeResource(context.getResources(),
model.getImagePath());// This will convert drawbale image into
mainHolder.imageview.setImageBitmap(image);
}
Here's a sample, that's why i meant in " sample - example ", modify it to your own requirements, This "sample" display Recyclerview items with images, below the recyclerview there's a button, when pressed it adds a new image, you can modify the place - animation, Here's the Item.java :
public class Item {
private int Pic;
public Item(String Title, int Pic, int Color) {
setPic(Pic);
}
public int getPic() {
return Pic;
}
public void setPic(int photo) {
this.Pic= photo;
}
}
Adapter :
public class Adapter extends RecyclerView.Adapter<RecyclerViewHolders>{
private List<Item> itemList;
public Context context;
public Adapter(Context context, List<Item> itemList) {
this.itemList = itemList;
this.context = context;
}
#Override
public RecyclerViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.your_item, null);
return new RecyclerViewHolders(layoutView);
}
#Override
public void onBindViewHolder(RecyclerViewHolders holder, int position) {
holder.Pic.setImageResource(itemList.get(position).getPic());
}
#Override
public int getItemCount() {
return this.itemList.size();
}
}
It's all normal to now, in your MainActivity or where your Adapter initialized
Adapter rcAdapter;
public static List<Item> Items;
public static Button button;
In your onCreate:
initListViews();
Now the initListViews method :
public void initListViews()
{
List<Item> rowListItem = getAllItemList();
LinearLayoutManager yourlayout= new LinearLayoutManager(this);
RecyclerView RcView = (RecyclerView)findViewById(R.id.recycler_view);
button = (Button)findViewById(R.id.button);
RcView.setHasFixedSize(true);
RcView.setLayoutManager(yourlayout);
rcAdapter = new RecyclerViewAdapter(this, rowListItem);
RcView.setAdapter(rcAdapter);
}
private List<Item> getAllItemList(){
Items.add(new Item(YourImageResources));
Items.add(new Item(YourImageResources));
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AllItems.add(new Item(YourImageResources));
}
});
}
If you want a specific number of items to be added :
int PressedTimes = 0;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(PressedTimes < 7)
{
AllItems.add(new Item(YourImageResources));
PressedTimes=+1;
}
if(PressedTimes == 7)
{
button.setVisibility(View.GONE);
}
}
});