Add fragments dynamically - android

I need to show text below image but with horizontal swipe functionality. That is every time a person swipes horizontally image and text is updated. But the problem is I don't know the number of images with text beforehand as they are returned by server. How can I proceed with this?

Use a ViewPageAdapter to achieve this functionality. The first step is to obtain the images and the corresponding text from the server. I would prefer to customize the server to return it json array.
With the obtained value from server, write an adapter something similar to below. Here myValues will be the array where all the values in the server will be stored.
Create a Fragment (below MyFragment) with a static method that will return a new instance of the fragment with desired values set.
Now as you swipe, the method getItem(pos) will be called and a new instance of fragment will be shown to the user.
private class MyPagerAdapter extends FragmentPagerAdapter {
ArrayList<Values> myValues = new ArrayList<Values>();
public MyPagerAdapter(FragmentManager fm,ArrayList<Values> val) {
super(fm);
this.myValues = val;
}
#Override
public Fragment getItem(int pos) {
return MyFragment.newInstance("text","image");
}
#Override
public int getCount() {
return myValues.length();
}
}
}
Hope this helps. :)

use a RecyclerView with Horizontal Scroll.
recyclerview = (RecyclerView) view.findViewById(R.id.recyclerview);
layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
recyclerview.setLayoutManager(layoutManager);
recyclerview.setItemAnimator(new DefaultItemAnimator());
recyclerview.setHasFixedSize(true);
recyclerview.setAdapter(adapter);

You need to update textView (either in separate fragment or in the same activity) inside onSwipeLeft or onSwipeRight.
To have these callbacks you need to create your custom listener that extends the TouchListener.
Great example of doing this is here.

Related

add recyclerView inside another recyclerView

I have a list of data to be displayed in a recycler view and that is working fine. Now I have another dynamic list of data to be displayed inside the parent recycler-view. So I tried using another recycler-view inside the parent recycler-view, that is not working fine. It will be good if I get some idea of using recycler-view inside another one. Thanks in advance..!
I have illustrated my problem with an example:
For eg: I have a parent recyclerView with five linearLayout and I have created a child recyclerView inside the Linearlayout with visibility GONE. Now when I click the first Linearlayout I am changing the visibility of child recyclerView for the first Linearlayout to VISIBLE and attaching a separate view to it and same concept for all the other Linearlayouts. What happens is when I click first, second, third and fourth linearLayout the child recyclerView is not displaying date which I pass to it, all those first, sec, third and fourth data are accumulated and displayed in the last (i.e) inside fifth linearLayout.
Here is my parent recyclerview code:
class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder>
{
RecyclerView insideCardRecyclerView,recyclerView;
List<String> monthsWeek = new ArrayList<>();
List<String> dealers = new ArrayList<>();
List<String> dealersList = new ArrayList<>();
List<String> date = new ArrayList<>();
HashSet<String> dealersListHash = new HashSet<>();
public CardAdapter(List<String> monthsWeek,List<String> dealers,List<String> date)
{
this.monthsWeek = monthsWeek;
this.dealers = dealers;
this.date = date;
}
public class MyViewHolder extends RecyclerView.ViewHolder
{
ProgressBar progressBar;
RecyclerView recyclerView;
TextView period;
LinearLayout linearLayoutParent,linearLayoutCardDetails;
public MyViewHolder(View view)
{
super(view);
linearLayoutParent = (LinearLayout) view.findViewById(R.id.card_view_linear_parent_layout);
linearLayoutCardDetails = (LinearLayout) view.findViewById(R.id.linear_card_layout_details);
period = (TextView) view.findViewById(R.id.period_summary_graph_card);
insideCardRecyclerView = (RecyclerView) view.findViewById(R.id.summary_graph_card_view_recycler_view);
}
}
#Override
public CardAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(getActivity())
.inflate(R.layout.summary_card_view,parent,false);
recyclerView = (RecyclerView) parent;
return new CardAdapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
holder.period.setText(monthsWeek.get(position));
holder.linearLayoutParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
if(searchClick)
{
for (String date1 : date)
{
if(Objects.equals(date1,monthsWeek.get(position)))
{
Log.e("Summary123 date..///", date1);
dealersList.add(dealers.get(date.indexOf(date1)));
}
}
searchClick = false;
holder.linearLayoutCardDetails.setVisibility(View.VISIBLE);
dealersListHash.addAll(dealersList);
dealersList.clear();
dealersList.addAll(dealersListHash);
//if the condition is true i am attaching another recyclerview inside this.
cardAdapterList = new CardAdapterList(dealersList);
LinearLayoutManager mLayoutManager1 = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL,true);
mLayoutManager1.setReverseLayout(false);
insideCardRecyclerView.setLayoutManager(mLayoutManager1);
insideCardRecyclerView.setItemAnimator(new DefaultItemAnimator());
insideCardRecyclerView.setAdapter(cardAdapterList);
}
else
{
searchClick = true;
holder.linearLayoutCardDetails.setVisibility(View.GONE);
}
}
});
}
#Override
public int getItemCount() {
return monthsWeek.size();
}
}
I've been in trouble sometimes with RecyclerView when multiple lists are needed to be shown in a single page of the application. Its not a very good idea actually to have multiple lists in a single layout but however, the idea of having a ScrollView and the lists inside that ScrollView is even worse.
I had to implement a ListView inside a ScrollView once and yes it was not a very good experience. Firstly, my list was not scrolling at all. Then I had to add some code to disable the scrolling when the touch is detected inside the list. It was not a very good idea of solving the actual problem. I had another problem of having a fixed height of the ListView. In case of list items with dynamic heights, the solution failed.
Having two lists in the layout, one after one is not a good idea either. As the first list need to have a fixed height.
So, after searching for suggestions about how can I implement two lists in a single layout file, I found most of the developers suggests of having a single list with a header and footer if necessary. Later, I could manage to show two lists in a single RecyclerView using my custom Adapter. I thought I should save some of my code for future use and hence, you see this note.
You can refer this sample code.

