RecyclerView and CardView Click listener implementation - android

I just moved into the android RecyclerView and CardViewand ... and realized recyclerview don't have an option for Onclick listner, but after implement this feature, i just don't see any result.
hear is my code and layouts:
MainActivity:
public class MainActivity extends AppCompatActivity {
//private usefulSites_RecyclerView_Adapter mAdapter;
final Context context = this;
private RecyclerView mRecyclerView;
private StaggeredGridLayoutManager mGridLayoutManager;
private RecyclerViewAdapter mAdapter;
private String[] mList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
//使用CollapsingToolbarLayout必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上则不会显示
CollapsingToolbarLayout mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout);
mCollapsingToolbarLayout.setTitle("test");
//通过CollapsingToolbarLayout修改字体颜色
mCollapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);//设置还没收缩时状态下字体颜色
mCollapsingToolbarLayout.setCollapsedTitleTextColor(Color.GREEN);
//mList = getResources().getStringArray(R.array.numbers);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mGridLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setLayoutManager(mGridLayoutManager);
mAdapter = new RecyclerViewAdapter(getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
RecyclerViewAdapter
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private Context mContext;
private String [] mSiteTitle,mSiteLink;
private int [] mThumbnail;
public RecyclerViewAdapter(Context contexts, String[] list) {
this.mContext = contexts;
this.mSiteTitle = list;
}
public RecyclerViewAdapter(Context contexts) {
this.mContext = contexts;
initList();
}
private void initList(){
UsfulSite_init mInit=new UsfulSite_init();
mSiteTitle= mInit.initSiteTitle();
mSiteLink=mInit.initSiteLink();
mThumbnail=mInit.initThumbnail();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.usful_sites_card_view, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.sTitle.setText(mSiteTitle[position]);
holder.sLink.setText(mSiteTitle[position]);
holder.sImage.setImageResource(mThumbnail[position]);
holder.setClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
if (isLongClick) {
Toast.makeText(mContext, "#" + position + " - " + mSiteTitle[position] + " (Long click)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "#" + position + " - " + mSiteTitle[position], Toast.LENGTH_SHORT).show();
}
}
});
}
#Override
public int getItemCount() {
return mSiteTitle.length;
}
public static class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener{
private TextView sTitle,sLink;
private ImageView sImage;
private ItemClickListener clickListener;
public ViewHolder(View itemView) {
super(itemView);
sTitle = (TextView)itemView.findViewById(R.id.useful_sites_Title);
sLink=(TextView)itemView.findViewById(R.id.useful_sites_Link);
sImage=(ImageView)itemView.findViewById(R.id.useful_sites_thumbnail);
itemView.setTag(itemView);
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;
}
}
}
Activity_main.xml:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
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=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#30469b"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/a"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none" />
</LinearLayout>
CardView Layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_margin="5dp"
card_view:cardCornerRadius="5dp"
card_view:cardElevation="4dp"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/mainHolder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/useful_sites_thumbnail"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:tint="#color/photo_tint"
android:layout_centerInParent="true"
/>
<TextView
android:id="#+id/useful_sites_Title"
android:gravity="center"
android:background="?android:selectableItemBackground"
android:focusable="true"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="100dp"
android:textSize="24sp"
android:layout_centerInParent="true"
android:textColor="#android:color/white"
/>
<TextView
android:id="#+id/useful_sites_Link"
android:gravity="center"
android:background="?android:selectableItemBackground"
android:focusable="true"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="100dp"
android:textSize="24sp"
android:layout_centerInParent="true"
android:textColor="#android:color/white"
android:visibility="gone"
/>
</RelativeLayout>
I tested another ways for clickListner in RV butT, i just stuck in this.
can some one look at my code and guide me, Little about this.

If you want to implement onClick for each cardview, you can do like this:
in your XML:
<android.support.v7.widget.CardView
android:id="#+id/card_view">
...
</android.support.v7.widget.CardView>
Then, in your adapter code:
public static class ViewHolder extends RecyclerView.ViewHolder {
private CardView cardView;
public ViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView.findViewById(R.id.card_view);
}
}
...
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//implement onClick
System.out.println("Clicked");
}
});
}
}

