Android: How to scroll a parentView when child containing recyclerView is scrolled - java

I have created an app that contains a viewPager inside the mainActivity, this viewPager contains 5 fragments, of which 4 are recyclerViews and one is a normal linearLayout containing some text...
Here is a screenshot of the app, not all tabs in the tablayout are visible:
Now, as you might have seen already, there isn't much space in the viewPager for the user to see anything, so they have to scroll too much to view things in the recylerView. I want to modify my app so that when the user tries to scroll inside the recyclerView, the visible part of the mainActivity is scrolled down till the recyclerView occupies the entire page and then the recyclerView begins to scroll normally.
Can someone please help me implement this type of scroll feature into my app. You can just check out this app for a reference to what I'm saying. Just open up any movie or tvSeries and then try scrolling, the mainActivity gets scrolled first and then the rest of the layout.... Can someone please help. I've already tried the solutions on stackOverflow and many of them don't work, I also tried to google for a solution, but didn't get anything useful....
Here is the code for my adapter:
public class cardViewAdapterCreditsCast extends RecyclerView.Adapter<cardViewAdapterCreditsCast.ViewHolder> {
private Context context;
private List<creditsModel> creditsModels;
public cardViewAdapterCreditsCast(Context context, List<creditsModel> creditsModels) {
this.context = context;
this.creditsModels = creditsModels;
}
#Override
public cardViewAdapterCreditsCast.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_cast_row, parent, false);
return new cardViewAdapterCreditsCast.ViewHolder(view);
}
#Override
public void onBindViewHolder(cardViewAdapterCreditsCast.ViewHolder holder, int position) {
final creditsModel creditsModel = creditsModels.get(position);
int tempNumber = holder.celebImage.getWidth();
holder.celebImage.setMinimumHeight(tempNumber);
holder.celebImage.setMaxHeight(tempNumber + 1);
String imagePath = "https://image.tmdb.org/t/p/w500" + creditsModel.getProfilePath();
holder.starringAs.setText(creditsModel.getCharacter());
holder.celebName.setText(creditsModel.getActorName());
if (creditsModel.getProfilePath() != null) {
Picasso.with(context).load(imagePath).transform(new CircleTransform()).into(holder.celebImage);
} else
holder.celebImage.setBackgroundResource(R.drawable.not_found);
}
#Override
public int getItemCount() {
return creditsModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView celebImage;
public TextView celebName;
public TextView starringAs;
private LinearLayout linearLayout;
public ViewHolder(View itemView) {
super(itemView);
linearLayout = (LinearLayout) itemView.findViewById(R.id.castRowMainLinearLayout);
celebImage = (ImageView) itemView.findViewById(R.id.castRowImage);
celebName = (TextView) itemView.findViewById(R.id.castRowName);
starringAs = (TextView) itemView.findViewById(R.id.castRowAppearance);
}
}
}

Use nested Scrollview if there are more than one scrollview or recyclerview.

You can use a CoordinatorLayout to implement this easily.
Example, http://saulmm.github.io/mastering-coordinator
Documentation, https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.html

Related

Remove certain items which are empty from RecyclerView list?

