I'm trying to load image and name from Firebase database to my app in RecyclerView but when I run it it is not showing the image and name with no error, here is my code.
//Load menu
recycler_menu=(RecyclerView)findViewById(R.id.recycler_menu);
recycler_menu.setHasFixedSize(true);
layoutManager= new LinearLayoutManager(this);
recycler_menu.setLayoutManager(layoutManager);
loadMenu();
}
private void loadMenu(){
FirebaseRecyclerOptions<Category> options = new FirebaseRecyclerOptions.Builder<Category>().setQuery(category, Category.class).build();
FirebaseRecyclerAdapter<Category, MenuViewHolder> adapter= new FirebaseRecyclerAdapter<Category, MenuViewHolder>(options){
#Override
public MenuViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
MenuViewHolder viewHolder = new MenuViewHolder(view);
return viewHolder;
}
#Override
protected void onBindViewHolder(MenuViewHolder holder, int position, Category model) {
holder.txtMenuName.setText(model.getName());
Picasso.with(getBaseContext()).load(model.getImage()).into(holder.imageView);
}
};
recycler_menu.setAdapter(adapter);
}
My code is running fine but image and name is not showing, blank activity is displaying. Please help.
Just add adapter.startListening() in your OnStart to start the data listener and adapter.stopListening() in your onStop to stop it by this way :
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
This is one of the main problems you usually face when you are using Firebase as your database. I could not see where you retrieve your data but the problem is, most probably, Firebase event listeners working asynchronous. In another way, your method returns before you retrieve the data. In my project, I solved this issue by retrieving data on onCreateView or onCreate and then use those values inside other methods. If it is not the case I just simply do the work inside the event listener, if I have to (It will make your code look extremely complicated and unreadable). It would be more helpful if you do more research about Firebase and its asynchronous working style.
If you somehow find a better way to solve this, please comment below so I can use it too. I am still seeking a better way.
Related
I am trying to make a text change when a button located along with the text (layoutPasswd) in recycler view and to change it back if the button is again pressed.Like a password hiding button. The values to the adapter is from a static class object as arraylist. The problem occurring now is that the value for all the items (only for layoutPasswd) in recycler view is same.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
holder.layoutUName.setText(users.get(position).getUserName());
pos = position;
holder.layoutPasswd.setText("********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (holder.view1) {
holder.layoutPasswd.setText(users.get(pos).getPasswd());
holder.btnViewChanger.setText("hide");
holder.view1 = false;
} else {
holder.layoutPasswd.setText("********");;
holder.btnViewChanger.setText("Show");
holder.view1 = true;
}
}
});
You cannot rely on the ViewHolders or Views in a RecyclerView to hold any state, because they are recycled. Every time a view scrolls onto the screen, first it calls your onBindViewHolder function to update the contents of that ViewHolder to match the data.
Any configuration you set on the views or the ViewHolder instance in onBindViewHolder cannot be relied on to stay the same if the view scrolls off the screen, because the original ViewHolder might be recycled to be used for some other data, and when it scrolls back on screen, you might be looking at some other view that has been recycled from other data that just scrolled off the screen.
So if your views have configuration that you want to "stick", you have to back it up when you change it, and restore it in onBindViewHolder. The way you accomplish this will depend on how you are managing the data that you pass to the adapter.
If you can modify your User class, you can add a Boolean to it that stores whether it should show the password. Then in your onBindViewHolder, you restore the state based on this Boolean. And you also update this Boolean when the state changes.
I also updated the way the click listener works to simplify it for toggling. I removed the pos = position line, because almost certainly that is not something you should be doing.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
final User user = users.get(position)
holder.layoutUName.setText(user.getUserName());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
user.setShowPassword(!user.isShowPassword());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setText(user.isShowPassword() ? "hide" : "show");
}
});
// ...
}
If you cannot modify the User class, this is more complicated. Then the adapter should have its own ArrayList<Boolean> to store the state by position index, but you need to keep this list at least as long as the data that is bound, and reset everything to false if the whole list of data is refreshed.
I am trying to make carousel with clickable images, made as a list. I want to make so using SetOnItemCLickListener, but when I try to rebuild the project an error pops up and tells me:
error: cannot find symbol method setOnItemClickListener(<anonymous OnItemClickListener>)
I searched the internet for a solution but I am still stuck.
I have tried so far:
Clean the project (obvious);
Searched for name illegal elements, there are none;
When I tried only with SetOnCLickListener it did not work; it is in red-colored letters and there is no method like this.
public class FinalSadMovies extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_final_sad_movies);
List<CarouselPicker.PickerItem> imageItems = new ArrayList<>();
imageItems.add(new CarouselPicker.DrawableItem(R.drawable.joker));
imageItems.add(new CarouselPicker.DrawableItem(R.drawable.starwars_resized));
imageItems.add(new CarouselPicker.DrawableItem(R.drawable.test));
CarouselPicker.CarouselViewAdapter imageAdapter = new CarouselPicker.CarouselViewAdapter(this, imageItems, 0);
carouselPicker.setAdapter(imageAdapter);
carouselPicker.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("MainActivity", "ListView item clicked.");
}
});
If you're using carouselPicker from GoodieBag then setOnItemClickListener is not supported.
The only thing you can rely on is the addOnPageChangeListener but this requires you to change your app UI and flow (because you need to use a button to confirm the selection).
Another option is to create a custom CarouselViewAdapter class where you can handle the click event on the instantiateItem method.
I am making a chat app on Android that uses google firebase to store messages that users write to each other. To display these messages to the users I read them from the database and organize them into a custom ListView with a ListAdapter. This was working fine until I updated my dependencies, specifically firebase ui to:
com.firebaseui:firebase-ui:3.1.0
Now the code to construct the list adapter does not work, being:
adapter = new FirebaseListAdapter<ChatMessage>(FirebaseDatabase.getInstance().getReference("Lobbies").child(leaderID).child("Messages"), ChatMessage.class, R.layout.message, this) {
#Override
protected void populateView(View v, ChatMessage model, int position) {
// Get references to the views of message.xml
TextView messageText = (TextView)v.findViewById(R.id.message_text);
TextView messageUser = (TextView)v.findViewById(R.id.message_user);
TextView messageTime = (TextView)v.findViewById(R.id.message_time);
// Set their text
messageText.setText(model.getMessageText());
messageUser.setText(model.getMessageUser());
// Format the date before showing it
messageTime.setText(DateFormat.format("dd-MM-yyyy (HH:mm:ss)",
model.getMessageTime()));
}
};
To fix this issue, I updated the code to conform to the new firebase ui requirements, making the code become:
FirebaseListOptions<ChatMessage> options = new FirebaseListOptions.Builder<ChatMessage>()
.setQuery(FirebaseDatabase.getInstance().getReference("Lobbies").child(leaderID).child("Messages"), ChatMessage.class).setLayout(R.layout.message).build();
adapter = new FirebaseListAdapter<ChatMessage>(options) {
#Override
protected void populateView(View v, ChatMessage model, int position) {
// Get references to the views of message.xml
TextView messageText = v.findViewById(R.id.message_text);
TextView messageUser = v.findViewById(R.id.message_user);
TextView messageTime = v.findViewById(R.id.message_time);
// Set their text
messageText.setText(model.getMessageText());
messageUser.setText(model.getMessageUser());
// Format the date before showing it
messageTime.setText(DateFormat.format("dd-MM-yyyy (HH:mm:ss)",
model.getMessageTime()));
}
};
This code compiles fine now, but the listview is not displaying the data. Is there a specific right way to use the new firebase ui dependency to make the list adapter?
To let the FirebaseRecyclerAdapter and FirebaseListAdapter show the data on the activity
You need to use this:
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
Since FirebaseListAdapter uses a listener to check for changes in the firebase database, then to being listening for data you need to add adapter.startListening() inside the onStart() to be able to show the data in the listview.
Then inside onStop() (when activity is stopped), you can use adapter.stopListening() to remove the listener and the data in the adapter.
Check this for more info: Adapter LifeCycle
Note:
If after using the above, you get a nullpointexception or cannot resolve symbol, you have to declare adapter as global variable and please check the below answer: Error in startListening()
In addition to what's been said in Peter's answer, according to FirebaseRecyclerAdapater latest api documentation, you can create a FirebaseRecyclerAdapter passing in a FirebaseRecyclerOptions instance which is created by a builder. In the builder you specify a lifecycle owner so you don't have to modify either its onStart or its onStop manually:
private fun MainActivity.setUpFirebaseRecyclerAdapter():
FirebaseRecyclerAdapter<User, ListOnlineViewHolder> {
val options = FirebaseRecyclerOptions.Builder<User>()
.setQuery(ONLINE_USERS.limitToLast(10), User::class.java)
.setLifecycleOwner(this)
.build()
return object : FirebaseRecyclerAdapter<User, ListOnlineViewHolder>(options){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListOnlineViewHolder {
return ListOnlineViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.user_layout, parent, false))
}
override fun onBindViewHolder(holder: ListOnlineViewHolder, position: Int, model: User) {
holder.bindMessage(model)
}
}
}
The options builder is made up of a setQuery method which takes in a reference to the db and a model object; the setLifecycleOwner which takes in the activity that will trigger the adapter updates.
I've currently got an activity that creates a view. this view uses other classes (such as one to create a random sequence of integers). I need to run a method (which will display the sequence using bitmaps) once the view is created. So once the user clicks "Start Game" this sequence will be displayed.
I've tried calling the method after setting the content view inside the onCreate method by the sequence is not generated (all 0's) correctly. I've tries this also with onStart and onFinishInflate inside the myView class.
Is there a way i can run this method after everything is inflated and initialized? So after the user clicks "Start Game" and the view is changed, the method needs to run.
Thanks for looking.
Edit: A failed attempt.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.gameView = new GameView(getApplicationContext(), getCellSource(getApplicationContext()));
setContentView(this.gameView);
// this.gameView.displaySequence(this.gameView.gameEngine.getGenSequence()); Need this to run once view is displayed.
}
Try using ViewTreeObserver as follow:
final View yourView = View.inflate(....);
ViewTreeObserver observer = yourView .getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Do what you need with yourView here...
}
});
Notice that the function removeGlobalOnLayoutListener(this) is different in some sdk versions.
I use an BaseAdapter to display a list of objects. This objects are fetched from a server. The getView() method of the BaseAdapter is the following:
/* (non-Javadoc)
* #see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout content = (LinearLayout) View.inflate(lexs, R.layout.favorite_item, null);
LinearLayout paragraphView = new LinearLayout(lexs);
paragraphView.setBackgroundColor(Color.RED);
paragraphView.setGravity(Gravity.CENTER_VERTICAL);
paragraphView.setOrientation(LinearLayout.HORIZONTAL);
ImageView img = new ImageView(lexs);
img.setImageResource(R.drawable.down);
paragraphView.addView(img);
img.setPadding(0, 0, 5, 0);
img.setOnClickListener(new WorkspaceOnClickListener(position));
TextView text = new TextView(lexs);
text.setTextColor(Color.WHITE);
text.setText(favorites.get(position).getParentChapter().getBook().getName() + ": §" + favorites.get(position).getName());
text.setOnClickListener(new ParagraphOnClickListener(position));
DisplayMetrics metrics = new DisplayMetrics();
lexs.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int maxWidth = metrics.widthPixels - 100;
text.setMaxWidth(maxWidth);
text.setMinWidth(maxWidth);
paragraphView.addView(text);
ImageView imgClose = new ImageView(lexs);
imgClose.setMinimumHeight(30);
imgClose.setMinimumWidth(30);
imgClose.setImageResource(R.drawable.close);
imgClose.setPadding(5, 0, 0, 0);
paragraphView.addView(imgClose);
imgClose.setOnClickListener(new CloseListener(position));
content.addView(paragraphView);
content.setPadding(0, 0, 0, 10);
if (favorites.get(position).isExpanded()) {
LinearLayout textLayer = new LinearLayout(lexs);
textLayer.setBackgroundColor(Color.rgb(214, 214, 214));
LinearLayout left = new LinearLayout(lexs);
left.setOrientation(LinearLayout.VERTICAL);
ImageView moveUp = new ImageView(lexs);
moveUp.setImageResource(R.drawable.move_up);
moveUp.setOnClickListener(new MoveListener(UP, position));
moveUp.setPadding(0, 0, 0, 10);
left.addView(moveUp);
ImageView moveDown = new ImageView(lexs);
moveDown.setImageResource(R.drawable.move_down);
moveDown.setOnClickListener(new MoveListener(DOWN, position));
left.addView(moveDown);
TextView paragraphText = new TextView(lexs);
paragraphText.setText(favorites.get(position).getText());
paragraphText.setTextColor(Color.BLACK);
LinearLayout right = new LinearLayout(lexs);
right.setOrientation(LinearLayout.HORIZONTAL);
right.addView(paragraphText);
textLayer.addView(left);
textLayer.addView(right);
content.addView(textLayer);
img.setImageResource(R.drawable.up);
}
return content;
}
}
So I'd like that this whole method is called in background and during the method is executed, a ProgresDialog is shown. I defined the ProgressDialog the following way:
public class LoadingInformation {
private static ProgressDialog progressDialog;
public static void showProgressInformation(Context view) {
progressDialog = ProgressDialog.show(view, "Please wait...", "Doing Extreme Calculations...", true);
System.out.println("Start Loading Screen");
}
public static void stopShowingProgressInformation() {
Handler handler=new Handler();
handler.post(new Runnable(){public void run(){progressDialog.dismiss();}});
System.out.println("Stop Loading Screen");
}
}
But the problem is, i don't know how to implement the calls correctly. I tried to replace all lines:
adapter.notifyDataSetChanged();
With the following code:
public void updateFavoriteList() {
LoadingInformation.showProgressInformation(lexs);
new Thread() {
public void run() {
lexs.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
LoadingInformation.stopShowingProgressInformation();
}
});
}
}.start();
}
But unfortunately this doesn't work as expected. Has anybody a hint how to do it betteR?
The getView() method of the adapter isn’t responsible for downloading the content in the list, all it does is build the current row being displayed in the ListView. getView() doesn’t have anything to do with actually downloading the data from a network resource, which is where you wan’t the ProgressDialog.
When you instantiate the adapter (not shown in your code) you pass the data you want your BaseAdapter to populate the ListView with. Either the data has already been downloaded and simply handed off to the adapter, or the adapter itself does in the constructor.
If you wan’t the dataset for your ListView to change, you’re going to have to create some setter method or have a method in the adapter that refreshes the dataset from the network automatically. In any case though, this is where you want the ProgressDialog to show, not while the getView() method is setting up the ListView row.
P.S. One other suggestion I'd give is to make use of the convertView param passed to getView(). It'll improve the performance of your list by a lot if you have a large list. Check out Romain Guy's presentation about ListView in either the Google I/O 2009 or 2010 talks.
EDIT:
So “favorties” is an array (or List, etc.) of data for your ListView. You most likely pass the “favorites” data to the adapter via a constructor argument. At that point the data already exists in the adapter, or you’re passing a network location so the adapter can fetch the data and display it in the ListView.
For simplicity sake, lets say you download some string data into a String array, before passing it to the adapter. Each element contains a string to be displayed in your ListView via a TextView. You pass that array to the adapter and it handles formatting the ListView for you with the logic in the getView() method.
Now while you are downloading the data to populate the ListView with, you want to show the ProgressDialog to let the user know something is happening. Like some people have already said, you would use an AsyncTask to accomplish this. AsyncTask will allow you to display a ProgressDialog via onPreExecute() while you actually download the data in doInBackground() and get rid of the dialog in onPostExecute().
I hope that makes sense.
The time consuming tasks should be handled with an AsyncTask.
Read https://developer.android.com/resources/articles/painless-threading.html
It has been added to the Android framework to help you do these time consuming things and update progress dialogs without having to code the boilerplate of Tasks and Handlers yourself.