You can pass listener from Adapter to ViewHolder, and listen events there, and you also can pass event from Adapter to your Activity or where you create this Adapter
public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder> {
private List<Album> albums;
private Context context;
public AlbumAdapterClickListener recListener;
public AlbumAdapter(List<Album> albums, Context context, AlbumAdapterClickListener recListener) {
this.albums = albums;
this.context = context;
this.recListener = recListener;
}
public interface AlbumAdapterClickListener {
void recyclerViewClick(String albumID);
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView title;
public TextView count;
public ImageView picture;
public String albumID;
public AlbumClickListener listener;
//listener passed to viewHolder
public interface AlbumClickListener {
void albumOnClick(String albumID);
}
public ViewHolder(View v, AlbumClickListener listener) {
super(v);
title = (TextView) v.findViewById(R.id.album_list_title);
count = (TextView) v.findViewById(R.id.album_list_count);
picture = (ImageView) v.findViewById(R.id.album_list_picture);
this.listener = listener;
v.setOnClickListener(this);
}
#Override
public void onClick(View v) {
listener.albumOnClick(this.albumID);
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.album_list_item, viewGroup, false),
new ViewHolder.AlbumClickListener() {
#Override
public void albumOnClick(String albumID) {
//TODO show gridView with current albumID
Log.e("fdf", albumID);
// albumID going to Fragment
recListener.recyclerViewClick(albumID);
}
});
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Album album = albums.get(i);
viewHolder.albumID = album.getId();
viewHolder.count.setText(album.getPhotoCount());
viewHolder.title.setText(album.getName());
Picasso.with(context).load(albums.get(i).getCoverURL()).placeholder(R.mipmap.fb_place_holder).resize(140, 140)
.centerCrop().into(viewHolder.picture);
}
#Override
public int getItemCount() {
return albums.size();
}
}

