How to store changed values for RecyclerView adapter - android

Hi I've started learning android recently and am Trying to get familiar with RecyclerView.
I have a Main menu which is a simple list. When clicked on any item a new activity named subMenu starts. This SubMenu contains a RecyclerView where each item is a cardview having two buttons, that can be used to increase or decrease items quantity (which is also being shown card view). Now I want to retain changes made in Recyclerview for each main menu. Currently I have adapter for only one sub menu.
Any help or sample is appreciated.
Main menu xml
<?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"
tools:context=".MainMenu">
<ListView
android:id="#+id/categories_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar">
</ListView>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Main menu .java file
public class MainMenu extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
TextView textView=findViewById(R.id.editText);
Toolbar toolbar=findViewById(R.id.toolbar);
toolbar.setTitle("Table "+getIntent().getStringExtra("table_no"));
setSupportActionBar(toolbar);
final ListView listView=findViewById(R.id.categories_list);
ArrayAdapter<String> adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,getResources().getStringArray(R.array.main_categories));
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent=new Intent(MainMenu.this,subMenu.class);
intent.putExtra("main_category",listView.getItemAtPosition(position).toString());
startActivity(intent);
}
});
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.custom_menu,menu);
return true;
}
}
sub menu .xml file
<?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"
tools:context=".subMenu">
<Toolbar
android:id="#+id/toolbar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary">
</Toolbar>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_submenu"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar2">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
Edit
To make my question more clear
Here is main menu
When clicked sub menu opens
The number on top right is quantity that can be incremented or decremented by user as shown below
Now when user moves to main menu and comes back to sub menu of same category these changes in quantity are not retained.
I hope I made myself clear. Simple example would be helpful.
Edit
Adapter code for sub menu RecyclerView.
public class RecyclerViewAdapterSubItems extends RecyclerView.Adapter<RecyclerViewAdapterSubItems.subItemViewHolder> {
private Context context;
private List<SubItems> subItemsList;
public RecyclerViewAdapterSubItems(Context context, List<SubItems> subItemsList){
this.context=context;
this.subItemsList=subItemsList;
}
#Override
public subItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
LayoutInflater inflater=LayoutInflater.from(context);
view=inflater.inflate(R.layout.cardview_sub_menu_item,parent,false);
return new subItemViewHolder(view);
}
#Override
public void onBindViewHolder(final subItemViewHolder holder, final int position) {
holder.subItemName.setText(subItemsList.get(position).getName());
holder.subItemPrice.setText(Integer.toString(subItemsList.get(position).getPrice()));
holder.subItemQuantity.setText(Integer.toString(subItemsList.get(position).getQuantity()));
holder.incrementQuantityButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
subItemsList.get(position).setQuantity(subItemsList.get(position).getQuantity()+1);
holder.subItemQuantity.setText(Integer.toString(subItemsList.get(position).getQuantity()));
}
});
}
#Override
public int getItemCount() {
return subItemsList.size();
}
public static class subItemViewHolder extends RecyclerView.ViewHolder {
TextView subItemName;
TextView subItemPrice;
TextView subItemQuantity;
ImageButton incrementQuantityButton;
ImageButton decrementQuantityButton;
public subItemViewHolder(View itemView) {
super(itemView);
subItemName=itemView.findViewById(R.id.textView_sub_item_name);
subItemPrice=itemView.findViewById(R.id.textView_sub__item_price);
subItemQuantity=itemView.findViewById(R.id.textView_sub_item_quantity);
incrementQuantityButton=itemView.findViewById(R.id.imageButton_increment_quantity);
decrementQuantityButton=itemView.findViewById(R.id.imageButton_decrement_quantity);
}
}
}
submenu.java
public class subMenu extends AppCompatActivity {
static List<SubItems> subItemList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub_menu);
RecyclerView recyclerView=findViewById(R.id.recyclerview_submenu);
subItemList=new ArrayList<>();
subItemList.add(new SubItems("Pasta",20,0));
subItemList.add(new SubItems("Peparoney",20,0));
subItemList.add(new SubItems("asta",40,0));
RecyclerViewAdapterSubItems adapterSubItems=new RecyclerViewAdapterSubItems(this,subItemList);
recyclerView.setAdapter(adapterSubItems);
recyclerView.setLayoutManager(new GridLayoutManager(this,1));
}
}

