I am trying to populate my Firebase's recycler view with image url. But its not working. Few images are changing on their own. Few aren't displaying. I don't know. May be the image link is not right. Image link are like following:
github.com/GDGVIT/gdgvit.github.io/blob/new-website/members/Shiv.jpg?raw=true,
https://scontent.fdel1-2.fna.fbcdn.net/v/t1.0-9/941872_1155868307766654_5025113179683551097_n.jpg?oh=f83c14f2bc3036ca9be144a08c35b58c&oe=58F02E42,
http://drive.google.com/uc?export=view&id=0BzezVIpuqaxqS3ZzNW56bDJTN0E
Do the size of image in the url matter?
public void onStart()
{
super.onStart();
View view=getView();
mRef=new Firebase("https://gdg-vit-vellore-af543.firebaseio.com/managementmembers");
mRecyclerView = (RecyclerView)view.findViewById(R.id.recycler_view_management_member);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
custom_font = Typeface.createFromAsset(getActivity().getAssets(),"fonts/Montserrat-Regular.ttf");
FirebaseRecyclerAdapter<MemberModel,ManagementMemberFragment.MembersViewHolder> adapter=new FirebaseRecyclerAdapter<MemberModel,ManagementMemberFragment.MembersViewHolder>(
MemberModel.class,
R.layout.card_member,
ManagementMemberFragment.MembersViewHolder.class,
mRef.getRef()
) {
#Override
protected void populateViewHolder(ManagementMemberFragment.MembersViewHolder membersViewHolder, MemberModel memberModel, int i) {
membersViewHolder.name.setTypeface(custom_font);
membersViewHolder.work.setTypeface(custom_font);
membersViewHolder.githubid.setTypeface(custom_font);
membersViewHolder.profile_pic.setImageDrawable(null);
membersViewHolder.name.setText(memberModel.getName());
membersViewHolder.work.setText(memberModel.getWork());
membersViewHolder.githubid.setText(memberModel.getGithubid());
Log.v("From"+"management fragment",memberModel.getProfile_pic());
Glide.with(getActivity()).load(memberModel.getProfile_pic()).thumbnail(0.4f).error(R.drawable.image_not_found).into(ManagementMemberFragment.MembersViewHolder.profile_pic);
}
};
mRecyclerView.setAdapter(adapter);
}
public static class MembersViewHolder extends RecyclerView.ViewHolder{
TextView name,work,githubid;
static CircleImageView profile_pic;
public MembersViewHolder(View v) {
super(v);
name=(TextView)v.findViewById(R.id.member_name);
work=(TextView)v.findViewById(R.id.member_work);
githubid=(TextView)v.findViewById(R.id.member_github_id);
profile_pic=(CircleImageView)v.findViewById(R.id.member_image);
}
}
Please help!!!
profile_pic=(CircleImageView)v.findViewById(R.id.member_image);
make sure "member_image" image is of the right xml file. I had same issue i was findingViewId of wrong xml. That was causing this issue.
Hope this helps
Don't use a static member to hold any views in a ViewHolder:
static CircleImageView profile_pic;
Make it an instance member, and refer to it through the ViewHolder reference just like any other view.
EDIT: I've solved this issue, if interested, please take a look at my answer to see how I did it!
I am currently working in Android Studio. I have a ListView that I populate with several items. Within each of these items is an ImageButton that has a "+" as the image. What I want to do is, whenever that image is clicked (not the entire ListView item, just the image), I want that image of "+" to become another image. Any help would be appreciated, as this has been an ongoing issue for a while!
Here is the current code that I attempt to use to achieve this:
final ImageButton movieSeen = (ImageButton convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
}
});
Currently this does update the image that I click correctly, BUT it also updates images that are not yet rendered on the screen, so when I scroll the list view down, other objects are also changed to ic_check_circle_black_24dp.
What I want is pretty straightforward, I just don't know how to achieve it. I just want to click an ImageButton, that's inside an item on a ListView, and have that ImageButton change its image resource.
Here is my custom array adapter as requested!
private class MovieScrollAdapter extends ArrayAdapter<Movie> {//custom array adapter
private Context context;
private List<Movie> movies;
public MovieScrollAdapter(Context context, List<Movie> movies){
super(context, -1, movies);
this.context = context;
this.movies = movies;
if(this.movies.isEmpty()){//if no results were returned after all processing, display a toast letting the user know
Toast.makeText(getApplicationContext(), R.string.no_matches, Toast.LENGTH_SHORT).show();
}
}
#Override
public View getView(final int position, View convertView, ViewGroup parent){
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.movie_layout, parent, false);
}
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(movies.get(position).getTitle());
TextView plot = (TextView) convertView.findViewById(R.id.plot);
plot.setText(movies.get(position).getPlot());
TextView genre = (TextView) convertView.findViewById(R.id.genre);
genre.setText(movies.get(position).getGenre());
TextView metaScore = (TextView) convertView.findViewById(R.id.metascore);
if(movies.get(position).getMetaScore() == -1){//if the metaScore is set to -1, that means movie has not been rated, which by inference means it is not yet released
metaScore.setText(R.string.movie_not_released);
metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9.5f);//smaller text so it fits without breaking anything
metaScore.setTextColor(getColor(R.color.colorAccent));
} else {
metaScore.setText(" " + Integer.valueOf(movies.get(position).getMetaScore()).toString() + " ");//using white space for minor formatting, instead of altering margins each time this is rendered
metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25);
//setting up a "highlighted" background to achieve metacritic square effect
Spannable spanText = Spannable.Factory.getInstance().newSpannable(metaScore.getText());
spanText.setSpan(new BackgroundColorSpan(getColor(R.color.metaScore)), 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
metaScore.setText(spanText);
metaScore.setTextColor(getColor(android.R.color.primary_text_dark));
}
ImageView image = (ImageView) convertView.findViewById(R.id.imageView);
new ImageDownloadTask((ImageView)image).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, movies.get(position).getPosterURL());//because there are several images to load here, we let these threads run parallel
title.setOnClickListener(new View.OnClickListener() {//setting up a simple onClickListener that will open a link leading to more info about the movie in question!
#Override
public void onClick(View v) {
Uri uri = Uri.parse(movies.get(position).getMovieURL());
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
}
});
return convertView;
}
}
The problem is on a ListView, the views are being reused to save memory and avoid creating a lot of views, so when you change a view it keeps the state while it's being reused to show another item.
For example, you have 100 elements, you touch the first element ImageButton and that button is changed. Maybe on the screen there are 5 elements of the list showing, and you changed the first one. But if you scroll to the element number 15 the system is not creating 15 views, is taking the first one you clicked before and is changing the content.
So, you are expecting to have a view with a "+" ImageButton icon but you see another icon, that's because you must keep the view state inside a model object and set the state every time 'getView' is called.
Post your list adapter to see how is implemented.
UPDATE:
Now I see your adapter implementation I suggest you to add an int field inside Movie class to save the resource id you want to show on the ImageButton. Then inside the onClickListener you must set to this field the resource you want to show on the view when its clicked, and call notifyDataSetChanged(). After that you must do inside getView():
movieSeen.setImageResource(movies.get(position).getButtonImageResource());
Use RecyclerView and set the OnItemClickListener on your ImageButton within your view holder.
This already answered question should help.
The adapted code below is coming from this nice tutorial. Using ReciclerView with an adapter like this will solve your concern.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<String> mDataset;
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView txtHeader;
public ViewHolder(View v) {
super(v);
txtHeader = (TextView) v.findViewById(R.id.xxx);
imageView = (ImageView) v.findViewById(R.id.yyy);
}
}
public MyAdapter(ArrayList<String> myDataset) {
mDataset = myDataset;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rowlayout, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final String name = mDataset.get(position);
holder.txtHeader.setText(mDataset.get(position));
holder.imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Do here what you need to change the image content
}
});
holder.itemView.setBackground(....); // Initialize your image content here...
}
//...
}
Here is my suggestion to achieve what you want :
Create An Interface in your adapter :
public interface YourInterface{
void selectedImage(int position,ImageView iamgeView);
}
Create variable interface in your adapter that you just created :
private YourInterface yourInterface;
and make your adapter constructor like this :
public YourAdapterConstructor(YourInterface yourInterface){
this.yourInterface = yourInterface;
}
in your ImageView onClickListener :
final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
yourInterface.selectedImage(position, imageView);
}
});
and then finally in your class activity, Implements YourInterface and change you ImageView there :
#Override
public void selectedImage(final int position,final ImageView imageView) {
//change your image view here
}
I'd like to thank everyone for their support. Unfortunately, with the way my code is written (rather messily and without much regard for what my professors taught me), I was unable to get most of these solutions to work. I did however, find a solution that falls in line with my own framework that I've had going into this. Unfortunately I could not redo my entire adapter method, or implement various interfaces that would cause me to have to rewrite a huge chunk of code for something seemingly trivial.
So, if anyone finds themselves in this situation in the future, here is my solution:
In the Movie class, I add a boolean value that represents my values, along with some getters and setters:
private boolean watchedStatus;
public boolean hasSeen() {return watchedStatus;}
public void toggleWatchedStatus(){
watchedStatus = !watchedStatus;
}
In the getView method, I simply get a reference to the ImageButton, and then based on the boolean value returned by "hasSeen," I set the ImageResource to one of two states:
#Override
public View getView(final int position, View convertView, ViewGroup parent){
ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieSeen);
if(movies.get(position).hasSeen()){
movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
} else {
movieSeen.setImageResource(R.drawable.ic_add_circle_black_24dp);
}
}
Next, I override the OnClickListener, and make it so that whenever the button is clicked, the boolean value in the Movie.java class is toggled. The key here was using the ArrayAdapter's method "notifyDataSetChanged()" This completes the process, and lets the ListView know that it should update itself:
final ImageButton movieSeenForClick = (ImageButton) convertView.findViewById(R.id.movieSeen);
movieSeen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//movieSeenForClick.setImageResource(R.drawable.ic_check_circle_black_24dp);
movies.get(position).toggleWatchedStatus();
System.out.println(movies.get(position).hasSeen() + " ------- position: " + position);
notifyDataSetChanged();
}
});
Thanks again for the time taken to provide information, a lot of it really did steer me int he right direction, I just had to use the information correctly with the way my code was structured.
I'm trying to create a custom view that has a content area and a progress bar so that any inheriting custom view would have it's layout inflated in the content area. That way you could show/hide the progress bar on the child view.
#EViewGroup(R.layout.loading_view)
class LoadingView extends RelativeLayout{
// some code
#ViewById FrameLayout content;
#ViewById ProgressBar pb;
public View getFrameLayout(){
// sub-classes will use this to inject their content
return content;
}
void setIsLoading(boolean l){
// shows\hides pb
}
}
#EViewGroup(R.layout.other_view)
class OtherView extends LoadingView{
#ViewById TextView someTxt;
public OtherView(...){
// inject to getFrameLayout()
}
}
Now this doesn't remotely work. Here is why:
#EViewGroup on OtherView overrides the one on LoadingView so R.layout.loading_view doesn't even gets loaded. it only gets loaded if you specify #EViewGroup(R.layout.loading_view)
if you do specify the same layout , there is no point in having Android Annotations in the first place since it will not inject view in OtherView.
Is there any way at all to make this work ? Any ideas ?
If I have understood you correctly, you could solve overriding the method:
#EViewGroup(R.layout.loading_view)
class LoadingView extends RelativeLayout {
// some code
#ViewById FrameLayout content;
#ViewById ProgressBar pb;
public View getFrameLayout(){
return content;
}
void setIsLoading(boolean l){
// shows\hides pb
}
}
And each child implement the method returning its content:
class OtherView extends LoadingView {
#ViewById TextView someTxt;
#Override
public View getFrameLayout() {
View content = inflate(R.layout.other_view);
return content;
}
}
As you said, If you put #EViewGroup(R.layout.other_view) in child class, AndroidAnnotations overwrite the main layout view.
I am using Android Annotations 3.1 in my project in Android Studio and I am using an Expandable List View.
I am adding a header to my list like this:
View header = getLayoutInflater().inflate(R.layout.list_view_header, null);
txt_user = (TextView) header.findViewById(R.id.txt_user);
txt_user.setText(" ... ");
mExpandableListView.addHeaderView(header);
Question:
How can I use #ViewById to set my TextView to:
- avoid using findViewById
- changing its text
- and then adding that as a header during:
#AfterViews
protected void init() {
....
}
If I do this inside my Activity when using my text view inside init:
#ViewById
TextView txt_user;
It gives null pointer exception.
Why don't you create a custom view like this :
#EViewGroup(R.layout.list_view_header)
public class MyHeader extends LinearLayout {
#ViewById(R.id.txt_user)
protected TextView mTextView;
public void setText(String text) {
mTextView.setText(text);
}
public MyHeader(Context context) {
super(context);
}
}
Then, in your Activity, write this :
#EActivity(R.layout.activity_xxx)
public class MyActivity extends Activity {
private MyHeader mHeader;
#AfterViews
protected void init() {
mHeader = MyHeader_.build(this);
mHeader.setText("...");
mExpandableListView.addHeaderView(mHeader);
}
}
To use a view signed with #ViewGroup within a layout, you need to implement the constructors that take AttributSet in its class.
NOTE: Remember to use the generated class that has a _ (underline) at the end.
She who must be declared in the layout xml.
I have a question about Android.
Assume we have our main xml layout file, and defining there a place holder by using (for example) a FrameLayout. Also assume we have 2 other xml layout files displaying any content.
So what I want to do is inject dynamically and programmtically one of the two layouts into the place holder. I know there exists the concept of Activitis, Fragments, ViewFlipper etc. But I find it comfortable to do things like this:
public class MainActivity extends Activity {
private FrameLayout placeHolder;
private View view1;
private View view2;
private RelativeLayout canvasPlaceHolder;
private PuzzleCanvas canvas;
private TextView infoLabel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// init gui
setContentView(R.layout.activity_main);
// Load layouts from xml
LayoutInflater factory = getLayoutInflater();
view1 = factory.inflate(R.layout.view1, null);
view2 = factory.inflate(R.layout.view2, null);
}
}
with for example a Button on screen that does something like this:
#Override
public void onClick(View v) {
placeHolder.removeView(view1);
placeHolder.addView(view2);
}
For example to show a loadingAnimation (view2) instead of the normal content (view1) and so I can define both views comfortable and independent in xml.
Is the use of LayoutInflater commendable? What about the performance and memory management? What do you think about this? Is that a common way in Android?
Use 'include ' tag on the xml's frame layout to include both your xml's in the main xml. All you have to do is switch their ' VISIBILITY' through java according to ur app logic.
eg: on a listener, set :
public void onClick(View v) {
innerView1.setVisibilty(View.INVISIBLE);
innerView2. setVisibilty(View.VISIBLE);
}
// Load layouts from xml
LayoutInflater factory = getLayoutInflater();
view1 = factory.inflate(R.layout.view1, null);
view2 = factory.inflate(R.layout.view2, null);
Using LayoutInflater is ok, but I suggest not directly do this action in onCreate, if your layout is very complex, it might cause ANR (draw layout over 5 secs). Since these two views only appears after user reaction, I prefer to do with sendEmptyMessage with handler.
onCreate(...){
handler.sendEmptyMessage(1);
}
private Handler handler = new Handler(){
#Override
public void handleMessage(final Message msgs) {
if(msgs.what == 1){
view1 = factory.inflate(R.layout.view1, null);
view2 = factory.inflate(R.layout.view2, null);
}
}
}