The answer from VLeong works great for me! But a little clarification. This :
holder.cardView.setOnClickListener(new View.OnClickListener() {
need to be changed to :
holder.itemView.setOnClickListener(new View.OnClickListener(){

You Can Add your listener into constructor of View Holder.
public ViewHolder(View v) {
super(v);
title = (TextView) v.findViewById(R.id.album_list_title);
count = (TextView) v.findViewById(R.id.album_list_count);
picture = (ImageView) v.findViewById(R.id.album_list_picture);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Handel yout event here
}
});
}

Related

Whatsapp like image choose and add caption

I wants to develop app in which user could choose multiple photos from gallery and can add caption like there is in whatsapp to add captions in multiple images
Anyone can help me in this.
If you looking for this,
You are on right place,
Here is the full solution I do for helping the beginners :
Layout UI Design
<ImageView
android:contentDescription="#string/app_name"
android:id="#+id/currentStreamImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<ImageView
android:id="#+id/selected_photo"
android:contentDescription="#string/app_name"
android:background="#null"
android:layout_margin="12dp"
android:layout_alignParentEnd="true"
android:src="#drawable/add_image_icon"
android:layout_width="40dp"
android:layout_height="40dp" />
<LinearLayout
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:padding="10dp"
android:background="#drawable/fade_in_black"
android:id="#+id/captionArea"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/caption"
android:hint="#string/enter_caption_here"
android:textStyle="italic"
android:textColor="#android:color/white"
android:textColorHint="#android:color/white"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:src="#android:drawable/ic_menu_send" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="100dp"/>
</LinearLayout>
AddImageWithCaptionFragment
public class AddImageWithCaptionFragment extends Fragment implements ImageWithCaptionListener {
private ArrayList<ImgCap> imgCapArrayList = new ArrayList<>();
private PerfectAdapter adapter;
private RecyclerView recyclerView;
private ImageView select,mainStream;
private EditText captionEt;
private int mCurrentPosition;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.add_img_with_cap_layout, container, false);
recyclerView = (RecyclerView)view.findViewById(R.id.recyclerView);
select = (ImageView) view.findViewById(R.id.selected_photo);
mainStream = (ImageView) view.findViewById(R.id.currentStreamImage);
captionEt = (EditText) view.findViewById(R.id.caption);
select.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TedBottomPicker bottomSheetDialogFragment = new TedBottomPicker.Builder(getActivity())
.setOnMultiImageSelectedListener(new TedBottomPicker.OnMultiImageSelectedListener() {
#Override
public void onImagesSelected(ArrayList<Uri> uriList) {
imgCapArrayList.clear();
for (int i=0;i<uriList.size();i++) {
ImgCap imgCap = new ImgCap(i,"", uriList.get(i));
imgCapArrayList.add(imgCap);
}
adapter = new PerfectAdapter(getActivity(),imgCapArrayList,mainStream,AddImageWithCaptionFragment.this);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(adapter);
}
})
.setPeekHeight(1600)
.showTitle(false)
.setCompleteButtonText("Done")
.setEmptySelectionText("No Select")
.create();
bottomSheetDialogFragment.show(getActivity().getSupportFragmentManager());
}
});
captionEt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
imgCapArrayList.get(mCurrentPosition).setCaption(s.toString());
}
#Override
public void afterTextChanged(Editable s) {
}
});
return view;
}
#Override
public void imgCaptionCallBack(int position) {
mCurrentPosition = position;
captionEt.setText(imgCapArrayList.get(mCurrentPosition).getCaption());
}
}
Custom Adapter Class
public class PerfectAdapter extends RecyclerView.Adapter<PerfectAdapter.MyViewHolder>{
private LayoutInflater inflater;
private Context context;
private ArrayList<ImgCap> imgCapsList;
private ImageView mainStream;
private ImageWithCaptionListener mCallBack;
public PerfectAdapter(Context context,ArrayList<ImgCap> imgCapsList,ImageView mainStream,ImageWithCaptionListener mCallBack) {
inflater = LayoutInflater.from(context);
this.context = context;
this.imgCapsList = imgCapsList;
this.mainStream = mainStream;
this.mCallBack = mCallBack;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.image_item_layout, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(MyViewHolder holder,final int position) {
final ImgCap element = imgCapsList.get(holder.getAdapterPosition());
Glide.with(context).load(element.getImagePath()).into(holder.image);
Glide.with(context).load(imgCapsList.get(0).getImagePath()).into(mainStream);
holder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Glide.with(context).load(element.getImagePath()).into(mainStream);
mCallBack.imgCaptionCallBack(position);
}
});
}
#Override
public int getItemCount() {
return imgCapsList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder
{
ImageView image;
public MyViewHolder(View itemView) {
super(itemView);
image = (ImageView) itemView.findViewById(R.id.image);
}
}
}
Listener Class For CallBack
public interface ImageWithCaptionListener {
void imgCaptionCallBack(int position);
}
Item layout
<RelativeLayout
android:background="#android:color/white"
android:padding="1dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:contentDescription="#string/app_name"
android:id="#+id/image"
android:scaleType="centerCrop"
android:layout_width="100dp"
android:layout_height="100dp" />
</RelativeLayout>
POJO Class
public class ImgCap {
private int position;
private String caption;
private Uri imagePath;
public ImgCap(int position, String caption, Uri imagePath) {
this.position = position;
this.caption = caption;
this.imagePath = imagePath;
}
public int getPosition() {
return position;
}
public String getCaption() {
return caption;
}
public Uri getImagePath() {
return imagePath;
}
public void setPosition(int position) {
this.position = position;
}
public void setCaption(String caption) {
this.caption = caption;
}
public void setImagePath(Uri imagePath) {
this.imagePath = imagePath;
}
}
Just Copy and Paste , Enjoy !!!

How save the state of RecyclerView row item?