Your issue is here:
RecyclerView recyclerView=findViewById(R.id.recyclerview_submenu);
subItemList=new ArrayList<>();
subItemList.add(new SubItems("Pasta",20,0));
subItemList.add(new SubItems("Peparoney",20,0));
subItemList.add(new SubItems("asta",40,0));
You are creating new SubItems everytime you start your SubMenuActivity. You need to save these subItems permanently for every category.
One of the way is to create a Category POJO having List<SubItems> and initialize them at the MainMenu Activity and pass that Category POJO on itemClick.

Take an array of Boolean type with size equal to the size of your main menu items.
Initialise all values to false.
And whenever you select any main menu, if it opens, set that position in your array as true and if it closes, set the place as false.
On bindViewHolder add a condition like if the array value is true, show the sub menu else hide the sub menu
And you can also store this array in database to retain the value if you need it even after you kill your application.
Let me know if you need help with the code.

Related

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

Android TV Leanback

I am new to Android TV development. I don't know which type of fragment to use. I want two achieve layout similar to above screenshot as jiocinema. Somehow I have achieved it using a two xml fragments inside activity layout. Second fragments loads screenshot after hitting an API so it loads after some time. As can be seen in the above screenshot I want the layout in two parts..top one with details and some buttons and the bottom one is a list of screenshots of that movie.
In my case the problem is, bottom list part takes the focus on loading this particular screen after that on pressing up button or any button it never loses focus and never goes on the top part.
Note: below fragment loads asynchronously, as it hits an api for screenshot urls
May be I haven't used proper fragments for this particular layout. Can someone point me to the code or help me out in deciding what to use for this kind of layout. As it can be achieved but navigation is the main thing.
code
activity layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/photo_label_box">
<fragment
android:id="#+id/detail_layout"
android:layout_width="match_parent"
android:name="com.DetailsActivityGame$Detalfragment"
android:layout_height="200dp"></fragment>
<fragment
android:id="#+id/row_layout"
android:layout_width="match_parent"
android:layout_below="#+id/detail_layout"
android:name="com.DetailsActivityGame$SampleFragmentC"
android:layout_height="wrap_content"></fragment>
</RelativeLayout>
Thanks
Try to use the RowSupportFragment in V4 Support Fragment for desired output.
Divide layout into two parts layout with buttons, description and below scrolling layout(Represent by RowSupportFragment)
//----------------------detail_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/leader_background">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/main_layout"
//your own layout design for buttons and description
</RelativeLayout>
<fragment
android:name="FragmentScreenshots"
android:id="#+id/screenshot_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
//----------------Detailfragment--------------------
public static class Detailfragment extends Fragment {
public Detailfragment(){ }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.detail_layout, container, false);
//————————your own implementation—————————————————————————
return view;
}
public static class FragmentScreenshots extends RowsSupportFragment {
private ArrayObjectAdapter mRowsAdapter = null;
public FragmentScreenshots() {
mRowsAdapter = new ArrayObjectAdapter(new ShadowRowPresenterSelector());
setAdapter(mRowsAdapter);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//———————Provide data accordinally———————————
List<ScreenshotItem> list;
// Add a Related items row
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
new ScreenshotCardPresenter(getActivity()));
for (ScreenshotItem s:list)
{
listRowAdapter.add(s);
}
HeaderItem header = new HeaderItem("Screenshots");
mRowsAdapter.add(new ListRow(header, listRowAdapter));
setAdapter(mRowsAdapter);
setOnItemViewClickedListener(new OnItemViewClickedListener() {
#Override
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof ScreenshotItem) {
}
else{
}
}
});
setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
#Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
}
});
}
#Override
public void setExpand(boolean expand) {
super.setExpand(true);
}
#Override
public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
super.setOnItemViewClickedListener(listener);
}
#Override
public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
super.setOnItemViewSelectedListener(listener);
}
}}
You have to use BrowseFragment for your purpose. It is composed of a RowsFragment and a HeadersFragment.
A BrowseFragment renders the elements of its ObjectAdapter as a set of rows in a vertical list. The elements in this adapter must be subclasses of Row.
This tutorial can help you to get started.

