I have a root recyclerview and each item is populated with this 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/headerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/accent_gradient"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:paddingStart="16dp"
android:text="My original"
android:textColor="#color/colorWhite"
android:textSize="14sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rootRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
</LinearLayout>
As you can see, there is another recylcerview within each item. I wanted to achieve the effect of sectioned recyclerview with headers. (So Header > List Header > List)
Now each recyclerview is populated with this layout view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_toStartOf="#id/relativeLayout3"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:id="#+id/foodName_fv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:text="Food name"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#color/colorBlack"
android:textSize="18sp"
android:transitionName="Food_Name"/>
<TextView
android:layout_below="#id/foodName_fv"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:id="#+id/foodGrams_fv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="Grams"
android:textColor="#color/colorLightGrey"
android:textSize="15sp" />
<LinearLayout
android:id="#+id/relativeLayout3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:orientation="vertical"
android:layout_alignParentEnd="true"
>
<TextView
android:id="#+id/foodCalories_fv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Calories"
android:textColor="#color/colorBlack"
android:textSize="18sp"
android:transitionName="Food_Cal"/>
<TextView
android:layout_gravity="center"
android:id="#+id/calText_fv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingBottom="8dp"
android:text="#string/calText_fv"
android:textColor="#color/colorLightGrey"
android:textSize="14sp" />
</LinearLayout>
</RelativeLayout>
This is a fairly simple layout but for some odd reason the child recyclerview takes soo much time to inflate these rows. I did some testings and it took (on average) 1200 ms to load 300 rows!
Now the weirdest thing is that before this code i had a plain listview (without any sections and headers, just a big list) and it loaded all these rows very fast. I have'nt changed nothing but this code (Converting it from listview with an adapter to recyclerview with an adapter) so it looks pretty strange to me, but i dont know i might be missing something here.
This is the onCreateViewHolder method:
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.food_view, parent, false);
return new ViewHolder(view);
}
In conclusion the child recyclerview takes too much time to do this work. And yes I am aware i can do "Lazy loading" but I want to figure out why the hell is it so slow?
EDIT 1:
Tried implementing it in a fresh new project, thought something with the broke somehow, but its still gave same results, also tried on my actual phone and not an emulator and still slow. I really want to figure out this one already because it seems very illogical.
EDIT 2:
Posting the adapters code for hamza khan:
Code for the root recyclerview:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class SelectFoodRootRecyclerAdapter extends RecyclerView.Adapter<SelectFoodRootRecyclerAdapter.ViewHolder> {
private static final String TAG = "SelectFoodRootRecyclerAdapter";
private Context mContext;
private ArrayList<SectionedFoodGroup> items;
private ArrayList<SelectFoodChildRecyclerAdapter> adapters; //For filter use outside this class
RecyclerView.RecycledViewPool recycledViewPool;
public SelectFoodRootRecyclerAdapter(Context mContext) {
this.mContext = mContext;
items = new ArrayList<>();
adapters = new ArrayList<>();
recycledViewPool = new RecyclerView.RecycledViewPool();
SectionedFoodGroup section1 = new SectionedFoodGroup(mContext.getString(R.string.myFoods),
FoodsDBHelper.getAllFoodRows(Food.DBType.USER_CREATED_FOODS_DB.ordinal()));
SectionedFoodGroup section2 = new SectionedFoodGroup(mContext.getString(R.string.foods),
FoodsDBHelper.getAllFoodRows(Food.DBType.REGULAR_FOOD_DB.ordinal()));
addSection(section1);
addSection(section2);
}
private void addSection(SectionedFoodGroup sectionedFoodGroup){
if(sectionedFoodGroup.getFoods().size() > 0){
items.add(sectionedFoodGroup);
adapters.add(new SelectFoodChildRecyclerAdapter(mContext, sectionedFoodGroup.getFoods()));
}
}
public ArrayList<SelectFoodChildRecyclerAdapter> getAdapters(){
return adapters;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_sfa_viewholder, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
SectionedFoodGroup sectionedFoodGroup = items.get(position);
holder.headerText.setText(sectionedFoodGroup.getHeaderName());
holder.foodsRecycler.setAdapter(adapters.get(position));
holder.foodsRecycler.setRecycledViewPool(recycledViewPool);
}
#Override
public int getItemCount() {
return items != null ? items.size() : 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView headerText;
RecyclerView foodsRecycler;
public ViewHolder(#NonNull View itemView) {
super(itemView);
headerText = itemView.findViewById(R.id.headerText);
foodsRecycler = itemView.findViewById(R.id.rootRecycler);
}
}
}
Code for the child recyclerview (the one containing all the actual rows):
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.text.DecimalFormat;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class SelectFoodChildRecyclerAdapter extends RecyclerView.Adapter<SelectFoodChildRecyclerAdapter.ViewHolder>{
private static final String TAG = "SelectFoodChildRecyclerAdapter";
private Context mContext;
private ArrayList<Food> original;
private DecimalFormat decimalFormat;
public SelectFoodChildRecyclerAdapter(Context mContext, ArrayList<Food> foods) {
this.mContext = mContext;
this.original = foods;
decimalFormat = new DecimalFormat("#.0");
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.food_view, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int foodPos) {
Food currFood = original.get(foodPos);
holder.foodName.setText(currFood.getName());
float grams = 100;
if(currFood.getAmount() != 0) grams = (float)currFood.getAmount();
int roundedGrams = (int)Math.round(grams);
holder.foodGrams.setText(roundedGrams +" "+ mContext.getResources().getString(R.string.gramsWord));
float caloriesToShow = (float)currFood.getCalories();
holder.foodCalories.setText(decimalFormat.format(caloriesToShow));
}
#Override
public int getItemCount() {
return original != null ? original.size() : 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView foodName;
TextView foodGrams;
TextView foodCalories;
public ViewHolder(View itemView) {
super(itemView);
foodName = itemView.findViewById(R.id.foodName_fv);
foodGrams = itemView.findViewById(R.id.foodGrams_fv);
foodCalories = itemView.findViewById(R.id.foodCalories_fv);
}
}
}
The calling activity onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_food);
foodsRecyclerView = findViewById(R.id.rootRecycler);
selectFoodRootRecyclerAdapter = new SelectFoodRootRecyclerAdapter(this);
foodsRecyclerView.setAdapter(selectFoodRootRecyclerAdapter);
}
Again, as you can see all the code is fairly simple..
EDIT 3:
Ok so i've narrowed it down even more. I tried to do just one big recyclerview of the second layout and it worked fast. SOOO the problem must be with the first layout/the root recyclerview adapter code. but everything seems fine with it...
I really don't know what am I missing here!! Thanks for any kind of help!
Thanks!
Well, because no one was able to answer my question and I've found my answer then ill share it with other people who might look at this in the future and think, wow this is exactly what I need.
So.. It turns out using a RecyclerView within RecyclerView is a big no no. thats because the onCreateViewHolder method gets called as many times as your items array size, and thats very heavy, especially if you have a large list. (I think its the same as RecyclerView within a ScrollView) - So try to avoid using this type of implementation.
So what can you do?
Option 1 - Create a multi view RecylcerView adapter. that means that you have one adapter and it can handle multiple view types (perfect for what i wanted. 1 view type is a header and the second view type is another layout to act as my main row).
You can look it up in google to find good results on how to implement it but the main
So declare these variables:
private int VIEW_TYPE_HEADER = 0;
private int VIEW_TYPE_MAIN_ROW = 1;
Next override the next method, and what this basically does it gives each row a special int which we then can compare in onCreateViewHolder and decide which layout we want to inflate (e.g. If its going to be a header or a main row)
#Override
public int getItemViewType(int itemPos) {
if(items.get(itemPos) instanceof String){
return VIEW_TYPE_HEADER;
}
return VIEW_TYPE_MAIN_ROW;
}
On the onCreateViewHolder we can test to see what type of row we are dealing with and inflate accordingly.
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_HEADER){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header)_layout, parent, false);
return new HeaderViewHolder(view);
}
//Else a regular row.
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_row, parent, false);
return new MainViewHolder(view);
}
And of course make a separate view holder for each type.
Option 2 - Because I need only 2-3 sections, what i could also do is just "hardcode" it like this:
Create a TextView to be the header and below it a RecyclerView AND again 2-3 times. this approach is obviously less flexible but it works if you want some small.
Hope this'll help someone one day.
Related
I'm trying to build a word-to-word RTL language ebook application. The data for the ebook is stored in an Sqlite database. After vigorous searching I found Flexbox layout suitable to my need in order to display my data in my required format. The following link helped me out: Android GridLayout with dynamic number of columns per row But the issue is that if the data passed in the view is large, then partial data is being displayed. If the data passed is small, then i have no issues at all.
Here is the adapter code:
public class VerseAdapter extends RecyclerView.Adapter<VerseAdapter.ViewHolder> {
private List<verseItem> items;
private Context context;
wordsAdapter sAdapter;
GridLayoutManager layoutManager;
public VerseAdapter(List<verseItem> items, Context context) {
this.items = items;
this.context = context;
}
// This method is used to attach
// custom layout to the recycler view
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View vw = LayoutInflater.from(parent.getContext()).inflate(R.layout.verse_row, parent, false);
return new ViewHolder(vw);
}
// This method is used to set the action
// to the widgets of our custom layout.
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
verseItem item = items.get(position);
ArrayList<wordsModel> models = new ArrayList<>(item.getWordsModelList());
String ayahnum = "" + item.getAyah();
holder.ayah.setText(ayahnum);
holder.verses_telugu.setText(item.getVerses_telugu());
// FlexboxLayoutManager code:
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexDirection(FlexDirection.ROW_REVERSE);
layoutManager.setJustifyContent(JustifyContent.FLEX_START);
ViewHolder.wordsrecyclerView.setLayoutManager(layoutManager);
sAdapter = new wordsAdapter(models);
ViewHolder.wordsrecyclerView.setAdapter(sAdapter);
sAdapter.notifyItemChanged(position);
holder.setIsRecyclable(false);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView ayah;
TextView verses_telugu;
public static RecyclerView wordsrecyclerView;
public ViewHolder(View itemView) {
super(itemView);
ayah = itemView.findViewById(R.id.ayah);
verses_telugu = itemView.findViewById(R.id.telugu_line);
wordsrecyclerView = itemView.findViewById(R.id.words_RV_id);
}
}
}
Here is my layout file:
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/ayah"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_margin="5dp"
android:textAlignment="viewStart"
android:textColor="#27A82C"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/words_RV_id"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/telugu_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_margin="5dp"
android:textAlignment="textStart"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
This is how the final result show actually look like:
This is how my app looks like:
Now if you notice from the screenshots, the data that i'm trying to display is same. But in my app, not all words are being displayed as you can notice.
Another thing i have noticed is for larger data if the screen orientation is vertical the maximum lines i can see is 10 and if it is horizontal i can see 6 lines at max. These number of lines are fixed for all the places where the data is large. If the data is smaller it is displayed in the exact number of lines required and as mentioned earlier I have no issues at all.
Hoping someone helps me out soon. Thanks in advance. (:
I am following the code from Android Developers:RecyclerView
, and I want a more complex layout for a single item in the RecyclerView (I want it to have an image, and 2 text views inside a RelativeLayout, rather than just a single TextView without any Layout parent.
The code works when my my_text_view.xml file contains just a TextView like so:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/my_text_view"
android:layout_width="match_parent"
android:layout_height="28dp"
android:textSize="24sp"
tools:text="Hello John Doe" />
But when I try to change it to a more complex layout, that contains an ImageView and 2 TextViews under a RelativeLayout, like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip" >
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/secondLine"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="#id/icon"
android:ellipsize="marquee"
android:maxLines="1"
android:text="Description"
android:textSize="12sp" />
<TextView
android:id="#+id/firstLine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="#id/secondLine"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="#id/icon"
android:gravity="center_vertical"
android:text="Example application"
android:textSize="16sp" />
</RelativeLayout>
Then the app will crash.
I really don't know where I should apply more changes, I am quite lost in their explanation on holders, and adapters, and layout managers. What exactly in their code should be changed? I basically copied their code if you need reference (And it worked, that's why I didn't copy their code here)
Edit: I believe the changes should be somewhere in this code, but I don't know where and how exactly
package com.example.lastlocation.recyclerviewer;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class MyViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView mTextView;
public MyViewHolder(TextView v) {
super(v);
mTextView = v;
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
TextView v = (TextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.mTextView.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
If you modify the contents of the item view XML file, you will also have to modify your ViewHolder class and your Adapter's onCreateViewHolder() and onBindViewHolder() methods.
In your new version, you have a RelativeLayout, an ImageView, and two TextViews. You probably don't need to do anything to the RelativeLayout, so your ViewHolder will only have references to the ImageView and TextViews:
public static class MyViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView firstLine;
public final TextView secondLine;
public MyViewHolder(View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.icon);
firstLine = itemView.findViewById(R.id.firstLine);
secondLine = itemView.findViewById(R.id.secondLine);
}
}
Your onCreateViewHolder() method doesn't need to change much. Mostly you just need to remove the cast, since now you're no longer inflating a TextView object:
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
return new MyViewHolder(itemView);
}
Your onBindViewHolder() method needs to change to use the new views you've specified:
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.icon.setImageResource(R.drawable.whatever);
holder.firstLine.setText("Hello");
holder.secondLine.setText("World");
}
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
}
I've tried creating a RecyclerView which displays the songs I have on my phone from a pre-populated ArrayList. Here is my code for my activity:
public class HomeActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private RecyclerView songRecyclerView;
private RecyclerView.Adapter songRecyclerAdapter;
private RecyclerView.LayoutManager recyclerLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
//get the songs list for the adapter
ArrayList<Audio> songList;
StorageUtils storageUtils = new StorageUtils(getApplicationContext());
songList = storageUtils.loadAudio();
//Recycler view setup for songs display
songRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
songRecyclerView.setHasFixedSize(true);
recyclerLayoutManager = new LinearLayoutManager(this);
songRecyclerView.setLayoutManager(recyclerLayoutManager);
songRecyclerAdapter = new SongAdapter(songList);
songRecyclerView.setAdapter(songRecyclerAdapter);
}
The Audio class has the getTitle() and getArtist() methods, which do work. The loud audio() also works so Songlist definitely has elements in it.
Here is the xml of the recyclerview item:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/recycler_item"
android:layout_width="match_parent"
android:layout_height="72dp"
android:visibility="visible">
<TextView
android:id="#+id/recycler_item_songName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="song name"
android:textColor="#color/textPrimary"
android:textSize="16sp"
android:textStyle="normal"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/recycler_item_artistName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:text="song artist"
android:textColor="#color/textSecondary"
android:textSize="16sp"
android:textStyle="normal"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/recycler_item_songName" />
</android.support.constraint.ConstraintLayout>
Here is my implementation of the Adapter:
package com.ecebuc.gesmediaplayer;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
// The dataset
private ArrayList<Audio> songList;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView recyclerTitleView, recyclerArtistView;
public ViewHolder(View itemView) {
super(itemView);
this.recyclerTitleView = (TextView) itemView.findViewById(R.id.recycler_item_songName);
this.recyclerArtistView = (TextView) itemView.findViewById(R.id.recycler_item_artistName);
}
}
// Constructor
public SongAdapter(ArrayList<Audio> songList){
this.songList = songList;
}
#Override
public SongAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View listItemView = layoutInflater.inflate(R.layout.song_list_item, parent, false);
ViewHolder viewHolder = new ViewHolder(listItemView);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Audio currentSong = songList.get(position);
holder.recyclerTitleView.setText(currentSong.getTitle());
holder.recyclerArtistView.setText(currentSong.getArtist());
Log.d("onBind: ", (String)holder.recyclerTitleView.getText() + (String)holder.recyclerArtistView.getText());
}
#Override
public int getItemCount() {
return songList.size();
}
}
The frustrating thing is that at my very first attempt at creating the whole recycleView, it did work and displayed the text. I tried adding an imageView as the cover of the songs to the layout of each item in the list, and the code to display that as well, and it wasn't working anymore. When tried to revert and have only the code for text, it stopped working altogether.
I am bashing my head over this because if I did make some small change somewhere, I don't know where it might be any more now. But I did try to recreate the class for the adapter and the layout file for the whole recycler functionality from scratch, and still not showing. The layout items have to be there because I see the shadow of the scroll.
Also, in the adapter's onBindViewHolder, that Log.d displays correctly each song title and artist. And it calls the newly created views' getText(). It's like the text was white. And no, the values of #color/text primary and textSecondary in the XML are #212121 and #424242 and were always like that (again, it did display the values the first time, and I haven't touched the colours).
I've looked for similar problems on StackOverflow and online, but I don't seem to have that kind of mistakes. I don't know what to do anymore. Heck, I'll even include the XML of the screen that has the actual recyclerView in it. At this point I don't know what could make those views invisible:
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".HomeActivity"
tools:showIn="#layout/app_bar_home">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Finally, I thought of looking at the developer options on my phone and display layout outlines, and this is what I had...as if the two textViews are not being created, yet I was able to get their text content after I've set it, so how is that even possible? Thank you to whoever can help me, I'm just trying to learn here...
For future reference in case anyone has a similar problem, I managed to solve it by simply changing my constraint layout in the recycler view item XML, to linear layout (or I'd guess whatever type of layout other than constraint) and compiled.
It worked well, displaying everything correctly. Then changing the XML back to constraint layout like before, and everything was still working fine. At this point I'd say it will forever remain a mystery.
make some change in adapter constructor like below code..
public Context context;
// Constructor
public SongAdapter(Context context,ArrayList<Audio> songList){
this.context=context;
this.songList = songList;
}
then after main activity make adapter object like below ..
SongAdapter adapter;
and remove this line of code ..
private RecyclerView.Adapter songRecyclerAdapter;
and used set adapter like below code..
RecyclerView recyclerView;
SongAdapter songRecyclerAdapter;
private void setAdapter(){
recyclerView.setLayoutManager(new LinearLayoutManager(this));
if (songRecyclerAdapter==null) {
songRecyclerAdapter = new SongAdapter(this,songList);
recyclerView.setAdapter(songRecyclerAdapter);
songRecyclerAdapter.notifyDataSetChanged();
}
else{
songRecyclerAdapter.notifyDataSetChanged();
}
}
I'm trying the two new features in android L version,
RecyclerView
CardView
Well the RecyclerView got working fine for me.But when I tried to execute the CardView.It throws an error and I was not able to find whats the error is!
MainActivity.java
public class MainActivity extends Activity {
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] myDataset ={"Uday","Harihar","chetan","ravi","harish","Rahul","satish","vikaram","ravikiran","harish2"};
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// improve performance if you know that changes in content
// do not change the size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
// specify an adapter (see also next example)
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
}
MyAdapter.java
package com.hrh.material;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private String[] mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView mTextView;
public ViewHolder(View v) {
super(v);
mTextView = (TextView)v.findViewById(R.id.text);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_card_view, parent, false);
// set the view's size, margins, paddings and layout parameters
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) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.mTextView.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
activity_card_view.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<android.support.v7.widget.CardView
android:id="#+id/cardview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:elevation="100dp"
card_view:cardCornerRadius="8dp"
>
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
</RelativeLayout>
in the layout it shows that "Resource id 0x7f040001 is not of type STYLE (instead anim)".
I resolve the issue following this steps:
Removing the android-support-v7-appcompat, and cardview libs projects from my IDE (Eclipse in my case).
Update Android SDK Tools and Android Support Library from Android SDK Manager.
Restart your IDE (Eclipse).
Import the android-support-v7-appcompat, and cardview libs projects.
And make sure you added xmlns:card_view at the top of your your xml file:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp">
And thats it , hope it helps.
i am also facing same error,i found solution for this just remove two packages from sdk and install again.They are Extras->android support repository and android support library