Setting different adapter in single RecyclerView in Android

I have MenuActivity which has RecyclerView and ViewPager. This ViewPager has 3 pages which use three different Fragment.
FragmentOne,FragmentTwo, FragmentThree, All these Fragments use RecyclerView data.
I want to use three different ArrayList need to be set in RecyclerView Adapter, which is depends on what Fragment user is viewing.
One solution is to put RecyclerView in all three fragment and get update data in Fragment. I just want to know if it is possible to set Adapter data in MenuActivity based on what Fragment is called.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_menu, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
itemList = new ArrayList<>();
prepareMenuData();
vegadapter = new MenuItemAdapter(getContext(),vegItemList);
nonvegadapter = new MenuItemAdapter(getContext(),nonvegItemList);
dessertAdapter = new MenuItemAdapter(getContext(),dessertItemList);
recyclerView.setAdapter(vegadapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
return rootView;
}
prepareMenuData();
for (MenuItem item:itemList)
{
if(item.getCategory().equals("VEG"))
{
vegItemList.add(item);
vegadapter.notifyDataSetChanged();
}
if(item.getCategory().equals("NONVEG"))
{
nonvegItemList.add(item);
nonvegadapter.notifyDataSetChanged();
}
if(item.getCategory().equals("DESSERTVEG"))
{
dessertItemList.add(item);
dessertAdapter.notifyDataSetChanged();
}
}
This code does not know when to set vegadapter,nonvegadapter etc.
Please suggest.
Thanks
Deepak
Technically yes you can, but whether thats good practice is another question.
You'll have to have the RecyclerView in each Fragment regardless but you can get the data for the adapter from the Activity. Fragments have access to their Activity, thats how the Android lifecycle works so from the Fragment you could call ((MenuActivity) getActivity()).getMenuList(); and then pass the result to that Fragments adapter. This will however couple the Fragment with the Activity which isn't good practice.
Given the fact that it looks like your data isn't dynamic what I would personally do is make your MenuItem class implement Parcelable, then when you create your Fragments you can pass through your ArrayList of MenuItems as an argument, this way your Fragment is independent of your Activity.