I have a recyclerview which populates data from SQL database. Now each row in the recyclerview has a seekbar which when moved displays it's progress in a textview inside the same row. The problem is when I scroll the recyclerview up or down then return back to the first changed row, the seekbar is returned to its default position. How can I make it save the new position ? In normal activities/fragments I use lifecycle methods as "onPause" to save/restore the state. Here we have onAttachedToRecyclerView, I think it should solve my problem but I don't know exactly how.
EDIT : here is a full simple app files which I'm working on to test this problem.
MainActivity.class
public class MainActivity extends AppCompatActivity {
private List<Score> scoreList = new ArrayList<>();
private RecyclerView recyclerView;
private MyAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mAdapter = new MyAdapter(scoreList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
prepareScoreData();
}
private void prepareScoreData() {
Score score = new Score("title", 5);
scoreList.add(score);
for(int i= 0; i<1000; i++){
score = new Score("title", 5);
scoreList.add(score);
}
mAdapter.notifyDataSetChanged();
}
}
MyAdapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<Score> scoresList;
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView title, scoreView;
SeekBar seekbar;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
scoreView = (TextView) view.findViewById(R.id.score);
seekbar = (SeekBar) view.findViewById(R.id.seekbar);
}
}
public MyAdapter(List<Score> scoresList) {
this.scoresList = scoresList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
final Score score = scoresList.get(position);
holder.title.setText(score.getTitle());
if (!score.getProgressed()) {
holder.seekbar.setProgress(0) ;
} else {
holder.seekbar.setProgress(score.getSeekbarProgress());
}
holder.seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
holder.scoreView.setText(String.valueOf(i));
score.setSeekbarProgress(i);
score.setProgressed(true);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
#Override
public int getItemCount() {
return scoresList.size();
}
}
Score class
public class Score {
private String title;
int seekbarProgress;
boolean progressed;
public Score() {
}
public Score(String title,int seekbarProgress) {
this.title = title;
this.seekbarProgress = seekbarProgress;
}
public void setProgressed(boolean progressed) {
this.progressed = progressed;
}
public void setTitle(String title) {
this.title = title;
}
public void setSeekbarProgress(int seekbarProgress) {
this.seekbarProgress = seekbarProgress;
}
public String getTitle() {
return title;
}
public int getSeekbarProgress() {
return seekbarProgress;
}
public boolean getProgressed() {
return progressed;
}
}
MainActivity_Layout
<?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="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.moaness.tut_recyclerview.MainActivity">
<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
item.xml
<?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:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="60dp"
android:paddingBottom="60dp"
android:layout_marginBottom="10dp"
android:clickable="true"
android:background="#f2f2f2"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:text="title"
android:textColor="#color/title"
android:textSize="16dp"
android:paddingTop="16dp"
android:textStyle="bold"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/score"
android:text="score"
android:layout_below="#+id/title"
android:textSize="16dp"
android:paddingBottom="16dp"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/score"
android:id="#+id/seekbar"
/>
</RelativeLayout>
If you are using recyclerview you need to maintain states of each row, means if you are checking using a condition(i.e. if) at any stage of recyclerview item(in recyclerview adapter class) then you need to handle else as well. I can send you a code snippet so you can have a good idea for recyclerview adapter.
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> {
List<ViewHolder> holders = new ArrayList<ViewHolder>();
private ArrayList<ContactModel> arrayList = new ArrayList<>();
private Context context;
private LayoutInflater inflater;
public void clearAdapter() {
arrayList.clear();
notifyDataSetChanged();
}
public ContactsAdapter(Context context, ArrayList<ContactModel> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
public void setList(ArrayList<ContactModel> listSearch) {
this.arrayList = listSearch;
notifyItemRangeChanged(0, listSearch.size());
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.custom_row_for_contacts, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
holders.add(viewHolder);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ContactModel current = this.arrayList.get(position);
holder.txtDriverName.setText(current.getName());
holder.txtDriverPhone.setText(current.getPhone());
if (current.getImgUrl().length() > 0) {
String urlLicenceThumb = UrlEndPoints.parentUrl + current.getImgUrl();
Glide.with(context).load(urlLicenceThumb).error(R.mipmap.ic_launcher).into(holder.imgDriver);
} else {
Glide.with(context).load(R.mipmap.ic_launcher).into(holder.imgDriver);
}
}
public void delete(int position) {
arrayList.remove(position);
notifyItemRemoved(position);
}
#Override
public int getItemCount() {
return (null != arrayList ? arrayList.size() : 0);
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView txtDriverName, txtDriverPhone;
private CircleImageView imgDriver;
private Button btnInvite;
private CheckBox chkAdd;
public ViewHolder(View itemView) {
super(itemView);
chkAdd = (CheckBox) itemView.findViewById(R.id.chkAdd);
imgDriver = (CircleImageView) itemView.findViewById(R.id.imgDriver);
txtDriverName = (TextView)itemView.findViewById(R.id.txtDriverName);
txtDriverPhone = (TextView) itemView.findViewById(R.id.txtDriverPhone);
btnInvite = (Button) itemView.findViewById(R.id.btnInvite);
}
}
}

child recyclerview methods is not calling (onCreateViewHolder, onBindViewHolder). But getItemCount() method is calling

I have one recyclerview and this will display data in vertically. One row will display data in horizontally so i used another recyclerview. which is calling from first recyclerview's onBindViewHolder.
parent recylerview is working but when i am adding child recycler view. these methods are not calling (onCreateViewHolder, onBindViewHolder). Probably something issue in xml files.
parent xml:-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:context="com.catrackapp.catrack.MainActivity" >
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/textView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="VIDEO LATEST"
android:id="#+id/textView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:gravity="center" />
</RelativeLayout>
child xml which i am using in onCreateViewHolder as displaying row
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</android.support.design.widget.CoordinatorLayout>
and this xml is using by child recyclerview.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="5dp"
>
<com.google.android.youtube.player.YouTubeThumbnailView
android:id="#+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/no_thumbnail"
android:adjustViewBounds="true" />
</LinearLayout>
These are code parent and child.
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {
private List<Videos> moviesList;
// private ThumbnailListener thumbnailListener;
public class MyViewHolder extends RecyclerView.ViewHolder {
// public VideoView videoView;
//public ImageView imageView;
// YouTubeThumbnailView thumbnail;
RecyclerView recyclerView1;
public MyViewHolder(View view) {
super(view);
// videoView = (VideoView) view.findViewById(R.id.videoView);
// imageView = (ImageView) view.findViewById(R.id.imageView);
// thumbnail = (YouTubeThumbnailView) view.findViewById(R.id.thumbnail);
recyclerView1 = (RecyclerView) view.findViewById(R.id.recycler_view1);
// LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
// recyclerView1.setLayoutManager(layoutManager);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView1.setLayoutManager(mLayoutManager);
}
}
public MoviesAdapter(List<Videos> moviesList) {
this.moviesList = moviesList;
//thumbnailListener = new ThumbnailListener();
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Videos movie = moviesList.get(position);
System.out.println("first = " + movie.getImage());
String url = movie.getVideo_url();
final String m = url.substring(url.lastIndexOf('/') + 1);
final String txt = movie.getPlain_text2();
System.out.println("url = "+m);
List<String> l = new ArrayList<String>();
l.add(m);
l.add(m);
l.add(m);
l.add(m);
l.add(m);
final MoviesAdapterHorizontal adapter = new MoviesAdapterHorizontal(l);
holder.recyclerView1.setAdapter(adapter);
// holder.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
adapter.notifyDataSetChanged();
}
#Override
public int getItemCount() {
return moviesList.size();
}
private final class ThumbnailListener implements
YouTubeThumbnailView.OnInitializedListener,
YouTubeThumbnailLoader.OnThumbnailLoadedListener {
#Override
public void onInitializationSuccess(
YouTubeThumbnailView view, YouTubeThumbnailLoader loader) {
loader.setOnThumbnailLoadedListener(this);
// thumbnailViewToLoaderMap.put(view, loader);
// view.setImageResource(R.drawable.loading_thumbnail);
String videoId = (String) view.getTag();
loader.setVideo(videoId);
}
#Override
public void onInitializationFailure(
YouTubeThumbnailView view, YouTubeInitializationResult loader) {
view.setImageResource(R.drawable.no_thumbnail);
}
#Override
public void onThumbnailLoaded(YouTubeThumbnailView view, String videoId) {
}
#Override
public void onThumbnailError(YouTubeThumbnailView view, YouTubeThumbnailLoader.ErrorReason errorReason) {
view.setImageResource(R.drawable.no_thumbnail);
}
}
}
//Horizontal images view.
public class MoviesAdapterHorizontal extends RecyclerView.Adapter<MoviesAdapterHorizontal.MyViewHolder> {
private List<String> moviesList;
private ThumbnailListener thumbnailListener;
public class MyViewHolder extends RecyclerView.ViewHolder {
YouTubeThumbnailView thumbnail;
public MyViewHolder(View view) {
super(view);
thumbnail = (YouTubeThumbnailView) view.findViewById(R.id.thumbnail);
}
}
public MoviesAdapterHorizontal(List<String> moviesList) {
this.moviesList = moviesList;
thumbnailListener = new ThumbnailListener();
System.out.println("called size = "+this.moviesList.size());
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.hotrow, parent, false);
System.out.println("onCreateViewHolder called");
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final String url = moviesList.get(position);
System.out.println("rahul url" + url);
final String m = url.substring(url.lastIndexOf('/') + 1);
System.out.println("url = "+m);
holder.thumbnail.setTag(m);
holder.thumbnail.initialize("AIzaSyDW-sxPUqy2rD6ZWs3vTNb0jKEKA21RjrY", thumbnailListener);
holder.thumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), FullscreenDemoActivity.class);
intent.putExtra("url", m);
intent.putExtra("txt", "");
startActivity(intent);
}
});
}
#Override
public int getItemCount() {
System.out.println("moviesList 2 size = "+this.moviesList.size());
return this.moviesList.size();
}
private final class ThumbnailListener implements
YouTubeThumbnailView.OnInitializedListener,
YouTubeThumbnailLoader.OnThumbnailLoadedListener {
#Override
public void onInitializationSuccess(
YouTubeThumbnailView view, YouTubeThumbnailLoader loader) {
loader.setOnThumbnailLoadedListener(this);
String videoId = (String) view.getTag();
loader.setVideo(videoId);
}
#Override
public void onInitializationFailure(
YouTubeThumbnailView view, YouTubeInitializationResult loader) {
view.setImageResource(R.drawable.no_thumbnail);
}
#Override
public void onThumbnailLoaded(YouTubeThumbnailView view, String videoId) {
}
#Override
public void onThumbnailError(YouTubeThumbnailView view, YouTubeThumbnailLoader.ErrorReason errorReason) {
view.setImageResource(R.drawable.no_thumbnail);
}
}
}
kindly help me. i am trying to fix it last 4 days. but now able to fix.
UPDATE:-
i used it and now child all methods are calling but layout is not displaying.
MoviesAdapterHorizontal adapter = new MoviesAdapterHorizontal(l);
holder.recyclerView1.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView1.setAdapter(adapter);
adapter.notifyDataSetChanged();
i saw below error in log
04-21 11:40:50.571 27006-27006/com.androidbelieve.drawerwithswipetabs W/ResourcesManager: getTopLevelResources: com.androidbelieve.drawerwithswipetabs for user 0
In CoordinatorLayout layout ,you cannot set RecyclerView height to android:layout_height="wrap_content"
try to set
android:layout_height="200dp"
OR
android:layout_height="match_parent"

