I'm developing an android app with a group of colleage friends , we are learing android development as we go so if this question is really easy , we are all sorry .
In one of the sections of our apps , we need to show a Card (that contains data about a user ) and then , beneath de Card , a list (variable in size ) of items all of the same type . Previously , when we needed to do a list we used a recycler view with a custom adapter , and that worked perfectly with a list consisting of just one type of item , but now that we need the recyclerView to host both the Card and the list of items we don't know how to do it .
For now , we have made the Card static and only apply the rcycler view to the list of items beneath the Card , and that means that only the list is scrollable . We need to make the whole screen scrollable .
So , how can we achieve this? My gut tells me that we would need to turn that card at the top into a fragment and then make some changes to the our custom adapter so that it can handle both tipes of fragments (the card and the item of the list that goes beneath the card) . Is recyclerView the right answer to our problems?
Hope you can help us , here is our code so far :
TeacherProfileFragment
public class TeacherProfileFragment extends Fragment implements FragmentsMethods,ItemAdapterListener<Course>{
private TextView teacherName;
private ImageView teacherImage;
private RecyclerView recyclerviewTeacherRatings;
private RecyclerView recyclerViewTeacherCourses;
private RatingListAdapter ratingListAdapter;
private CourseListAdapter courseListAdapter;
private ImageView teacherCardBackground;
public TeacherProfileFragment(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view=inflater.inflate(Constants.FRAGMENT_PROFILE_TEACHER_LAYOUT, container, false);
setUpElements(view);
addListeners();
return view;
}
#Override
public void setUpElements(View view) {
teacherCardBackground= (ImageView) view.findViewById(R.id.teacher_card_background);
Picasso.with(getActivity()).load("https://newevolutiondesigns.com/images/freebies/google-material-design-wallpaper-17.jpg").fit().centerCrop().into(teacherCardBackground);
//Bitmap bitmap = ((BitmapDrawable)teacherCardBackground.getDrawable()).getBitmap();
//Palette p = Palette.from(bitmap).generate();
//teacherCardBackground.setBackgroundColor(p.getVibrantColor(0x0000000));
teacherName=(TextView)view.findViewById(R.id.profile_teacher_mame);
teacherName.setText(Storage.getSingelton().getInfo(this,Storage.KEY_TEACHER_NAME));
teacherImage=(ImageView)view.findViewById(R.id.profile_teacher_image);
Picasso.with(getActivity()).load(Storage.getSingelton().getInfo(this,Storage.KEY_TEACHER_IMAGE)).transform(new CircleTransform()).into(teacherImage);
recyclerViewTeacherCourses=(RecyclerView)view.findViewById(R.id.course_list);
//recyclerviewTeacherRatings=(RecyclerView)view.findViewById(R.id.rating_list);
courseListAdapter=new CourseListAdapter(getActivity(),getData(""));
courseListAdapter.setListener(this);
ratingListAdapter=new RatingListAdapter(getActivity(),getStaticData());
//recyclerviewTeacherRatings.setAdapter(ratingListAdapter);
//recyclerviewTeacherRatings.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerViewTeacherCourses.setAdapter(courseListAdapter);
recyclerViewTeacherCourses.setLayoutManager(new LinearLayoutManager(getActivity()));
}
#Override
public void addListeners() {
}
#Override
public void itemClicked(View view, Course object) {
Storage.getSingelton().storage(object,this);
Redirect.getSingelton().showFragment(this,Constants.TEACHER_CONTAINER,Constants.FRAGMENT_TEACHER_COURSE);
}
public List<Rating> getStaticData(){
List<Rating> ratings=new ArrayList<>();
ratings.add(new Rating((float) 4.0,"tecnica"));
ratings.add(new Rating((float) 2.5,"salud"));
ratings.add(new Rating((float) 1.3,"conmosion"));
ratings.add(new Rating((float) 3.0,"desarrollo"));
return ratings;
}
#Override
public List<Course> getData(String data) {
List<Course> courses=new ArrayList<>();
courses.add(new Course("Matematica ", (float) 4.0,"FISI"));
courses.add(new Course("Fisica", (float) 4.0,"FISI"));
courses.add(new Course("Matematica", (float) 4.0,"FISI"));
courses.add(new Course("Matematica", (float) 4.0,"FISI"));
return courses;
}
}
CourseListAdapter
public class CourseListAdapter extends RecyclerView.Adapter<CourseListAdapter.CourseHolder>{
private List<Course>courses= Collections.emptyList();
private ItemAdapterListener itemAdapterListener;
private LayoutInflater layoutInflater;
public CourseListAdapter(Context context,List<Course> courses){
layoutInflater=LayoutInflater.from(context);
this.courses=courses;
}
#Override
public CourseHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=layoutInflater.inflate(Constants.COURSE_NEW_ITEM,parent,false);
CourseHolder courseHolder=new CourseHolder(view);
return courseHolder;
}
#Override
public void onBindViewHolder(CourseHolder holder, int position) {
Course course=courses.get(position);
holder.setElements(course);
}
#Override
public int getItemCount() {
return courses.size();
}
public void setListener(ItemAdapterListener listener){
this.itemAdapterListener =listener;
}
public class CourseHolder extends RecyclerView.ViewHolder implements ViewHolderSetters<Course>,View.OnClickListener{
private TextView courseName;
private TextView courseFaculty;
private RatingBar courseRating;
private TextView courseMark;
private View vista;
private Course current;
private ImageView initialLetterImage;
public CourseHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
courseName= (TextView) itemView.findViewById(R.id.profile_course_course_name);
courseFaculty=(TextView) itemView.findViewById(R.id.course_faculty);
initialLetterImage=(ImageView) itemView.findViewById(R.id.letterImageBackground);
//courseRating=(RatingBar) itemView.findViewById(R.id.course_rating);
//courseMark=(TextView) itemView.findViewById(R.id.course_mark);
vista=itemView;
}
#Override
public void onClick(View v) {
itemAdapterListener.itemClicked(v,current);
}
#Override
public void setElements(Course elements) {
current=elements;
courseName.setText(elements.getName());
courseFaculty.setText(elements.getFaculty());
ColorGenerator generator = ColorGenerator.MATERIAL;
int color = generator.getColor(courseName.getText().charAt(0));
TextDrawable.IBuilder builder = TextDrawable.builder().round();
TextDrawable textDrawable = builder.build(courseName.getText().toString().charAt(0)+"", color);
initialLetterImage.setImageDrawable(textDrawable);
//courseMark.setText(elements.getRating()+"");
//courseRating.setRating(elements.getRating());
}
}
}
FrangmentTeacherProfile
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_teacher_profile">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view_teacher_profile"
android:layout_width="match_parent"
android:layout_height="280dp"
card_view:cardCornerRadius="0dp"
card_view:cardElevation="5dp"
card_view:cardPreventCornerOverlap="true"
card_view:cardUseCompatPadding="true">
<ImageView
android:id="#+id/teacher_card_background"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginBottom="5dp"
android:id="#+id/profile_teacher_image"
android:layout_centerHorizontal="true"
/>
<!--style="#style/ProfileTeacherItemImage" -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/profile_teacher_mame"
android:layout_below="#id/profile_teacher_image"
android:textColor="#android:color/white"
android:textSize="25dp"
android:text="Frank Escobedo Bailon"
android:gravity="center"
android:maxLines="2"/>
<!--style="#style/ProfileTeacherItemName"-->
<RelativeLayout
android:id="#+id/profile_rating_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="#id/profile_teacher_mame">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/profile_teacher_rating"
android:textColor="#android:color/white"
android:textSize="20dp"
android:text="4.5"
android:gravity="center"
android:maxLines="1"
android:layout_centerHorizontal="true"/>
<ImageView
android:id="#+id/teacher_profile_star_drawable"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_toRightOf="#id/profile_teacher_rating"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:src="#mipmap/ic_grade_white_24dp"/>
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
<TextView
android:id="#+id/profile_courses_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/card_view_teacher_profile"
android:layout_margin="5dp"
android:textSize="15dp"
android:text="Cursos Que Dicta"/>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="#color/eventDetailDividerColor"
android:layout_toRightOf="#id/profile_courses_label"
android:layout_below="#id/card_view_teacher_profile"
android:layout_marginTop="15dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/course_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/profile_courses_label"
style="#style/ProfileTeacherItemCourse">
</android.support.v7.widget.RecyclerView>
Before the recycler view (e.g. list view), there was an easy way to achieve this:
Just add the card view as header to the list.
With a recycler view you can't simply call addHeaderView(), but you can declare two view types inside your adapter for the recycler view.
With this link you should be able to do this :
Is there an addHeaderView equivalent for RecyclerView?
This way, you just need two layout files (one for the card view and one for the list items) and then you can inflate both layouts inside your adapter.
If you have further questions just ask!
Related
I am working on a simple project and in which I am using RecyclerView to show a grid of some ImageView and TextView using GridLayoutManager in my fragment. I followed some tutorials on Google and made it. But I don't know why there is automatically so much padding between vertical views as if only 2 adjacent views are only showing up. Here is a GIF of it
This is my fragment_home.xml file in which the recylerview is to be shown:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/gridrecyle
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="220dp"
android:gravity="center"
android:numColumns="2"
android:columnWidth="180dp"/>
This is my single_item.xml file for each view:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imagessssss"
android:layout_width="180dp"
android:layout_margin="10dp"
android:layout_marginBottom="30dp"
android:background="#drawable/grid_image"
android:layout_height="180dp" />
<TextView
android:id="#+id/texttsss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:maxLines="1"
android:textSize="25sp" />
</LinearLayout>
This is my fragment_home java class :
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View vii = inflater.inflate(R.layout.fragment_home, null);
context = getActivity();
RecyclerView recyclerView = vii.findViewById(R.id.gridrecyle);
CustomRecycleAdaptor adaptor = new CustomRecycleAdaptor(context);
recyclerView.setAdapter(adaptor);
recyclerView.setLayoutManager(new GridLayoutManager(context , 2 , GridLayoutManager.VERTICAL , false ));
This is my CustomRecycleAdaptor java class for the adaptor :
public class CustomRecycleAdaptor extends RecyclerView.Adapter <CustomRecycleAdaptor.Viewholder>{
private final Context mcontext;
public CustomRecycleAdaptor(Context mcontext) {
this.mcontext = mcontext;
}
//This is a method for giving padding to imageviews with converting dp to pixels
public static float convertDpToPixel(float dp, Context context){
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
public Integer[] mimg = {
R.drawable.cake, R.drawable.donut, R.drawable.ice_creame, R.drawable.smoothie
};
public String[] mstrng = {
"Cakes" , "Donuts" , "Ice Creams" , "Smoothies"
};
#NonNull
#Override
public CustomRecycleAdaptor.Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(mcontext);
View vii = layoutInflater.inflate(R.layout.single_item , parent , false);
Viewholder viewHolder = new Viewholder(vii);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull CustomRecycleAdaptor.Viewholder holder, int position) {
TextView textView = holder.texts;
ImageView imageView = holder.images;
textView.setText(mstrng[position]);
imageView.setImageResource(mimg[position]);
int imgpadding = Math.round(convertDpToPixel(10 , mcontext));
int txtpadding = Math.round(convertDpToPixel(40 , mcontext));
imageView.setPadding(imgpadding , imgpadding , imgpadding , imgpadding);
textView.setPadding(txtpadding , 0 , 0 , 0);
}
#Override
public int getItemCount() {
return mstrng.length;
}
public class Viewholder extends RecyclerView.ViewHolder {
public TextView texts;
public ImageView images;
public Viewholder(#NonNull View itemView) {
super(itemView);
texts = itemView.findViewById(R.id.texttsss);
images = itemView.findViewById(R.id.imagessssss);
}
}}
Kindly Assist me , Thanks in advance!
Update: Uploaded full single_actvity.xml
Could you please try the following code in your recyclerview XML
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
Change the height to wrap content and add the second the line. This way the recyclerview should have the height same as its content but never exceeds the guideline/contraint.
The answer is as #i_A_mok said , the linear layout's height in single_item.xml should be set to "wrap content" in order to just consider the single view not the full screen (of match_parent). Thank You!
I'm trying to implement a 2 expandable recyclerview with 2 headings.So my view has
1.
2.
3.
4.
Since my recyclerview is expandable, and when I expand the 1st recyclerview , it goes inside my 2nd textview.
ie.there is a separate scroll for my recycler view.
I want the 2nd textview and recyclerview to move down when expanding the 1st recyclerview.
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginBottom="10dp"
tools:context=".ListActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="22sp"
android:text="Weightage: 40%"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fafafa" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="22sp"
android:layout_marginBottom="10dp"
android:text="Weightage: 60%"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recview2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fafafa" />
</LinearLayout>
</ScrollView>
the same happens with the second recyclerview. On expanding the second recycler view, recyclerview items have a separate scroll and the text above stays at a place.The overall scroll doesn't work.
You can achieve this using only one Recyclerview. Make one model class named "Model" of your data, in which add one variable like "isHeader". When you prepare your data list to show in Recyclerview make 2 model object with
'isHeader = true'
Also, make two (item_layout.xml & header_layout.xml) xml files, one for header & another for your original data item.
And in Recyclerview adapter class you have two separate ViewHolder classes for each of the above layouts. refer this for more details and example
public class MultiViewTypeAdapter extends RecyclerView.Adapter {
public static final int HEADER_VIEW = 0;
public static final int LIST_VIEW = 1;
private Resources resources;
private DownloadListener downloadListener;
public MyToursAdapter(List<Model> objects) {
super(objects);
resources = App.getApp().getResources();
}
#NonNull
#Override
public RecycleView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == HEADER_VIEW) {
return new SectionHeaderItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.header_layout, parent, false));
} else
return new ListItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
}
#Override
public void onBindViewHolder(#NonNull BaseRecycleAdapter.ViewHolder holder, int position) {
if (getItemViewType(position) == HEADER_VIEW)
((HeaderItemViewHolder) holder).bindHeaderViewHolder(objects.get(position));
else
((ListItemViewHolder) holder).bindListViewHolder(objects.get(position));
}
#Override
public int getItemViewType(int position) {
if (objects.get(position).isHeader())
return HEADER_VIEW;
else
return LIST_VIEW;
}
public class ListItemViewHolder extends BaseRecycleAdapter.ViewHolder {
//write code to show data from list object.
}
public class HeaderItemViewHolder extends BaseRecycleAdapter.ViewHolder {
//write code to show data from list object.
}
}
enter code here
I'm new to android app development and i have recently made my first app containing a basic tablayout with two fragments in my MainActivity.
In my second tab, it consists of only one RecyclerView as a rootlayout. What i did was populating the RecyclerView with an arraylist of my own dataModel.
My RecyclerView has 13 items. The view of these items is a expansion layout from https://github.com/florent37/ExpansionPanel I followed exactly the way he did for RecyclerView and it worked perfectly.
HOWEVER, my app's startup time has INCREASED significantly to 1s++. I found that the onBindViewHolder method in my RecyclerViewAdapter is the culprit. I tried commenting out the content of onBindViewHolder method and the startup time has reduced by half to 600ms, which is okay for me.
So my questions is How can I solve this problem?
Should I bind my data in a different way?
I dont want to have a slow startup time.
I saw some posts about using databinding API, running on a different threads and asyncs, but i dont know how to do it.
Please help ~ Thanks in advance!
Here's my Adapter:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ArrayList<DataModel> list;
private final ExpansionLayoutCollection expansionsCollection = new ExpansionLayoutCollection();
MyAdapter(ArrayList<DataModel> list, Context context) {
this.list = list;
this.context = context;
expansionsCollection.openOnlyOne(true);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView == LayoutInflater.from(parent.getContext()).inflate(R.layout.expansion_panel, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
DataModel currentData = list.get(position);
switch (currentData.getType()) {
case DataModel.NO_FORMULA:
holder.titleText.setText(currentData.getTitle());
holder.contentText.setText(currentData.getContent());
holder.formulaView.removeAllViews();
expansionsCollection.add(holder.expansionLayout);
break;
case DataModel.WITH_FORMULA:
View formulaLayout = ((Activity)context).getLayoutInflater().inflate(currentData.getFormulaLayoutId(),null);
holder.titleText.setText(currentData.getTitle());
holder.contentText.setText(currentData.getContent());
holder.formulaView.removeAllViews();
holder.formulaView.addView(formulaLayout);
expansionsCollection.add(holder.expansionLayout);
break;
}
}
#Override
public int getItemCount() {
return list.size();
}
private static class ViewHolder extends RecyclerView.ViewHolder {
TextView titleText, contentText;
ExpansionLayout expansionLayout;
FrameLayout formulaView;
ViewHolder(View itemView) {
super(itemView);
this.titleText = itemView.findViewById(R.id.title_text);
this.contentText = itemView.findViewById(R.id.content_text);
this.expansionLayout = itemView.findViewById(R.id.expansionLayout);
this.formulaView = itemView.findViewById(R.id.formula_view);
}
}
This is my item layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="ContentDescription,RtlHardcoded,HardcodedText">
<com.github.florent37.expansionpanel.ExpansionHeader
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
app:expansion_headerIndicator="#id/headerIndicator"
app:expansion_layout="#id/expansionLayout"
app:expansion_toggleOnClick="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:minHeight="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:theme="#style/RecyclerViewTheme">
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="#id/headerIndicator"
android:textColor="#000" />
<android.support.v7.widget.AppCompatImageView
android:id="#+id/headerIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:adjustViewBounds="true"
app:srcCompat="#drawable/ic_expansion_header_indicator_grey_24dp" />
</RelativeLayout>
</com.github.florent37.expansionpanel.ExpansionHeader>
<com.github.florent37.expansionpanel.ExpansionLayout
android:id="#+id/expansionLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:expansion_expanded="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#F5F5F5">
<TextView
android:id="#+id/content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="#000" />
<FrameLayout
android:id="#+id/formula_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</com.github.florent37.expansionpanel.ExpansionLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.7dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
Here's my datamodel:
public class DataModel {
public static final int WITH_FORMULA = 0;
public static final int NO_FORMULA = 1;
private String title, content;
private int type, formulaLayoutId;
DataModel(int type, String title, String content) {
this.type = type;
this.title = title;
this.content = content;
}
DataModel(int type, String title, String content, int formulaLayoutId) {
this.type = type;
this.title = title;
this.content = content;
this.formulaLayoutId = formulaLayoutId;
}
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public int getFormulaLayoutId() {
return formulaLayoutId;
}
public int getType() {
return type;
}
*I've created 13 DataModel objects with long titles and long paragraphs as contents and put them in an arraylist. When the user click on the expansion panel's header, the expansion layout will expand to show the contents of it.
Only 2 out of the 13 items in the recyclerview is the type of WITH_FORMULA that has to inflate the formulaLayout.
If you are sure that the root of your problem is onBindViewHolder then the only problem there is the inflation.
inflate is a very expensive operation because it parses XML and then creates the view hierarchy. Don't do that in onBindViewHolder. Inflate the view once in onCreateViewHolder instead. If you don't want to show it in NO_FORMULA case just set its visibility to GONE and to VISIBLE in WITH_FORMULA case. Changing the visibility is way more simple operation.
I am using a recyclerview for displaying and broadcasting videos of users. However, when I scroll through the recycler view, I see my first view, which has my video gets recycled hence why it's not visible to other users. Is there a way I can make sure that the first view is not recycled so I dont have to worry about my video view getting resetrecycled every single time I scroll through my list?
Here's my code :
In my fragment...
...
<android.support.v7.widget.RecyclerView
android:id="#+id/videoList"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="#+id/myButtonContainer"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/myoldContainer">
...
and the corresponding adapter...
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.myViewHolder> {
private CopyOnWriteArrayList<Person> persons;
private Context mContext;
public GroupAdapter(#NonNull final CopyOnWriteArrayList<Person> persons , Context context) {
this.persons = persons;
this.mContext= context;
for (Person person : this.persons) {
//get names
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false);
final MyViewHolder viewHolder = new MyViewHolder(layout);
return viewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Person person = person.get(position);
final Participant participant = person.getParticipant();
if (person.getName() != null) {
holder.personName.setText(person.getName());
}
if (person.getImage() != null) {
holder.personImage.setImageBitmap(person.getImage());
} else {
holder.personImage.setImageResource(R.drawable.default_profile);
}
holder.personImage.setVisibility(View.INVISIBLE);
holder.personImage.setVisibility(View.VISIBLE);
final VideoView videoView;
if (participant.isMe) {
videoView = participant.videoStreamer.videoView;
} else {
videoView = participant.videoPlayer.videoView;
}
if (holder.personVideo.getChildCount() != 0) {
holder.personVideo.removeAllViews();
}
if (videoView.getParent() != null) {
ViewGroup parent = (ViewGroup) videoView.getParent();
parent.removeView(videoView);
}
holder.personVideo.addView(videoView, myViewHolder.videoLayoutParams);
if (person.isVideoPaused()) {
holder.personVideo.setVisibility(View.INVISIBLE);
holder.personImage.setVisibility(View.VISIBLE);
} else {
holder.personVideo.setVisibility(View.VISIBLE);
holder.personImage.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return persons.size();
}
public static final class MyViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.personVideo)
public ViewGroup personVideo;
#BindView(R.id.personImage)
public ImageView personImage;
#BindView(R.id.personName)
public TextView personName;
protected static FrameLayout.LayoutParams videoLayoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Here's how I am setting it in my fragment:
LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
videoAdapter = new VideoAdapter(myHelper.getPeople(), getContext());
videoList.setLayoutManager(manager);
videoList.setAdapter(videoAdapter);
item_person:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="#drawable/person_border"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:id="#+id/personContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<FrameLayout
android:id="#+id/personVideo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black" />
<ImageView
android:id="#+id/personImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black"
android:src="#drawable/default_profile" />
<TextView
android:id="#+id/personName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#AA555555"
android:gravity="center_horizontal|bottom"
android:textColor="#color/green"
android:textSize="12sp"
android:lines="1"
android:ellipsize="end"
tools:text="androiduser#gmail.com"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
fragment with recycle view: xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout>
....
</RelativeLayout>
<include containers>...</include>
...
<android.support.v7.widget.RecyclerView
android:id="#+id/personList"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
>
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
I don't think that "not recycling" the video view will actually stop it from being destroyed when you scroll. It's not the recycling is the problem but the view unbinding.
I think such complex component such as VideoView should not be inside the RecyclerView at all.. You can try adding it as a static content on top, which most likely will solve the issue. You can use a NestedScrollView for that. Take a look here: Recyclerview inside scrollview- How to scroll whole content?
If you still think you want to keep it in the RecyclerView and disable the recycling, do the following.
Create a separate view type for your video view items. Here is an example:
https://stackoverflow.com/a/26573338/3086818. Treat your video view item as a header view from the example.
Once you do this, there is a RecycledViewPool class which manages the recycling of items inside a RecyclerView. You can tell it which views to recycle and which not. By default it recycles all views. To disable recycling for your video view items use your new view type like this:
recyclerView.getRecycledViewPool().setMaxRecycledViews(TYPE_VIDEO_VIEW, 0);
where TYPE_VIDEO_VIEW is the new type that you created using the previous example. The number 0 tells the RecyclerView how many items to recycle - in this case it's 0, meaning "do not recycle". More info about this here: https://stackoverflow.com/a/36313437/3086818.
I hope this helps.. Good luck!
The answer is yes, you can actually do that.
Since you said "The first item" so you simply add a check.
if(position == 0)
{
holder.setIsRecyclable(false); //This line prevents the row from recycling
}
im trying to populate a list of note/message with check box in the item view.
Ive tried set listener unto the checkbox. sadly upon check box click nothing happens.
if i set listener unto the parent view. its able to trigger onClick method. but this will be triggered each time user click the whole item in the list.
my objectives is more on setting listener for checkbox.
so ill know that user has selected note(s) from the list.
heres my code adapter class together with view holder inner class
public class BroadcastRVA extends RecyclerView.Adapter<BroadcastRVA.BroadcastVH>{
private Context mContext;
private ObservableArrayList<MNote> notes;
private LayoutInflater inflater;
public BroadcastRVA(Context mContext, ObservableArrayList<MNote> notes, LayoutInflater inflater) {
this.mContext = mContext;
this.notes = notes;
this.inflater = LayoutInflater.from(mContext);
}
#Override
public BroadcastRVA.BroadcastVH onCreateViewHolder(ViewGroup parent, int viewType) {
NoteListitemBinding binding = NoteListitemBinding.inflate(inflater);
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.note_listitem, null);
BroadcastVH viewHolder = new BroadcastVH(binding, view);
// create a new view
return viewHolder;
}
#Override
public void onBindViewHolder(BroadcastVH holder, final int position) {
final MNote note = notes.get(position);
holder.cbox.setChecked(note.isSelected());
holder.cbox.setTag(note);
holder.vBinding.setNote(note);
holder.cbox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
MNote note = (MNote) cb.getTag();
note.setIsSelected(cb.isChecked());
notes.get(position).setIsSelected(cb.isChecked());
Toast.makeText(
v.getContext(),
"Clicked on Checkbox: " + cb.getText() + " is "
+ cb.isChecked(), Toast.LENGTH_LONG).show();
}
});
}
public ObservableArrayList<MNote> getNotes() {
return notes;
}
/**
* Returns the total number of items in the data set hold by the adapter.
*
* #return The total number of items in this adapter.
*/
#Override
public int getItemCount() {
if (notes != null)
return notes.size();
else return 0;
}
public class BroadcastVH extends RecyclerView.ViewHolder {
NoteListitemBinding vBinding;
TextView uuid;
CheckBox cbox;
public BroadcastVH(NoteListitemBinding binding, View view) {
super(binding.getRoot());
this.vBinding = binding;
this.uuid = (TextView) view.findViewById(R.id._UUID);
this.cbox = (CheckBox) view.findViewById(R.id.deleteNote);
}
}
}
note_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="note"
type="com.pbasolutions.android.model.MNote" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="1">
<TableLayout android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<CheckBox
android:layout_width="30dp"
android:layout_height="wrap_content"
android:id="#+id/deleteNote"
android:clickable="true"/>
<TextView
android:layout_width="270dp"
android:layout_height="wrap_content"
android:id="#+id/textViewNote"
android:layout_column="1"
android:layout_marginLeft="5dp"
android:text="#{note.textMsgs}"
android:editable="false"
android:textSize="22sp"/>
<TableLayout android:layout_column="1">
<TableRow>
<TextView
android:layout_width="80dp"
android:layout_height="wrap_content"
android:id="#+id/textViewNoteDate"
android:layout_column="0"
android:text="#{note.date}"
android:editable="false"
android:textSize="15sp"/>
</TableRow>
</TableLayout>
<TextView
android:layout_width="270dp"
android:layout_height="wrap_content"
android:id="#+id/_UUID"
android:layout_marginLeft="5dp"
android:text="#{note._UUID}"
android:visibility="invisible"/>
</TableRow>
</TableLayout>
<View style="#style/Line" />
</LinearLayout>
</layout>
what is NoteListitemBinding ? generally speaking ,onClick method must be triggered when you click the checkbox,when you click the view,the viewgroup will receive the touch event first and then pass the event to its child but if the viewgroup prevent the event,the event will consumed,and child cannot get the event, its onClick method will not be triggered.
thanks to #ljl5241861 for mentioning on DataBinding, i realized there small silly mistake in my code.. in order to get the child view responds onClick in need to get the view from databinding.getRoot() not the recycler specific one item group view. after change the ViewHolder constructor, by getting the item view via binding.getRoot, i able to trigger the click listener.. hope this helps others as well!!
public BroadcastVH(NoteListitemBinding binding, View view) {
super(binding.getRoot());
this.vBinding = binding;
View bindView = binding.getRoot();
this.uuid = (TextView) bindView.findViewById(R.id._UUID);
this.cbox = (CheckBox) bindView.findViewById(R.id.deleteNote);
}