Can I display random page when our recycleview apps?
I want to put a floatbutton on my detailactivity.javaclass and when I click it must display a page with my string data and image files.
mFlowerData = new FlowerData("Rose", getString(R.string.description_flower_rose),
R.drawable.rose);
mFlowerList.add(mFlowerData);
mFlowerData = new FlowerData("Carnation", getString(R.string.description_flower_carnation),
R.drawable.carnation);
How can I display it on the last page.
You can use something like this in your activity/fragment
private List<Simple> elements;
private String names[] = {
"Monk's Oartormow",
"Eldoth Nut",
"Eblurst",
"Poison Okule",
"Black Berry",
"Snake Eye",
"Witch's Root",
"White Burm",
"Yellow Pig Claw",
"Uchea Mint"
};
private String contents[] = {
"What if the adaptable injury ate the click?",
"The united chip stuffs into the babyish sad.",
"Did the interesting progress really argue the visual?",
"What if the chief sign ate the schedule?",
"Is the brush respect better than the raise?",
"It was then the inexperienced topic met the open period."
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flower);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
elements = new ArrayList<>();
adapter = new SimpleAdapter();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Simple s = generateRandomElement();
elements.add(0,s);
adapter.updateElements(elements);
}
});
}
private Simple generateRandomElement() {
int posNames = (int) (Math.random() * names.length);
int posContent = (int) (Math.random() * contents.length);
String name = names[posNames];
String content = contents[posContent];
return new Simple(name, content);
}
and for updating the elements in the adapter:
private List<Simple> elements;
public SimpleAdapter() {
this.elements = new ArrayList<>();
}
#Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_simple_element, parent, false);
return new SimpleViewHolder(view);
}
#Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
Simple element = elements.get(position);
holder.title.setText(element.getTitle());
holder.content.setText(element.getContent());
}
#Override
public int getItemCount() {
return elements.size();
}
public void updateElements(List<Simple> elements) {
this.elements.clear();
this.elements.addAll(elements);
notifyDataSetChanged();
}
you can improve how the elements are updated implementing a DiffCallback for your Flower class (link)
Related
I am displaying comments on a post using a recycler adapter. The code is set to scroll the recycler view to the bottom when the edit text is clicked, and when a new comment is posted by the current user.
If the keyboard is shown and the recycler view still does not touches the keyboard (there is like 2 to 4 comments displayed), the app crashes when the comment is posted. If there are many items (enough to go under the keyboard), the recyclerview scrolls and nothing crashes.
This is my code:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// some code
scrollToBottom = true; // this is initially false
loadComments();
}
});
private void loadComments () {
Query query = firebaseFirestore.collection...;
FirestoreRecyclerOptions<Model> options = new FirestoreRecyclerOptions.Builder<Model>()
.setLifecycleOwner(this)
.setQuery(query, Model.class)
.build();
adapter = new Adapter(options, this);
recyclerview.setHasFixedSize(true);
recyclerview.setLayoutManager(new LinearLayoutManager(this));
recyclerview.setAdapter(adapter);
if (scrollToBottom) {
scrollToBottom = false;
scrollToTheBottom();
}
}
private void scrollToTheBottom() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
recyclerview.smoothScrollToPosition(adapter.getItemCount());
}
}, 600);
}
Adapter code:
public class Adapter extends FirestoreRecyclerAdapter<Model, Adapter.ViewHolder> {
Context context;
TimeAgo timeAgo;
public Adapter(#NonNull FirestoreRecyclerOptions<Model> options, Context context) {
super(options);
this.context = context;
}
#Override
protected void onBindViewHolder(#NonNull final ViewHolder holder, int position, #NonNull Model model) {
final String userID = model.getUser_id();
String image = model.getImage();
String username = model.getUsername();
String comment = model.getComment();
Timestamp commentTimeAgo = model.getTimestamp();
String timestampString = String.valueOf(commentTimeAgo);
String[] noOpeningParentheses = timestampString.split("\\(");
String[] noClosingParentheses = noOpeningParentheses[1].split("\\)");
String[] noCommaAndSpace = noClosingParentheses[0].split(", ");
String[] secondsFromTimestamp = noCommaAndSpace[0].split("seconds=");
String[] nanosecondsFromTimestamp = noCommaAndSpace[1].split("nanoseconds=");
long millis = TimeUnit.SECONDS.toMillis(Long.parseLong(secondsFromTimestamp[1])) + TimeUnit.NANOSECONDS.toMillis(Long.parseLong(nanosecondsFromTimestamp[1]));
// Applying
if (image.equals("default")) {
holder.userImage.setImageResource(R.mipmap.no_image);
} else {
Glide.with(context).load(image).into(holder.userImage);
}
holder.userUsername.setText(username);
holder.comment.setText(String.valueOf(comment));
holder.commentTimeAgo.setText(timeAgo.getTimeAgo(context, millis));
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listitem, parent, false);
context = parent.getContext();
timeAgo = new TimeAgo();
return new ViewHolder(view);
}
public class ViewHolder extends RecyclerView.ViewHolder {
CircleImageView userImage;
TextView userUsername, comment, commentTimeAgo;
public ViewHolder(#NonNull View itemView) {
super(itemView);
userImage = itemView.findViewById(R.id.userimage);
userUsername = itemView.findViewById(R.id.userUsername);
comment = itemView.findViewById(R.id.comment);
commentTimeAgo = itemView.findViewById(R.id.timeago);
}
}
}
When viewing the logcat, I get an error on the 3rd line:
String timestampString = String.valueOf(timestamp);
String[] noOpeningParentheses = timestampString.split("\\(");
String[] noClosingParentheses = noOpeningParentheses[1].split("\\)"); // error here
String[] noCommaAndSpace = noClosingParentheses[0].split(", ");
String[] secondsFromTimestamp = noCommaAndSpace[0].split("seconds=");
String[] nanosecondsFromTimestamp = noCommaAndSpace[1].split("nanoseconds=");
long millis = TimeUnit.SECONDS.toMillis(Long.parseLong(secondsFromTimestamp[1])) + TimeUnit.NANOSECONDS.toMillis(Long.parseLong(nanosecondsFromTimestamp[1]));
This is a code that I wrote for converting a firebase firestore timestamp field to milliseconds.
What I'm getting in logcat is something like java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
I do not know how to solve this. Any help please?
Replace:
recyclerview.smoothScrollToPosition(adapter.getItemCount());
with:
int lastIndex = adapter.getItemCount()-1;
if(lastIndex!=-1)
{recyclerview.smoothScrollToPosition(lastIndex);}
I know, this question asked many times but I am totally lost after reading some answers. I am new to Android development. I have created a Fragment and initialized and set the adapter from that fragment
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(ConfigDataloggerViewModel.class);
dataLoggerList = getResources().getStringArray(R.array.DataLoggerStringArray);
// TODO: Use the ViewModel
//for (int j=0; j< dataLoggerList.length; j++){ DummyArrayList.add(dataLoggerList[j]);}
RecyclerView recyclerView = getView().findViewById(R.id.config_datalogger_recycle_view);
ConfigDataloggerAdapter configDataloggerAdapter = new ConfigDataloggerAdapter(dataLoggerList, getActivity());
recyclerView.setAdapter(configDataloggerAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
I have list of EditText in my fragment and I am setting those EditText in the adapter and I am also saving the values from EditTexts into an Array which is defined in Adapter itself.
public class ConfigDataloggerAdapter extends RecyclerView.Adapter<ConfigDataloggerAdapter.ViewHolder>{
//private ArrayList<RFIDReader> readers = new ArrayList<>();
private String [] mDummyList ;
// private ArrayList<String> mDummyArrayList = new ArrayList<>();
public String [] mDummyArrayList;
//public ArrayList<String> mConfigDataloggerData ;
public String[] mConfigDataloggerData;
// private ConfigDataloggerViewModel mConfigDataModel;
public Map<String, String> tempDataModel = new HashMap<>();
private Context mContext;
public ConfigDataloggerAdapter( String [] mDummyArrayList, Context mContext) {
this.mDummyArrayList = mDummyArrayList;
this.mContext = mContext;
mConfigDataloggerData = new String[mDummyArrayList.length];
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.config_datalogger_list,viewGroup,false);
ConfigDataloggerAdapter.ViewHolder holder = new ConfigDataloggerAdapter.ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull ConfigDataloggerAdapter.ViewHolder holder, int i) {
String [] mConfigDataloggerText = null;
// for (int j=0; j< mDummyList.length; j++){ mDummyArrayList.add(mDummyList[j]);}
//ReaderDevice readerDevice = mDummyArrayList.get(i);
String temp = mDummyArrayList[i];
holder.mConfigDataloggerListText.setText(temp);
// tempDataModel.put(temp,mConfigDataloggerData.get(i) );
// mConfigDataModel.setConfigDataloggerVMData(tempDataModel);
//holder.reader_checkedTextView.setText(readerDevice.getName() );
}
#Override
public int getItemCount() {
return mDummyArrayList.length;
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView mConfigDataloggerListText;
public EditText mConfigDataloggarListEditText;
public LinearLayout configDataloggerLayout;
public ViewHolder(#NonNull View itemView) {
super(itemView);
mConfigDataloggerListText = itemView.findViewById(R.id.textView_config_datalogger);
mConfigDataloggarListEditText = itemView.findViewById(R.id.editText_config_datalogger);
mConfigDataloggarListEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mConfigDataloggerData[getAdapterPosition()] =
mConfigDataloggarListEditText.getText().toString();
//here I am storing data from editText to the array
}
#Override
public void afterTextChanged(Editable s) {
}
});
configDataloggerLayout = itemView.findViewById(R.id.config_datalogger_list_layout);
}
}
}
I have two questions, 1) how to access mConfigDataloggerData from adapter in the Fragment? 2) I have a button in same fragment. when I press the button, other fragment starts. Now, I want to save data from mConfigDataloggerData to ViewModel when press the button. So where exactly I write mViewModel = ViewModelProviders.of(this).get(xxxx.class); ?
For your reference, below code is of an activity where my Fragments are attached.
public class defaultActivity extends AppCompatActivity {
private String TAG = "default activity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.datalogger_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ConfigDataloggerFragment configDataloggerFragment = new ConfigDataloggerFragment();
// getSupportFragmentManager().beginTransaction().add(R.id.default_datalogger_activity, datalogger).commit();
getSupportFragmentManager().beginTransaction().add(R.id.default_datalogger_activity, configDataloggerFragment).commit();
}
public void StartorStopInventory(View view) {
Button button = (Button) view;
if (application.mConnectedReader.isConnected()){
if (application.mIsInventoryRunning ){
application.mIsInventoryRunning = true;
button.setText("STOP");
try{
TriggerInfo triggerInfo = new TriggerInfo();
Log.d(TAG, "Start trigger setting when button is pressed" + triggerInfo.StartTrigger.getTriggerType());
Log.d(TAG, "Stop trigger setting when button is pressed" + triggerInfo.StartTrigger.getTriggerType());
application.mConnectedReader.Actions.Inventory.perform();
}catch (InvalidUsageException e){
Log.d(TAG, "StartorStopInventory: Inventory perform fail " + e);
} catch (final OperationFailureException op) {
op.printStackTrace();
Log.d(TAG, "StartorStopInventory: Operational failure " + op.getResults() + " " + op.getVendorMessage());
Toast.makeText(view.getContext(), op.getVendorMessage(), Toast.LENGTH_LONG);
}
}
}
}
public void start_data_logging_click(View view) {
Datalogger datalogger = new Datalogger();
// getSupportFragmentManager().beginTransaction().replace(R.id.default_datalogger_activity, datalogger);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.default_datalogger_activity, datalogger);
transaction.addToBackStack(null);
transaction.commit();
}
}
1) Create getters in Adapter for your fields, ex
public String[] getConfigDataloggerData(){
return mConfigDataloggerData;
}
...
To declare adapter globally;
public class YourFragment {
ConfigDataloggerAdapter configDataloggerAdapter,
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(ConfigDataloggerViewModel.class);
dataLoggerList = getResources().getStringArray(R.array.DataLoggerStringArray);
// TODO: Use the ViewModel
//for (int j=0; j< dataLoggerList.length; j++){ DummyArrayList.add(dataLoggerList[j]);}
RecyclerView recyclerView = getView().findViewById(R.id.config_datalogger_recycle_view);
configDataloggerAdapter = new ConfigDataloggerAdapter(dataLoggerList, getActivity());
recyclerView.setAdapter(configDataloggerAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
}
}
Then inside your Fragment, call configDataloggerAdapter.getConfigDataloggerData() and you will get your data. Same for other fields
2) I dont yet understand that part
I never asked any question before but hope you'll get my point.
I am making a chat app in which I am using a RecyclerView to show messages. The problem is when I scroll the RecyclerView some of the items disappear from the top and the whole items messes up when I try to add a message it doesn't even scroll to bottom nor added in the ListView.
Here is my RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/conversation_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:layout_above="#id/typingConversationLayout"
android:layout_below="#id/topLayout_conversation_activity"
android:layout_marginBottom="-5dp"
android:paddingBottom="7dp" />
Initializing and setting the RecycerView:
linearLayoutManager = new LinearLayoutManager(this);
adapter = new ConversationRecyclerViewAdapter();
conversationRecyclerView.setAdapter(adapter);
conversationRecyclerView.setLayoutManager(linearLayoutManager);
linearLayoutManager.setStackFromEnd(true);
conversationRecyclerView.setHasFixedSize(true);
conversationRecyclerView.setNestedScrollingEnabled(false);
Here is my Adapter class:
private class ConversationRecyclerViewAdapter
extends RecyclerView.Adapter<ConversationRecyclerViewAdapter.ConversationViewHolder> {
#NonNull
#Override
public ConversationViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
Log.d(TAG, "onCreateViewHolder: Users Find started");
View conversationsView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.layout_message_received, parent, false);
return new ConversationViewHolder(conversationsView);
}
#Override
public void onBindViewHolder(#NonNull final ConversationViewHolder holderConversation, int i) {
Log.d(TAG, "onBindViewHolder: Users Find started at position is " + i);
final int position = holderConversation.getAdapterPosition();
if (mOwnUser_1.get(position)) {
holderConversation.receivedMsgLayout.setVisibility(View.GONE);
holderConversation.sentProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.sentMsg.setText(mUserText_3.get(position));
} else {
holderConversation.sentMsgLayout.setVisibility(View.GONE);
holderConversation.receivedProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.receivedMsg.setText(mUserText_3.get(position));
}
Log.d(TAG, "onBindViewHolder: completed at " + position);
}
#Override
public int getItemCount() {
return mOwnUser_1.size();
}
public class ConversationViewHolder extends RecyclerView.ViewHolder {
RelativeLayout receivedMsgLayout, sentMsgLayout;
EmojiTextView receivedMsg, sentMsg;
CircleImageView receivedProfileImg, sentProfileImg;
public ConversationViewHolder(#NonNull View v) {
super(v);
receivedMsgLayout = v.findViewById(R.id.received_message_layout);
sentMsgLayout = v.findViewById(R.id.sent_message_layout);
receivedMsg = v.findViewById(R.id.received_message_text);
sentMsg = v.findViewById(R.id.sent_message_text);
receivedProfileImg = v.findViewById(R.id.received_message_user__profile_image);
sentProfileImg = v.findViewById(R.id.sent_message_user__profile_image);
}
}
}
Here I am adding data to ListView and displaying to the RecyclerView:
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String msg = editText.getText().toString().trim();
if (TextUtils.isEmpty(msg)) {
editText.setError("Please add a message");
editText.requestFocus();
} else {
Log.d(TAG, "onClick: send Btn ADDED TEXT.. ");
mOwnUser_1.add(user);
mUserProfileImg_2.add(image);
mUserText_3.add(message);
editText.setText("");
editText.requestFocus();
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
}
}
});
I don't know what i am doing wrong but it does not seem to work as i wanted.
Update Code:
The three listviews:
private ArrayList<Boolean> mOwnUser_1 = new ArrayList<>();
private ArrayList<Integer> mUserProfileImg_2 = new ArrayList<>();
private ArrayList<String> mUserText_3 = new ArrayList<>();
And the way of adding data to adapter:
mOwnUser_1.add(true);
mUserProfileImg_2.add(R.drawable.boy);
mUserText_3.add(edittext.getText().toString().trim());
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
My Whole Conversation Activity Class:
public class ConversationActivity extends AppCompatActivity {
private static final String TAG = "ConversationActivity";
private EditText editText;
private LinearLayout linearLayout;
private LinearLayoutManager linearLayoutManager;
private ImageView sendBtn;
private ImageView emojiImage;
private View rootView;
private Boolean popUpShown = false;
private Boolean micShown = false;
private ImageView micBtn;
private RelativeLayout micLayout;
private RecyclerView conversationRecyclerView;
// Array Lists for Find USERS
private ArrayList<Boolean> mOwnUser_1 = new ArrayList<>();
private ArrayList<Integer> mUserProfileImg_2 = new ArrayList<>();
private ArrayList<String> mUserText_3 = new ArrayList<>();
private ConversationRecyclerViewAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: started");
super.onCreate(savedInstanceState);
EmojiManager.install(new TwitterEmojiProvider());
setContentView(R.layout.activity_conversation);
editText = findViewById(R.id.conversationEditText);
linearLayout = findViewById(R.id.optionsOther);
emojiImage = findViewById(R.id.emojiIconOther);
rootView = findViewById(R.id.root_view_conversation);
micBtn = findViewById(R.id.microphoneBtn);
micLayout = findViewById(R.id.microphoneLayout);
conversationRecyclerView = findViewById(R.id.conversation_recyclerView);
sendBtn = findViewById(R.id.sendBtnConversation);
if (!(Build.VERSION.SDK_INT >= 21))
findViewById(R.id.typingConversationLayout).setBackgroundResource(R.drawable.edit_text_conversation_background_below_api);
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String msg = editText.getText().toString().trim();
if (TextUtils.isEmpty(msg)) {
editText.setError("Please add a message");
editText.requestFocus();
} else {
Log.d(TAG, "onClick: send Btn ADDED TEXT.. ");
addData(true, R.drawable.boy0, msg);
}
}
});
initConversationArrayList();
}
private void addData(Boolean user, int image, String message) {
mOwnUser_1.add(user);
mUserProfileImg_2.add(image);
mUserText_3.add(message);
editText.setText("");
editText.requestFocus();
adapter.notifyItemInserted(mOwnUser_1.size());
conversationRecyclerView.scrollToPosition(mOwnUser_1.size() - 1);
}
private void initConversationArrayList() {
Log.d(TAG, "initConversationArrayList: created");
mOwnUser_1.add(true);
mUserProfileImg_2.add(R.drawable.boy0);
mUserText_3.add("Hello How are you?");
Log.d(TAG, "initConversationArrayList: completed");
initConversationRecyclerView();
}
private void initConversationRecyclerView() {
Log.d(TAG, "initConversationRecyclerView: started");
linearLayoutManager = new LinearLayoutManager(this);
adapter = new ConversationRecyclerViewAdapter();
conversationRecyclerView.setAdapter(adapter);
conversationRecyclerView.setLayoutManager(linearLayoutManager);
linearLayoutManager.setStackFromEnd(true);
conversationRecyclerView.setHasFixedSize(true);
conversationRecyclerView.setNestedScrollingEnabled(false);
Log.d(TAG, "initConversationRecyclerView: completed");
}
Currently I am also working on chat module, let me show you how am I doing this. I am going to show you in steps.
Step 1: make two separate layout for recyclerview items, one for message that has been sent from your side and one for message received from another side.
Step 2 : make two view holders to populate different layout according to your scenario, made in above step, like this:
public class ChatNewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Chat> chats;
public ChatNewAdapter(List<Chat> chats) {
this.chats = chats;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View viewSend = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message_send, parent, false);
return new ViewHolderSend(viewSend);
} else {
View viewReceive = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message_received, parent, false);
return new ViewHolderReceive(viewReceive);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolderSend viewHolderSend = (ViewHolderSend) holder;
viewHolderSend.messageSend.setText(chats.get(position).getMessage());
break;
case 1:
ViewHolderReceive viewHolderReceive = (ViewHolderReceive) holder;
viewHolderReceive.messageReceived.setText(chats.get(position).getMessage());
break;
}
}
#Override
public int getItemCount() {
return chats.size();
}
#Override
public int getItemViewType(int position) {
if (chats != null && !chats.get(position).fromAdmin) {
return 0;
} else
return 1;
}
class ViewHolderSend extends RecyclerView.ViewHolder {
TextView messageSend;
public ViewHolderSend(View itemView) {
super(itemView);
messageSend = (TextView) itemView.findViewById(R.id.messageSend);
}
}
class ViewHolderReceive extends RecyclerView.ViewHolder {
TextView messageReceived;
public ViewHolderReceive(View itemView) {
super(itemView);
messageReceived = (TextView) itemView.findViewById(R.id.messageReceived);
}
}
public int addMessages(Chat chat) {
chats.add(chat);
notifyDataSetChanged();
return chats.size();
}
Step 3 : now in your activity:
public class Test extends AppCompatActivity {
RecyclerView chatList;
RecyclerView.LayoutManager mLayoutManager;
ChatNewAdapter adapter;
ImageView sendButton;
EditText messageEditText;
boolean keyboardUp = false;
boolean isRunning = false;
ArrayList<Chat> chats;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
isRunning = true;
setUpComponents();
}
public void setUpComponents() {
chatList = (RecyclerView) findViewById(R.id.chat_list);
chatList.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
chatList.setLayoutManager(mLayoutManager);
messageEditText = (EditText) findViewById(R.id.messageText);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
sendButton = (ImageView) findViewById(R.id.send);
adapter = new ChatNewAdapter(chats);
chatList.setAdapter(adapter);
chatList.scrollToPosition(chatList.getAdapter().getItemCount() - 1);
messageEditText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (keyboardShown(messageEditText.getRootView())) {
Log.d("keyboard", "keyboard UP");
if (keyboardUp == false) {
if (chats.size() > 0)
chatList.smoothScrollToPosition(chats.size() + 1);
keyboardUp = true;
}
} else {
Log.d("keyboard", "keyboard Down");
keyboardUp = false;
}
}
});
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String message = messageEditText.getText().toString().trim();
if (!message.equals("")) {
Chat chat = new Chat();
String name = message;
chat.setMessage(name);
messageEditText.setText("");
adapter.addMessages(chat);
chatList.scrollToPosition(chatList.getAdapter().getItemCount() - 1);
} else {
Log.d("sending message Error", "error fetching dates");
}
}
});
}
private boolean keyboardShown(View rootView) {
final int softKeyboardHeight = 100;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
int heightDiff = rootView.getBottom() - r.bottom;
return heightDiff > softKeyboardHeight * dm.density;
}
And this is my model class, ignore #PrimaryKey and #Required annotation it just because I am using Realm for local DB. In your case you wont required these annotation.
public class Chat extends RealmObject {
#PrimaryKey
#Required
public Long id;
public boolean fromAdmin;
#Required
public String message;
public int type;
public boolean isRead;
public boolean isSent;
public Date date;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isFromAdmin() {
return fromAdmin;
}
public void setFromAdmin(boolean fromAdmin) {
this.fromAdmin = fromAdmin;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isRead() {
return isRead;
}
public void setRead(boolean read) {
isRead = read;
}
public boolean isSent() {
return isSent;
}
public void setSent(boolean sent) {
isSent = sent;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
I hope it will be helpful for you, you can ask further if you want to know anything else related to code.
RecyclerView as the name stands recycles the views. When binding data to a view, you need to ensure you set or reset all views that are touched in the adapter. Messups typically occur when there's data that is set only conditionally for some but not all items.
In particular:
if (mOwnUser_1.get(position)) {
holderConversation.receivedMsgLayout.setVisibility(View.GONE);
holderConversation.sentProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.sentMsg.setText(mUserText_3.get(position));
} else {
holderConversation.sentMsgLayout.setVisibility(View.GONE);
holderConversation.receivedProfileImg.setImageResource(mUserProfileImg_2.get(position));
holderConversation.receivedMsg.setText(mUserText_3.get(position));
}
Both of these branches will need to reset the other layout back to visible.
Anyway with this kind of two-layout approach you are likely better off by having them as separate view types in your adapter. See How to create RecyclerView with multiple view type?
I'm building a simple IMDB app and I'm almost done save for one tiny detail. The API(http://www.omdbapi.com/) supplies only 10 movies at a time, and the user can specify which "page" do they want. I would like to retrieve all entries. My code looks something like this:
//This populates the list
private void populateList(String title) {
myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
#Override
public void onResponse(Call<Movies> call, Response<Movies> response) {
movies = response.body().getSearch();
recyclerView.setAdapter(new ItemAdapter(movies));
recyclerView.addOnItemTouchListener(
new ItemClickableListener(getActivity(), new ItemClickableListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
String id = movies.get(position).getImdbID();
showDetails(id, view);
}
}));
}
#Override
public void onFailure(Call<Movies> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
And in my interface:
//For populating the list
#GET("?")
Call<Movies> getSearchResults(#Query("s") String title, #Query("page") int pages);
There is a way to know how many entries there are in total but the query must run at least once to retrieve that info. I tried fixing it with a "do...while" loop and adding each consecutive batch of movies to a list and only then populating the RecyclerView but it just wouldn't work (it would leave the loop without displaying a thing). Maybe I overlooked something and that is the correct answer, but even then - Isn't there a more elegant approach?
I think you need EndlessRecyclerView to retrieve pages ten by ten. with following code:
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mAdapter = new MyAdapter(getActivity(), this);
scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) mRecyclerView.getLayoutManager()) {
#Override
public void onLoadMore(int page) {
callWebservice(page);
}
};
mRecyclerView.addOnScrollListener(scrollListener);
mRecyclerView.setAdapter(mAdapter);
When callWebservice is done add Items to your list:
#Override
public void onResponse(Call<List<ShortVideoModel>> call, Response<List<ShortVideoModel>> response) {
mAdapter.addItems(response.body());
}
I ended up checking out EndlessRecyclerView and it works almost perfectly, but I've run into a few issues so I'm posting the code here. It kept stacking listeners and adapters so I swap them. It also kept scrolling up each time data is inserted so I forced it to stay but it's little jittery.
public class SearchFragment extends Fragment {
final String TAG = "LOG.SearchFragment";
final String baseUrl = "http://www.omdbapi.com/";
Button searchButton;
EditText searchField;
RecyclerView recyclerView;
LinearLayoutManager llm;
String title = "";
int page = 1;
List<Search> movies;
Gson gson;
Retrofit retrofit;
MyAPI myAPI;
ItemClickableListener listener;
EndlessRecyclerOnScrollListener scrollListener;
int firstItem;
float topOffset;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "Starting SearchFragment...");
return inflater.inflate(R.layout.search_fragment, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//Preparing RecyclerView
recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler_view);
llm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(llm);
setOnScrollManager();
//List for the movies
movies = new ArrayList<>();
//UI
searchField = (EditText) getActivity().findViewById(R.id.search_field);
searchButton = (Button) getActivity().findViewById(R.id.search_button);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!searchField.getText().toString().equals("")) {
gson = new GsonBuilder().create();
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
myAPI = retrofit.create(MyAPI.class);
title = searchField.getText().toString();
movies.clear();
page=1;
setOnScrollManager();
fetchMovies(title, page);
}
}
});
}
private void setOnScrollManager() {
if (scrollListener!=null) recyclerView.removeOnScrollListener(scrollListener);
scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) {
//This happens when user scrolls to bottom
#Override
public void onLoadMore(int newPage) {
Log.d(TAG, "OnLoadMore "+newPage);
//Preparing the scroll
firstItem = llm.findFirstVisibleItemPosition();
View firstItemView = llm.findViewByPosition(firstItem);
topOffset = firstItemView.getTop();
//Getting new page
page=newPage;
fetchMovies(title, page);
}
};
recyclerView.addOnScrollListener(scrollListener);
}
//This populates the list
private void fetchMovies(String title, int page) {
Log.d(TAG, "Getting "+title+", page "+page);
myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
#Override
public void onResponse(Call<Movies> call, Response<Movies> response) {
if (movies.size()==0) Toast.makeText(getActivity(), "No movies found", Toast.LENGTH_SHORT).show();
movies.addAll(response.body().getSearch());
//We swap the adatper's content when user scrolls down and loads more data
recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
recyclerView.swapAdapter(new ItemAdapter(movies), true);
//Scrolling
Log.d(TAG, "Scrolling to "+firstItem);
llm.scrollToPositionWithOffset(firstItem, (int) topOffset);
//We avoid stacking up listeners
if (listener!=null) recyclerView.removeOnItemTouchListener(listener);
listener = new ItemClickableListener(getActivity(), new ItemClickableListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
String id = movies.get(position).getImdbID();
showDetails(id, view);
}
});
recyclerView.addOnItemTouchListener(listener);
}
#Override
public void onFailure(Call<Movies> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
//This gets the movie details
private void showDetails(String id, final View view){
myAPI.getDetails(id).enqueue(new Callback<MovieDetails>() {
#Override
public void onResponse(Call<MovieDetails> call, Response<MovieDetails> response) {
showPopup(response.body(), view);
}
#Override
public void onFailure(Call<MovieDetails> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
//This displays the movie details
private void showPopup(MovieDetails details, View anchorView) {
View popupView = getActivity().getLayoutInflater().inflate(R.layout.popup_layout, null);
PopupWindow popupWindow = new PopupWindow(popupView,
RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);
TextView title = (TextView) popupView.findViewById(R.id.movie_detail_title);
TextView year = (TextView) popupView.findViewById(R.id.movie_detail_year);
TextView rating = (TextView) popupView.findViewById(R.id.movie_detail_rating);
TextView director = (TextView) popupView.findViewById(R.id.movie_detail_director);
TextView stars = (TextView) popupView.findViewById(R.id.movie_detail_stars);
TextView desc = (TextView) popupView.findViewById(R.id.movie_detail_desc);
title.setText(details.getTitle());
title.setTextColor(Color.parseColor("#ffffff"));
year.setText(details.getYear());
year.setTextColor(Color.parseColor("#ffffff"));
rating.setText(details.getImdbRating()+"/10");
rating.setTextColor(Color.parseColor("#ffffff"));
director.setText("Dir: "+details.getDirector());
director.setTextColor(Color.parseColor("#ffffff"));
stars.setText("Stars: "+details.getActors());
stars.setTextColor(Color.parseColor("#ffffff"));
desc.setText(details.getPlot());
desc.setTextColor(Color.parseColor("#ffffff"));
UrlValidator urlValidator = new UrlValidator();
if (urlValidator.isValid(details.getPoster())) {
ImageView poster = (ImageView) popupView.findViewById(R.id.movie_detail_poster);
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(details.getPoster(), poster);
}
// If the PopupWindow should be focusable
popupWindow.setFocusable(true);
// If you need the PopupWindow to dismiss when when touched outside
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#CC000000")));
int location[] = new int[2];
// Get the View's(the one that was clicked in the Fragment) location
anchorView.getLocationOnScreen(location);
// Using location, the PopupWindow will be displayed right under anchorView
popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY,
location[0], location[1] + anchorView.getHeight());
}
}
i have been trying to implement the endless scroll feature for my product listing page of a ecommmerce app. The grids show the details of the items and when i scroll to the bottom i need to show a progress bar and then append the new grid of items.
The api call works like this, I need to send a start_row_number and limit, which will send me all the items from the start_row_number to limit. Example: start_row_number = 0 and limit = 10. This will return items from 0 to 10
After that i need to load more items when the user reaches the bottom of the grid, and append it to the gridview. So i will send start_row_number = 10 and limit = 10, this will return items form 10 to 20.
As of now, i can get the items from 0 to 10 but not after that. How can i create the endless scroll feature and make everything such that it doesn't give me error such as 'too much work on the main thread'
Here is my MainActivity:
public class ProductListing extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.product_listing_act);
init();
}
public void productListingApiCall(ProductListingCondtionModel productListingCondtionModel) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(productListingCondtionModel.getBase_url()).setLogLevel(RestAdapter.LogLevel.FULL).build();
final ProductListingApi productListingApi =
restAdapter.create(ProductListingApi.class);
productListingApi.getFeed(productListingCondtionModel.getFile(),
productListingCondtionModel.getOperation_condition(),
productListingCondtionModel.getSearch_string_condition(),
productListingCondtionModel.getMinprice_condition(),
productListingCondtionModel.getMaxprice_condition(),
productListingCondtionModel.getMincusratings_condition(),
productListingCondtionModel.getMaxcusratings_condition(),
productListingCondtionModel.getDiscount_condition(),
productListingCondtionModel.getCatids_condition(),
productListingCondtionModel.getBrands_condition(),
productListingCondtionModel.getAffids_condition(),
productListingCondtionModel.getStart_row_condition(),
productListingCondtionModel.getLimit(),
productListingCondtionModel.getOrderby_condition(),
productListingCondtionModel.getSortby_condition(), new Callback<ProductListingPojo>() {
#Override
public void success(ProductListingPojo productListingPojo, Response response) {
final ProductListingPojo product = productListingPojo;
new Thread(new Runnable() {
#Override
public void run() {
String[] t = Arrays.copyOf(product.getTitle(),
product.getTitle().length);
int[] p = Arrays.copyOf(product.getSellingprice(),
product.getSellingprice().length);
int[] m = Arrays.copyOf(product.getMrp(),
product.getMrp().length);
int[] d = Arrays.copyOf(product.getDiscountpercent(),
product.getDiscountpercent().length);
String[] i = Arrays.copyOf(product.getProductimageSmall1(),
product.getProductimageSmall1().length);
for(int j = 0; j < t.length; j++) {
CategoryAllApi categoryAllApi = new CategoryAllApi();
categoryAllApi.setTitle(t[j]);
categoryAllApi.setPrice(p[j]);
categoryAllApi.setMrp(m[j]);
categoryAllApi.setDiscount(d[j]);
categoryAllApi.setImage(i[j]);
arrayList.add(categoryAllApi);
}
}
}).run();
setAdapter();
}
#Override
public void failure(RetrofitError error) {
tv_title_header.setText(error.getMessage());
Log.e("error", error.getMessage());
}
});
}
void setAdapter() {
adapter = new ProductListingGridAdapter(this, arrayList);
gv_product_listing_act.setAdapter(adapter);
}
}
Heres the Adapter:
public class ProductListingGridAdapter extends BaseAdapter {
public ProductListingGridAdapter(ProductListing productListing, ArrayList<CategoryAllApi> arrayList) {
this.arrayList= arrayList;
context = productListing;
inflater = ( LayoutInflater )context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
public class Holder
{
ImageView im_pic;
TextView tv_title, tv_price, tv_mrp, tv_discount;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
GridView grid = (GridView) parent;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
grid.setColumnWidth(width);
grid.setNumColumns(2);
int size = grid.getRequestedColumnWidth() / 2 ;
Double d = new Double(size * 2);
int h = d.intValue();
Holder holder = new Holder();
View rowView;
int index = grid.getFirstVisiblePosition();
View v = grid.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - grid.getPaddingTop());
grid.setSelectionFromTop(index, top);
rowView = inflater.inflate(R.layout.product_listing_gv_items_lay, null);
rowView.setLayoutParams(new GridView.LayoutParams(size, h));
holder.im_pic = (ImageView) rowView.findViewById(R.id.im_product_listing_gv_items_lay_pic);
holder.tv_title = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_title);
holder.tv_price = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_price);
holder.tv_mrp = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_mrp);
holder.tv_discount = (TextView) rowView.findViewById(R.id.tv_product_listing_gv_items_lay_discount);
holder.tv_title.setTypeface(EasyFonts.robotoMedium(rowView.getContext()));
holder.tv_price.setTypeface(EasyFonts.robotoBlack(rowView.getContext()));
holder.tv_mrp.setTypeface(EasyFonts.robotoLight(rowView.getContext()));
holder.tv_mrp.setPaintFlags(holder.tv_mrp.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.tv_discount.setTypeface(EasyFonts.robotoLight(rowView.getContext()));
categoryAllApi = arrayList.get(position);
Ion.with(holder.im_pic).load(categoryAllApi.getImage());
holder.tv_title.setText(categoryAllApi.getTitle());
holder.tv_price.setText("Rs. " + categoryAllApi.getPrice());
holder.tv_mrp.setText("Rs. " + categoryAllApi.getMrp());
holder.tv_discount.setText("" + categoryAllApi.getDiscount() + "%");
rowView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, ProductDetails.class);
Bundle bundle = new Bundle();
bundle.putString("operation", "");
bundle.putString("productkey", "");
intent.putExtras(bundle);
context.startActivity(intent);
}
});
return rowView;
}
}
Heres the CategoryApiCall.java:
public class CategoryAllApi {
private String title, image;
private int price, mrp, discount;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getMrp() {
return mrp;
}
public void setMrp(int mrp) {
this.mrp = mrp;
}
public int getDiscount() {
return discount;
}
public void setDiscount(int discount) {
this.discount = discount;
}
}
I see you have used a GridView.
RecyclerView has been introduced by Google and overcomes flaws of listview and Gridview which filled the ram with junk and made app clunky.
Use RecyclerView with GridLayoutManager. Also, research endless scroll in RecyclerView (which is pretty easy to implement).
I strongly recommend this for your app, since I have tried the same and result is outstanding. App is faster, ram becomes light, and scrolling is great. Also there are many features like the recyclerView object will offer. About time Google took care of such things and raise app quality.
Process may look like a task but in long run it will help you and the app users a lot.