I'm making an app using TMDB API and have gotten stuck at a small issue.
TMDB API shows seasons and episodes which are empty, basically, those are yet to air but since those are empty, the app shows a blank item that I'm trying to get rid of.
Here's my adapter:
public class SeasonAdapter extends RecyclerView.Adapter<SeasonAdapter.ViewHolder> {
private final List<Season> seasons;
private final Context context;
private final RequestOptions requestOptions;
public SeasonAdapter(List<Season> seasons, Context context) {
this.seasons = seasons;
this.context = context;
requestOptions = new RequestOptions().centerCrop().placeholder(R.drawable.poster_placeholder).error(R.drawable.poster_placeholder);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_season_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final Season season = seasons.get(position);
holder.tvTitle.setText(season.getSeasonTitle());
if (season.getSeasonDate() != null && !season.getSeasonDate().isEmpty()) {
holder.tvDate.setText(context.getResources().getString(R.string.aired_on) + season.getSeasonDate());
} else {
holder.tvDate.setVisibility(View.GONE);
}
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
holder.tvEpisodes.setText(String.valueOf(season.getSeasonEpisodes()) + context.getResources().getString(R.string.total_episodes));
Glide.with(context).load(season.getSeasonImageURL()).apply(requestOptions).into(holder.ivPoster);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, EpisodeActivity.class);
intent.putExtra("title", season.getShowTitle());
intent.putExtra("seasonTitle", season.getSeasonTitle());
intent.putExtra("seasonNo", season.getSeasonNo());
intent.putExtra("tvId", season.getTvId());
v.getContext().startActivity(intent);
});
}
#Override
public int getItemCount() {
return seasons.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView ivPoster;
public TextView tvTitle, tvDate, tvEpisodes;
public ViewHolder(#NonNull View itemView) {
super(itemView);
ivPoster = itemView.findViewById(R.id.ivSeasonPoster);
tvTitle = itemView.findViewById(R.id.tvSeasonTitle);
tvDate = itemView.findViewById(R.id.tvSeasonAired);
tvEpisodes = itemView.findViewById(R.id.tvSeasonEpisodes);
//Poster Corners
ivPoster.setClipToOutline(true);
}
}
}
I tried doing this:
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
It does seem to hide the season which has no episodes but if a show has multiple seasons without episodes, my app crashes so I figured this isn't the right solution so any help is appreciated.
I suggest performing that removal logic in the constructor of the adapter rather than in onBind. onBind happens as the recycler view is finalising the details of each view holder immediately before it's shown to the user. You want to do as little as possible logic in here to keep the recycler view performant.
Inside the constructor (or even before the list is passed in) you should perform a loop and remove those items that don't meet the criteria before setting the instance variable.
It's been a long time since I wrote code in java and so I'd end up with unhelpful incorrect syntax if I tried to do it here.

Recyclerview is not recycling views properly

I am loading images from URL into horizontal recyclerview using Picasso as:
public class pIndicatorAdaptor extends
RecyclerView.Adapter<pIndicatorAdaptor.IndicatorViewHolder> {
//constructor and other stuff
#Override
public IndicatorViewHolder onCreateViewHolder(ViewGroup parent, int vType) {
View view = layoutInflater.inflate(R.layout.i_item, parent, false);
return new IndicatorViewHolder(view);
}
#Override
public void onBindViewHolder(IndicatorViewHolder holder, int position) {
picasso.load(images.get(position).getSrc()).fit().centerCrop()
.into(holder.imageView, null);
}
class IndicatorViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
IndicatorViewHolder(#NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
}
}
}
But I get the result like this,The bottom horizontal recyclerview is where I am loading images:
when you keep scroll the recycler view like that,it's load the image every time and ofcourse it took alot of time to load the image.i think you should use glide on this because glide is really good on load the image from cache ( if exist) ;)

Custom View to be Used as a Nested View or as a RecyclerView Item view in Android