Edittext in RecylerView start lossing data

hii i am working in android application in which there is recycler view with each row is edittext, as i click on floating button new edittext is added in which user can enter email,my problem is that after say 9th position the previously enterd email values replaced with some position , i am calling notifydatasetchanged() method below is the code...if anyone know the way plese share..
1)Challenge Invite_other.java
/**
* Add custom workout detail
*/
public class ChallengeInviteOtherActivity extends GlobalAppCompactActivity implements ResponseListener {
private Gson gson;
private List<ChallengeParticipant> challengeParticipants;
private RecyclerView mRecyclerView;
private ChallengeInviteOtherAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.challenge_invite_other);
Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
initlization();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.floating_add_custom_challenge);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
challengeParticipants.add(new ChallengeParticipant());
adapter.addAll(challengeParticipants);
}
});
setTitle(getString(R.string.nav_invite_participant));
//progressDialog = new ProgressDialog(this, R.style.CustomProgressDialog);
}
private void initlization() {
gson = CommonUtil.getGson();
challengeParticipants = new ArrayList<ChallengeParticipant>();
mRecyclerView = (RecyclerView) findViewById(R.id.challenge_list_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
challengeParticipants.add(new ChallengeParticipant());
adapter = new ChallengeInviteOtherAdapter(this);
adapter.addAll(challengeParticipants);
mRecyclerView.setAdapter(adapter);
}
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}else if (item.getItemId() == R.id.action_bar_trace_friend) {
List<ChallengeParticipant> challengeParticipantList = new ArrayList<ChallengeParticipant>();
for (ChallengeParticipant participant : challengeParticipants) {
if (CommonUtil.isNotNull(participant.getEmailAddress()) || CommonUtil.isNotNull(participant.getContactNo())) {
participant.setChallengeId(CommonUtil.CHALLENGE.getId());
challengeParticipantList.add(participant);
}
}
if(challengeParticipantList.size()==0){
AlertMsg.showToast(this, getString(R.string.at_least_fill_one_contact));
}else{
Type listType = new TypeToken<ArrayList<ChallengeParticipant>>() {
}.getType();
String json = CommonUtil.getGson().toJson(challengeParticipantList, listType);
VolleyRequest volleyRequest = VolleyRequest.getInstance();
volleyRequest.sendRequest(VolleyRequest.INVITE_PARTICIPANTS, json, CommonUtil.getObject(this, this));
}
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.clear();
getMenuInflater().inflate(R.menu.challenge_invite_participant_memu, menu);
getMenuInflater().inflate(R.menu.common, menu);
return true;
}
#Override
public void onResponse(Object... result) {
AlertMsg.showToast(this, getString(R.string.invite_successfully));
finish();
}
//private ProgressDialog progressDialog;
}
2)Adapter class
private List<ChallengeParticipant> itemDetailsrrayList;
private LayoutInflater layoutInflater;
private Context mContext;
public ChallengeInviteOtherAdapter(Context context) {
mContext = context;
layoutInflater = LayoutInflater.from(context);
}
class MyViewHolder extends RecyclerView.ViewHolder {
private EditText email;
public MyViewHolder(View view) {
super(view);
email = (EditText) view.findViewById(R.id.email);
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.challenge_invite_other_item_detail, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
final ChallengeParticipant c = itemDetailsrrayList.get(position);
holder.email.setText(c.getEmailAddress());
//holder.phone.setText(c.getContactNo());
//RecyclerView recyclerView = (RecyclerView) ((Activity) mContext).findViewById(R.id.price_listview);
//recyclerView.getLayoutParams().height = 150*itemDetailsrrayList.size();
holder.email.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(CommonUtil.isEmail(holder.email.getText().toString())){
c.setEmailAddress(holder.email.getText().toString());
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
/*holder.email.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
if (!b) {
}
}
});*/
/* holder.phone.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
if (!b) {
if(CommonUtil.isPhone(holder.phone.getText().toString())){
c.setContactNo(holder.phone.getText().toString());
}
}
}
});*/
}
public void addAll(List<ChallengeParticipant> list) {
itemDetailsrrayList = list;
notifyItemInserted(itemDetailsrrayList.size());
}
#Override
public int getItemCount() {
return itemDetailsrrayList.size();
}
}
3)xml layout of activity
<?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="match_parent"
android:background="#color/dim_background"
android:focusableInTouchMode="true"
android:orientation="vertical">
<include layout="#layout/toolbar" />
<!--<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="#string/or"/>-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:elevation="#dimen/general_elevation"
android:layout_margin="10dp"
android:background="#color/cardview_light_background">
<include layout="#layout/textview_no_record_found" />
<android.support.v7.widget.RecyclerView
android:id="#+id/challenge_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/floating_add_custom_challenge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:elevation="12dp"
android:src="#drawable/ic_add_white_24dp"
app:backgroundTint="#color/colorAccent"
app:borderWidth="0dp"
app:fabSize="normal"/>
</FrameLayout>
<include
layout="#layout/ad_system"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</LinearLayout>
When You add the new data to Array on which you set the adapter than Notify the adapter Add below lines after calling adapter.addAll(challengeParticipants);
adapter.notifyDataSetChanged();
adapter.notifyItemInserted(challengeParticipants.size());
Try this
public void addAll(List<ChallengeParticipant> list) {
itemDetailsrrayList = list;
notifyItemRangeChanged(0,challengeParticipants.size()-1);
}