RecyclerView not displaying elements from list

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();
}
}

CoordinatorLayout/AppBarLayout interfere with RecyclerView scrollToPosition(bottom scrolling)

This question has been asked many times but it just doesn't seem to have a solution that fits my requirement.
I have a Recipe that has instructions. Each instruction is ordered in a RecyclerView. The order must be from top to bottom. It cannot be reversed like many answers to this question.
I have an Add button that when clicked a new item is added at the bottom of the list. I need to scroll to the new item after the insert. So I employed the following code in the button's onClick event:
mRecyclerAdapter.addItem(newInstruction);
mRecyclerView.smoothScrollToPosition(mRecyclerAdapter.getItemCount() - 1);
This scrolls to the second last item because in the adapter's addItem() method I have this:
mItems.add(item);
notifyItemInserted(getItemCount() - 1);
I don't know the details of the notifyItemInserted() call, but it appears to be on a separate thread, which means that smoothScrollToPosition() is called before the views have been laid out after the insertion. Hence, it only scrolls to the second last item.
I have tried looking for a "callback" function along the lines of onItemInserted but I did not have any luck. eg, no answer Here.
How do I solve this problem?
EDIT START
I've simplified the code in a new project in hope to find the problem, and found out what the problem is. It appears to be a problem caused by CoordinatorLayout/AppBarLayout when scrolling flag is enabled. It seems as though the scrolling did not take into consideration the extra scrolling space added by the AppBarLayout.
In the sample codes below, if you disable the scrolling flag for the app bar, scrolling to the bottom of the screen works perfectly as intended. However, if you enable the AppBarLayout scrolling behavior, and change its height, you'll notice that scrolling to the bottom is off by the given height.
I need the scrolling behavior, so how do I make them play nicely with each other? Please note that I've changed the question to reflect the problem.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int ITEMS_COUNT = 50;
private FloatingActionButton mFloatingActionButton;
private RecyclerView mRecyclerView;
private RecyclerAdapter mRecyclerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fabButton);
mFloatingActionButton.setOnClickListener(this);
setupRecyclerView();
}
private void setupRecyclerView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerAdapter = new RecyclerAdapter(createItemList());
mRecyclerView.setAdapter(mRecyclerAdapter);
}
private List<String> createItemList() {
List<String> itemList = new ArrayList<>();
for (int i = 0; i < ITEMS_COUNT; i++) {
itemList.add("Item " + i);
}
return itemList;
}
#Override
public void onClick(View v) {
mRecyclerView.scrollToPosition(49);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- changing the height affects the scrolling distance -->
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Header"
app:layout_scrollFlags="scroll|enterAlways"/> <!-- this is the problem -->
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="20dp"/>
</android.support.design.widget.CoordinatorLayout>
RecyclerAdapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> mItemList;
public RecyclerAdapter(List<String> itemList) {
mItemList = itemList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return RecyclerItemViewHolder.newInstance(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
String itemText = mItemList.get(position);
holder.setItemText(itemText);
}
#Override
public int getItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
}
recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="#+id/itemTextView"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
style="#style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
EDIT END
Doing a bit more research I found this question/answer which has a workaround to this problem. Although not ideal, it will have to do for now. Though the real solution shouldn't have to collapse the AppBarLayout, the scrolling should take into consideration the extra scrolling space needed. Perhaps this is just a bug that is overlooked.
In your addItem() method change this
notifyItemInserted(getItemCount() - 1);
to
notifyItemInserted(mItems.size() - 1);
This will solve the problem!
add this after adding item
notifyItemRangeChanged(0, items.size());
Have you tried
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
You can also put it in a runnable like this to make sure item has added before you scroll to it.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
}
}, 1000);
Before you add list to adapter in Recycleview do some sorting by using Collection method according to your need,use this code may help you
Comparator<Long> comparator = Collections.reverseOrder();
Collections.sort(arrayList, comparator);