I'm new to android, but have a good JavaFX experience. I'm trying to create a custom view that i can reuse, but having a hard time figuring out the correct way to do it.
In javafx i could achieve this by: Creating a separate fxml file defining the layout of the custom view, then create a controller class linked to the fxml file, in that class, i'd have a method to retrieve the data model of the controller and use it to fill in the labels, etc.
The custom view i want would be
Constrained Layout
TextView (constrained to right anchor)
Round TextView (constrained to left anchor)
What is the best way to do this in android? Also, Is it possible to achieve this with a RecyclerView? If yes, how can i use a custom view for each item and set its data?
The question is broad. You may need additional research on creating views
Create a recyclerview in the main.xml,
a separate file with an item view.
You have 3 views in your item view - white background with margins (linearlayout?), right textView, and left textview.
The left textview should have android:background="drawable/round_shape" and round_shape.xml defined in your drawables folder. Everything is done in 3 xml files, main.xml for recyclerview, item.xml, round_background.xml. Then, the recyclerview adapter to bind the textviews with your array, and recyclerview initialization
A typical RV adaptor
public class MyRV extends RecyclerView.Adapter<MyRV.ViewHolder> {
private List<MyModelItemWith2Strings> mDataSet; // You may need to setup an array,
// with 2 String objects - for the right and left textviews
// Use an array of class with 2 elements rather than <String>, e.g. List<MyModelItemWith2Strings>
// pass your model here
// this setData will be used to provide the contents for the textviews
void setData(List< /* set your 2 string class here*/ > dataSet) {
mDataSet = dataSet;
}
static class ViewHolder extends RecyclerView.ViewHolder {
// Here you bind item TV's
// first you declare textviews that you will use to fill with data
// Add any other item views you will need to fill in
public TextView tv;
public TextView tv2;
public ViewHolder(LinearLayout v) {
super(v);
// Bind itemview views here. Put R.id.tv from your itemview.xml
tv = v.findViewById(R.id.....);
tv2 = v...
}
}
// Add your itemview layout here
#Override
public MyRV.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(/***R.layout.item_view***/, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder( MyRV.ViewHolder h, int position) {
// get content from your model (the above list) and fill in the the itemview textviews
String a= mDataSet.get(position).getItem1();
String b = mDataSet.get(position). getItem2();
...
h.tv.setText(a);
// set clickers if you want to. The clicker class is below.
h.tv.setOnClickListener(new Click(position));
h.tv2.setText(...)
}
// This is obligatory to pass for your RV to initialize. It won't work if you don' t tell Android how to count your array soze
#Override
public int getItemCount() {
return mDataSet.size();
}
// These are my implementation of clickers. I prefer to put them in the nested class of the adapter.
private class Click implements OnClickListener {
private int pos;
Click(int position) {
pos = position;
}
#Override
public void onClick(View p1) {
// get data from your array on click
mDataSet.get(pos);
// Use pos as position on the array, mData.get(pos)
}
}
}
Then, in your main class set a recyclerview
RecyclerView rv = (RecyclerView) findViewById(R.id.rv_In_Main_Xml);
// just additional tunings.
rv.setHasFixedSize(true);
rv.setLayoutManager(new LinearLayoutManager(context)); // <- context = this, if you are in the Main activity
Then set the adapter
MyRV rva = new MyRV();
rva.setData(myArray_with_2_string_objects_to_fill_tvs);
rv.setAdaptor(rva);
And your recycler view gets filled with data

RecyclerView items in ArrayList

I am working on an android project and I am putting some TextViews inside a RecyclerView and at the same time I am trying to put those things in an array list as ViewHolder type. After some tests on the program i understood that the items that are inserted in the ArrayList are only the items that are shown in the screen. For example if my screen fits 15 textviews and i put 30 textviews inside the recycler view and arraylist, the size of the arraylist will be only 15 so i can't make any changes to the rest of the items.
Also when i scroll down the recycler view the arraylist get a size of the items that has been shown while scrolling but when i scroll back to the top and try to change the number of the TextViews and make them less the program crashes.
What i want is to have all of the items that have been added to the recycler view also in the arraylist in order to can use them.
Recycler View class code:
public class Tab1Child1Numbers extends RecyclerView.Adapter<Tab1Child1Numbers.ViewHolder> {
ArrayList<Integer> textFront;
ArrayList<Integer> textBack;
ArrayList<Integer> colors;
Context context;
ArrayList<ViewHolder> texts = new ArrayList<>();
public Tab1Child1Numbers(Context context, ArrayList<Integer> textFront, ArrayList<Integer> textBack, ArrayList<Integer> colors) {
super();
this.context = context;
this.textFront = textFront;
this.textBack = textBack;
this.colors = colors;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.tab1_child1_numbers, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
viewHolder.textFront.setText(textFront.get(i)+"");
viewHolder.textBack.setText(textBack.get(i)+"");
viewHolder.textFront.setBackgroundColor(colors.get(i));
viewHolder.textBack.setBackgroundColor(colors.get(i));
texts.add(viewHolder);
// cardsFront.add(viewHolder.imageFront);
// cardsBack.add(viewHolder.imgThumbnail);
viewHolder.setClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
if (isLongClick) {
} else {
Deck deck = new Deck();
deck.flipCard(texts.get(position).frame, texts.get(position).textFront, texts.get(position).textBack);
}
}
});
}
#Override
public int getItemCount() {
return textFront.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public TextView textFront;
public TextView textBack;
public FrameLayout frame;
public LinearLayout layout;
private ItemClickListener clickListener;
public ViewHolder(View itemView) {
super(itemView);
textFront = (TextView) itemView.findViewById(R.id.txt1);
textBack = (TextView) itemView.findViewById(R.id.txt2);
frame = (FrameLayout) itemView.findViewById(R.id.list_frame);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public void setClickListener(ItemClickListener itemClickListener) {
this.clickListener = itemClickListener;
}
#Override
public void onClick(View view) {
clickListener.onClick(view, getPosition(), false);
}
#Override
public boolean onLongClick(View view) {
clickListener.onClick(view, getPosition(), true);
return true;
}
}
class Deck {
private void flipCard(View rootLayout, View cardFace, View cardBack) {
FlipAnimation flipAnimation = new FlipAnimation(cardFace, cardBack);
if (cardFace.getVisibility() == View.GONE) {
flipAnimation.reverse();
}
rootLayout.startAnimation(flipAnimation);
}
public void flipAll(){
System.out.println(texts.size()+"--------");
randomize();
for (int i = 0; i < texts.size(); i++) {
flipCard(texts.get(i).frame, texts.get(i).textFront, texts.get(i).textBack);
}
}
private void randomize(){
for (int i=0; i<texts.size(); i++){
if (texts.get(i).textFront.getVisibility() == View.GONE) {
texts.get(i).textFront.setText(textFront.get(i) + "");
texts.get(i).textFront.setBackgroundColor(colors.get(i));
}
else {
texts.get(i).textBack.setText(textBack.get(i) + "");
texts.get(i).textBack.setBackgroundColor(colors.get(i));
}
}
}
}
It looks like you're trying to do something that you shouldn't.
onBindViewHolder() is called by the system whenever it is about to display a ViewHolder to the user. So it will initially be called once for every view on the screen, plus once for a small number of views just off-screen (so that they can scroll on screen nicely).
However, this means it will also be called repeatedly as the user scrolls through the data in your adapter. In these cases, the ViewHolder being passed to onBindViewHolder() might have been recycled, meaning it might previously have been used to display different data and is now being re-used to display new data.
Let's say you have one million items in your data set, but your layout is set up in such a way that only 15 items are visible on screen at any one time. Chances are good that the system will wind up creating about 20 ViewHolder instances (15 on the screen, 5 available for re-use just off-screen). These same 20 ViewHolders will be used to display all one million items if the user scrolls enough. They'll just keep being re-used over and over.
That's why this line is problematic:
texts.add(viewHolder);
You're going to wind up building a list with potentially millions of objects in it, with the same 20 ViewHolders appearing multiple times in your list.
It would be much better to think about storing your data in a different way, rather than trying to store ViewHolders passed to onBindViewHolder().

General Question about Android Gallery Control

So, I've been doing xml layout for a project that involves getting a horizontal scrollable row of images on a screen, and did so using just a horizontalscrollview. and a bunch of imagebuttons. I used an include to put this on another xml layout page and another programmer will then populate the images dynamically.
My question is, how would the gallery control benefit us? I haven't done much Java programming and I've seen some instruction online of how to implement this control, but not a lot on WHY you would use this. It looks like this control works mainly via Java insertion via array, but other than that I can't tell what the benefits are from reading over my way of just creating the layout and having this other programmer insert his own images manually.
Another related question - do these images for a gallery need to me imageviews, or can they be imagemaps? Currently they are imagemaps because we want them to be clicable to go to a user's profile, etc.
Thanks!
Gallery is nearly perfect. In one of my projects I do have a LinearLayout with a Gallery in it:
<Gallery
android:id="#+id/gallery"
android:layout_height="0dip"
android:layout_weight="1"
android:layout_width="fill_parent"
android:spacing="2dip" />
An activity implements OnItemClickListener:
public class MyActivity extends Activity implements OnItemClickListener {
A data structure contains all items and is send to an adapter:
private void processGallery() {
adapter = new MyAdapter(this, containers, appName);
if (adapter != null) {
gallery.setAdapter(adapter);
}
}
#Override
public void onItemClick(final AdapterView<?> adapterView, final View view, final int position, final long id) {
if (containers != null) {
container = containers.get(position);
if (container != null) {
// Handle selected image
}
}
}
The adapter is a usual BaseAdapter - nothing magic:
public class MyAdapter extends BaseAdapter {
private ArrayList<Container> containers;
private Context context;
public int getCount() {
return containers.size();
}
public Object getItem(final int position) {
return containers.get(position);
}
public long getItemId(final int position) {
return position;
}
public View getView(final int position, final View contentView, final ViewGroup viewGroup) {
ImageView imageView = new ImageView(context);
Container container = containers.get(position);
if (container != null) {
// Do your image thing here
}
return imageView;
}
public MyAdapter(final Context context, final ArrayList<Container> containers, final String appName) {
this.context = context;
this.containers = containers;
}
}
This simple code gives a horizontal scrolling image gallery with clickable items. The click is send to the activity - no need to do something fancy in the adapter. I removed from the code shown here a DrawableCache that I use because my items do come from the web.

Categories