RecyclerView not generating list when it has a view above it

So I cannot make the RecyclerView generate the given list once I have another view above it. The view above it and the recycler view are both within a LinearLayout and which is the single child of a NestedScrollView (used for collapsing the toolbar). Once RecyclerView is the only view in the fragment everything works as expected.
StocksCompleteListFragment.java
public class StocksCompleteListFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
NestedScrollView sv = (NestedScrollView) inflater.inflate(
R.layout.fragment_stocks_completelist, container, false);
LinearLayout mylinear = (LinearLayout) sv.findViewById(R.id.mainLinear);
RecyclerView rv = (RecyclerView) mylinear.findViewById(R.id.recyclerview);
if (rv == null){
Toast.makeText(getActivity(),"Null", Toast.LENGTH_SHORT).show();
}
setupRecyclerView(rv);
return sv;
}
private void setupRecyclerView(RecyclerView recyclerView) {
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(getActivity(),
getRandomSublist(Cheeses.sCheeseStrings, 30)));
}
private List<String> getRandomSublist(String[] array, int amount) {
ArrayList<String> list = new ArrayList<>(amount);
Random random = new Random();
while (list.size() < amount) {
list.add(array[random.nextInt(array.length)]);
}
return list;
}
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {
private final TypedValue mTypedValue = new TypedValue();
private int mBackground;
private List<String> mValues;
public static class ViewHolder extends RecyclerView.ViewHolder {
public String mBoundString;
public final View mView;
public final ImageView mImageView;
public final TextView mTextView;
public ViewHolder(View view) {
super(view);
mView = view;
mImageView = (ImageView) view.findViewById(R.id.avatar);
mTextView = (TextView) view.findViewById(android.R.id.text1);
}
#Override
public String toString() {
return super.toString() + " '" + mTextView.getText();
}
}
public String getValueAt(int position) {
return mValues.get(position);
}
public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {
context.getTheme().resolveAttribute(R.attr.selectableItemBackground, mTypedValue, true);
mBackground = mTypedValue.resourceId;
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mBoundString = mValues.get(position);
holder.mTextView.setText(mValues.get(position));
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, CheeseDetailActivity.class);
intent.putExtra(CheeseDetailActivity.EXTRA_NAME, holder.mBoundString);
context.startActivity(intent);
}
});
Glide.with(holder.mImageView.getContext())
.load(Cheeses.getRandomCheeseDrawable())
.fitCenter()
.into(holder.mImageView);
}
#Override
public int getItemCount() {
return mValues.size();
}
}
}
And here's the layout xml fragment_stocks_completelist.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
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="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="#+id/mainLinear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/progressBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="4dp"
android:layout_marginTop="8dp"
android:background="#color/grayBackground"
android:gravity="center_vertical|center_horizontal"
android:padding="4dp"
android:visibility="gone">
<ProgressBar
android:id="#+id/progressBar1"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
You need to set
android:fillViewport="true"
on your NestedRecyclerView. If you put it into the CoordinatorLayout then also specify the behavior (which you've already done)

Categories

Resources