How to get reference to a View inside CardView from an activity?

I have got CardView with Views inside a RecyclerView. I have created adapter in which assets are attached to Views and everything works. Now, I would like to change these Views from my activity. Is it any simple way to do that?
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private List<Offer>offers;
TextView timer; //timer inside CardView
private void getViewReferences() {
recyclerView = (RecyclerView)findViewById(R.id.mainRecyclerView);
timer = (TextView)findViewById(R.id.timer);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getViewReferences();
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
initializeData();
initializeAdapter();
timer.setText("13:16"); //NullPointerException here
}
private void initializeData(){
offers = new ArrayList<>();
offers.add(new Offer("godzina", R.drawable.zdj, 200));
offers.add(new Offer("godzina", R.drawable.zdj));
}
private void initializeAdapter() {
RecyclerViewAdapter adapter = new RecyclerViewAdapter(offers);
recyclerView.setAdapter(adapter);
}
}
So you don't want to change the Views but you want to update the text values of the views in your list.
You can't directly do that by trying to find the Views in the recyclerView and change the text. The adapter is responsible for giving values to your list. So you need to update the dataset in the adapter and call one of the notifyDataSetChanged methods on the adapter to update the recyclerView.
The big advantage of this design is that it places Views in Cache in order to reuse them multiple times without having to inflate and construct them again. This is a huge performance improvement.
You need to read the docs and some tutorials to understand the adapter design pattern better.

Adding Fragments Dynamically on both side of the ViewPager using FragmentStatePagerAdapter

In one of my apps, I need to add Fragments on both sides of the ViewPager. First of all, I will get a constant of 5 feeds, and my ViewPager will show feed at index 2 i.e. my current displayed Fragment will contain data present at index 2. So overall my ViewPager will show center of 5 feeds at start and that i have achieved by just setting the ViewPager current Item as 2 like this
viewPager.setCurrentItem(2);
Now user can swipe both sides, when he will swipe left from center position, I will look for next feed i.e fetch from server and add feed at zero index of my ViewPager like this
feedsList.add(0, modelClassObject); // feedsList will contain all feeds and has been used by adapter to show data in fragments.
adapter.notifyDataSetChanged();
and when i swipe right from center position, i will add feed at the last simply like this
feedsList.add(modelClassObject);
adapter.notifyDataSetChanged();
Now the problem is if i only add feeds at the right i.e at the end of the feedsList, everything works fine, but problem comes when i add feeds at zero index. My adapter is not showing that new feed that has been added to zero position instead it is repeating one of the existing feed and that too on the right side but not on the left. I Have tried everything, but nothing is going right way. Here is my adapter code.
private class HorizontalPagerAdapter extends FragmentStatePagerAdapter {
public HorizontalPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int arg0) {
return FeedUserProfileFragment.newInstance(feedsList.get(arg0),
arg0);
}
#Override
public int getCount() {
return feedsList.size();
}
}
I have also used this
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
but no results.. :(
So in severe need, If anyone had done that earlier and faced the same issue, please let me know what i am doing wrong. I only need to add fragment at zero index of my ViewPager.
I faced a similar problem before, and my solution was :
at first the list is declared in the adapter itself, so that when creating an instance of that adapter I can have it's own list then.
modified the method getItem(int arg0) in the adapter class so that it returns a specific item from the list depending on that item position.
when creating a new fragment, use instantiate method to create it, and after that add it to your fragments.
So, the complete solution would be :
adapter class:
private class HorizontalPagerAdapter extends FragmentStatePagerAdapter {
public List<Fragment> feedsList;
public HorizontalPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return feedsList.get(position);
}
#Override
public int getCount() {
return feedsList.size();
}
}
and when creating the adapter:
public static YourPageAdapter adapter_obj; // make sure it's static object
adapter_obj = new YourPageAdapter(getSupportFragmentManager());
adapter_obj.feedsList = new ArrayList<Fragment>();
// then add the list of fixed fragments to it(the 5 in the beginning)
adapter_obj.feedsList = fragments_list; // an ArrayList contains the 5 fragments
and when want to create a new fragment:
adapter_obj.feedsList.add(0, Fragment.instantiate(mContext, ViewPager_Container_Class.class.getName(), page));
adapter_obj.notifyDataSetChanged();
FragmentStatePagerAdapter can't handle it right when you add a new fragment in front.
The workaround is this:
Add a new fragment at the end.
Call notifyDataSetChanged();
Bring the fragment to front.
Call notifyDataSetChanged();
BTW, getItemPosition() should return correct positions all along:
public int getItemPosition(Object object)
{
return feedsList.indexOf( object );
}
So, with your code, it should be:
newFrag = Fragment.instantiate(...);
feedsList.add( newFrag );
adapter_obj.notifyDataSetChanged();
feedsList.remove( feedsList.size() - 1 );
feedsList.add( 0, newFrag );
adapter_obj.notifyDataSetChanged();
I guess the implementation of FragmentStatePagerAdapter doesn't expect both adding a new fragment and changing position happen at the same time.