Display UI like facebook messenger in Android java programatically

Image
--
I want to achieve something like this in above image
I have to loop through this json array to get all my datas;
the data contained in my json array are for example
{
'img' : http:\\.....
'name' : XYZ
'msg' : xyz
'time' : abc
}
//this is where I am tring to append everything
final LinearLayout rl = (LinearLayout)main.findViewById(R.id.mainL);
for (int i = 0; i < json.length(); i++) {
try {
JSONObject c = json.getJSONObject(i);
//here components must be created and added to view
}catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I just want to know the how do you programmatically style the views(textview,imageview etc) like in the above image.
Any Help will be appreciated
Ok, the image you are looking at is actually a ListView serving custom-made views.
How does that work?
You will need to subclass the BaseAdapter class. This subclass will contain the underlying data which in your case you are getting as a JSON-formatted reply from the web-server.
When the getView() of the BaseAdapter subclass is called, you can inflate a layout that contains an ImageView and TextViews to display the data onscreen.
The answer is just for future visitors, since the question was asked long day ago. At first, I will just redirect you to my GitHub page where I used RecyclerView to show data and Retrofit to call data. I used Volley also. Finally, let me show simple way to load data to recyclerview.
You need an extra class call it CustomAdapter.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
Context context;
private String countryList[];
private int imgList[];
//constructor
public CustomAdapter(Context context, String countryList[],int imgList[]) {
this.context = context;
this.countryList = countryList;
this.imgList=imgList;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// inflate the item Layout
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.items, parent, false);
// set the view's size, margins, paddings and layout parameters
MyViewHolder vh = new MyViewHolder(view); // pass the view to View Holder
return vh;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myHolder, final int position) {
myHolder.txtName.setText(countryList[position]);
myHolder.imgCountry.setImageResource(imgList[position]);
// implement setOnClickListener event on item view.
myHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// display a toast with person name on item click
Toast.makeText(context, countryList[position], Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return countryList.length;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView txtName;
ImageView imgCountry;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
// get the reference of item view's
txtName = itemView.findViewById(R.id.txt_name);
imgCountry = itemView.findViewById(R.id.img_flag);
}
}
}
And in the MainActivity, you have declare a LayoutManager for RecyclerView and have to call CustomAdapter to load data in RecyclerView.
String countryList[]={"Bangladesh","India","China","Australia","America","New Zealand","Portugal"};
int imgList[]={R.drawable.bd,R.drawable.india,R.drawable.china,R.drawable.australia,R.drawable.america,R.drawable.new_zealand,R.drawable.portugle};
RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// get the reference of RecyclerView
recyclerView = findViewById(R.id.recycler_view);
// set a LinearLayoutManager with default vertical orientation
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
// set a LinearLayoutManager with default Horizontal orientation
// LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext(),LinearLayoutManager.HORIZONTAL,false);
recyclerView.setLayoutManager(linearLayoutManager);
// call the constructor of CustomAdapter to send the reference and data to Adapter
CustomAdapter customAdapter = new CustomAdapter(MainActivity.this,countryList, imgList);
recyclerView.setAdapter(customAdapter); // set the Adapter to RecyclerView
}
activity_main.xml :
<?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:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
items.xml
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/img_flag"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="5dp"
app:srcCompat="#mipmap/ic_launcher" />
<TextView
android:id="#+id/txt_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center|left"
android:paddingLeft="10dp"
android:text="TextView"
android:textSize="24sp" />
</LinearLayout>
</LinearLayout>
Here's the github repository. If you are calling data from server then visit my Volley repo.

Categories

Resources