RecyclerView onBindViewHolder method increases startup time? - android

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.

Related

Android FlexBox Layout shows only fixed number of lines

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. (:

Expandable recycler view and a textview below issue

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

Layout view does not wrap content after scrolling

I am using a recycler view to show some data. When the app is launched it looks correct as follows with wrap content for height. After I scroll past the last item, I am able to keep scrolling and the data is no longer wrapped, looking like match parent instead for height. Scrolling back up, everything has changed to match parent for the height.
Using past references here, I have tried with ConstraintLayouts and switched height wrapping between the parent layout and the recyclerview itself. Both doesn't help. I am guessing this has to do more with the xml. Please advice.
This is what I expect to always get. This is what I current get when app launches, but changes after I scroll to last item.
When I scroll to last item this happens.
Now if I scroll back up, the height is no longer wrapped. Everything seems to have changed to match parent.
This is xml for the custom view I am using to inflate.
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/feed_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/feed_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp"
tools:text="Test Title" />
<TextView
android:id="#+id/feed_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textSize="12sp"
app:layout_constraintTop_toBottomOf="#+id/feed_title"
tools:layout_editor_absoluteX="0dp"
android:layout_below="#+id/feed_title"
tools:text="This is some random description for testing purposes. Other wise just typing on to create more stuff..." />
</RelativeLayout>
This is layout for the Recycler View which is placed on a Fragment activity.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".fragment.CurrentFeedFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/current_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
This is over at my FragmentActivity where I am loading the data for the RecyclerView.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_current_feed, container, false);
initiateTestData();
loadDataToRecyclerView(view);
return view;
}
private void initiateTestData(){
testTitles = new ArrayList<>();
testDescriptions = new ArrayList<>();
for (int i = 5; i < 25; i++) {
testTitles.add("title " + i);
testDescriptions.add("This is some random description for testing purposes. Other wise just typing on to create more stuff... " + i);
Log.d(TAG, "initiateTestData: " + "title " + i);
}
}
private void loadDataToRecyclerView(View v){
Log.d(TAG, "loadDataToRecyclerView: " + testTitles.size());
RecyclerView recyclerView = v.findViewById(R.id.current_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
FeedAdapter adapter = new FeedAdapter(testTitles, testDescriptions, getActivity());
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
}
Don't think this is relevant. But for reference, this is my adapter class.
public class FeedAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final String TAG = "FeedAdapter";
private List<String> titles;
private List<String> descriptions;
private Context context;
public FeedAdapter(List<String> titles, List<String> descriptions, Context context) {
this.titles = titles;
this.descriptions = descriptions;
this.context = context;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.custom_current_feed, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int i) {
final ViewHolder holder = (ViewHolder) viewHolder;
holder.feedTitle.setText(titles.get(i));
holder.feedDescription.setText(descriptions.get(i));
holder.layout.setOnClickListener(v -> {
Log.d(TAG, "Clicked: " + titles.get(i));
Toast.makeText(context, titles.get(i), Toast.LENGTH_SHORT).show();
});
}
#Override
public int getItemCount() {
return titles.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView feedTitle;
TextView feedDescription;
RelativeLayout layout;
public ViewHolder(#NonNull View itemView) {
super(itemView);
feedTitle = itemView.findViewById(R.id.feed_title);
feedDescription = itemView.findViewById(R.id.feed_description);
layout = itemView.findViewById(R.id.feed_layout);
}
}
}
Your recycler view height is wrap_content
And recycler view item height match_parent
I would think you'd want them the other way around.
The RV's height = match_parent, i.e. the recycler view occupies all available height.
The RV item's height = wrap_content, i.e. each item only as tall as it needs to be, so that multiple items can fit.

How do I not recycle only the first 2 views of my recyclerview or any items?

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
}

How To Make a RecyclerView Show Two Different Fragments

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!

Categories

Resources