Display new items at the top of a ListView

I'm using a list to populate a ListView (). The user is able to add items to the list. However, I need the items to be displayed at the top of the ListView. How do I insert an item at the beginning of my list in order to display it in reverse order?
By default list adds elements at bottom. That is why all new elements you add will show at bottom. If you want it in reverse order, may be before setting to listadapter/view reverse the list
Something like:
Collections.reverse(yourList);
Another solution without modifying the original list, override getItem() method in the Adapter
#Override
public Item getItem(int position) {
return super.getItem(getCount() - position - 1);
}
Updated: Example
public class ChatAdapter extends ArrayAdapter<ChatItem> {
public ChatAdapter(Context context, List<ChatItem> chats) {
super(context, R.layout.row_chat, chats);
}
#Override
public Item getItem(int position) {
return super.getItem(getCount() - position - 1);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(R.layout.row_chat, parent, false);
}
ChatItem chatItem = getItem(position);
//Other code here
return convertView;
}
}
You should probably use an ArrayAdapter and use the insert(T, int) method.
Ex:
ListView lv = new ListView(context);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.id...);
lv.setAdapter(adapter);
...
adapter.insert("Hello", 0);
The ListView displays the data as it is stored in your data source.
When you are adding in your database, it must be adding the elements in the end. So, when you are getting all the data via the Cursor object and assigning it to the ArrayAdapter, it is in that order only. You should basically be trying to put data in the beginning of the database, rather that in the end, by having some time-stamp maybe.
Using ArrayList, you can do it by Collections.reverse(arrayList) or if you are using SQLite, you can use order by.
You can add element at the beginning of the list: like
arraylist.add(0, object)
then it will always display the new element at the top.
mBlogList is a recycler view...
mBlogList=(RecyclerView) findViewById(R.id.your xml file);
mBlogList.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
mBlogList.setLayoutManager(mLayoutManager);//VERTICAL FORMAT
You could always have a datestamp in your objects and sort your listview based on that..
public class CustomComparator implements Comparator<YourObjectName> {
public int compare(YourObjectName o1, YourObjectName o2) {
return o1.getDate() > o2.getDate() // something like that.. google how to do a compare method on two dates
}
}
now sort your list
Collections.sort(YourList, new CustomComparator());
This should sort your list such that the newest item will go on top
You can always use a LinkedList instead and then use addFirst() method to add elements to your list and it will have the desired behaviour (new items at the top of the ListView).

Categories

Resources